So now that we are able to create our transactions, let's go ahead and let's modify this page so that it actually loads transactions, so that we can see more information and finally so that we enable editing the transactions. So let's go inside of the app folder, inside of dashboard, transactions. And let's go inside of page.tsx. So inside of here we have a lot of instances of accounts because we copied this from our accounts page. So let's go ahead and do the most important one, use get accounts.
So we are no longer going to need this import. So let's remove it and instead let's add use get transactions, not transaction, but use get transactions, So multiple like this. So make sure you have an S at the end. And then we're going to use that here. So use get transactions.
And this is no longer going to be accounts query, but instead it's going to be transactions query. So I'm gonna go ahead and resolve these three errors, which I have right here. And I think I also have one down here so it's loading accounts and let's change that so this is accounts let's change this to be transactions like this Let's go ahead and add transactions right here but we are still gonna have an error here and that is because of the columns but let's before we go to the columns let's also remove this so we no longer need use bulk delete accounts instead we need use bulk delete transactions So let's add that here and let's rename this to deleteTransactions. Let's fix the errors here and let's add the mutation here. There we go.
So we replaced all hooks. I believe now if I search for account, there are no instances of the word account inside of this file. Great. What we have to do now is we have to resolve our columns. So let's go inside of columns right here.
First of all, let's fix the response type. So we are no longer looking into the accounts here. Instead, we are looking into transactions. And I believe that get 200 data and this is exactly the same. There we go.
So this is our response type, ID, date, category, category ID, payee, amount, notes, account ID and account. Great. And now let's go ahead and let's modify this. So we have these accessory keys for name. So we're gonna change this.
Instead of name the first one is going to be date right here and the header will say date like this and let's go ahead and let's modify the cell, right? So I'm gonna add cell here. Let me show you why I'm doing that, right? So right now if I just save columns, as you can see there are no longer errors here because the columns now match. But let me just show you that now, if you have an existing transactions, this is how the date will look like.
Right, so go ahead and create a transactions. Let me pick for example, 9th of May. Let me pick something here. Test, test, let's create a transaction. There we go, so you can see how this looks, right?
So let's go ahead and resolve this. So we're gonna go ahead and go inside of columns right here. And we're gonna add, after our header, here add a cell property. Go ahead and destructure row and return a method which will very simply return a span element here. And now what we are going to do is we're going to write const date row getValue Whoops, let me scroll up so you can see.
GetValue date as date like this and Inside of here we're going to use our format method from DateFNS. So just make sure you've added this. And we are going to format it by passing in the date. And then we're going to use a specific format. You can use anything you want, of course.
I'm gonna go ahead and use this format because I like it the most. So 9th of May, 2024. There we go. Let's just confirm that this date picker is working. So if I choose 12th of May and choose some random stuff here.
Let's just see if it's created, but why is it not here? All right, we might have a little bug here, but we're gonna come back to this later. Okay we're gonna debug that but let's first just finish our columns so we don't get sidetracked. Alright so we just finished the date and now what I want to do is I want to copy and paste this entire thing so with the accessor key, header and cell, let's copy and paste it and this one will go to category. So this will be category like that and now instead of here well for now what we can do is we can just say row.original.category Like this there we go.
So this will just save food. So for now, let's keep it like this. Let's keep it simple Now let's go ahead and copy and paste this again. Right here. So just above actions.
We're gonna call this payee and this will be payee And inside of here we don't need cell at all because it's just going to be plain text, right? We don't care. So for the one above we're going to change this later. That's where I rendered cell in a custom way. So let's go ahead now and let's copy our existing category.
So copy category because it has the cell option which is what we'll need for the next one. So skip the payee and now let's add the amount. So this will say amount. Let's go ahead and let's get our amount using parse float row get value amount like this and then inside of here let's go ahead and let's write format currency which is something we don't yet have so we're gonna have to go inside of our utils and we're gonna create the format currency method so let's do that I'm gonna go inside of lib folder utils and let's export function format currency so this is gonna be for the frontend only It's going to accept a value which is a number and inside of here what we are going to do is we are going to return a native JavaScript API for formatting so intl.numberformat n-us and then open options so style is going to be currency currency will be US dollars minimum fraction digits will be 2 and then we're gonna attach .format and pass in the value there we go Now go back inside of columns and you can import format currency from Libutils and pass in the amount.
So just ensure that you've added this import right here. Let's check this out and there we go. So you can see how this looks now and you probably also see a little bug here. So this is definitely a larger amount than what I've entered but don't worry we are going to resolve this later. Because remember we are working with that milli units value, right?
So we have to take care of that on the front end, but we're gonna do it later. All right, so we have this, and now what I wanna do is I wanna use the badge element instead. So let's go ahead and do the following. Let's go inside of the terminal and let's do bunx chat-cnui at latest add-batch. Like this.
Let's do bun run dev again. Let's refresh our localhost and inside of here instead of using span we're going to be using that new batch component which we've just installed. So just make sure you've added this. All right, and now we're gonna go ahead and give it some properties. So variant, if amount is larger than zero, in that case it's going to be destructive.
Otherwise, let's go ahead and let's make it the default. And let's give it a class name of TextExtraSmallFontMediumPx3.5 and Py2.5. Like this. So let's see if this is going to improve anything alright so it kind of looks better but we're gonna go ahead and we're going to modify these variants now So let's go inside of our components folder UI and go inside of badge directly. So I want to add another variant here.
So right here after outline I'm gonna add primary to be border transparent. BG blue 500 slash 10 and text blue 500. And then inside of my columns, inside of transactions, right? So where we just were adding the badge, instead of using default, I'm gonna be using primary like this. So let's see how that looks like.
There we go. So this is how I want it to look like for positive income. And for negative income, I want it to look red, but not that red. So I'm gonna go and find the destructive variant which is what we're using here. Right so find destructive and I'm gonna change this to be bg destructive slash 10 and I'm gonna go ahead and remove the hover like this and I'm gonna leave the text to just be destructive as well so text-destructive and bg-destructive-10 and border-transparent so that's how I'm gonna modify the batch component and there we go this looks like something now great so let's go ahead and see what else do we have to add so now we have to add the last field here which is going to be the account.
So go ahead and find category and copy it because it's the most similar one. So just above the actions go ahead and add account and this will say account and inside of here row.original.account we are going to modify both the account and the category later and there we go. So we now have everything we need here to display the proper transactions. And now we have to discuss this amount option. So what's going on?
This is in milli units, right? So this is, I believe, maybe $10 and 50 cents, but we store it as 1, 500, I think. Right, so that's an issue, right? We store it in milli units. So you have to find one place where you would convert those milli units into, well, dollars, right?
So you can choose how you want to do it if you are certain that you are always going to do always going to wrap currency inside of this format currency what you can do is you can convert this value for example const final value into convert amount from milli units and passing value here and then add final value here instead so just import convert amount from mil actually you don't have to import it's right here. And if you try it now, there we go. So now it looks normal, right? So that's one option, you know, but I kind of don't like this. I don't know why I don't want to depend on this because sometimes I want to use some different formatting value maybe.
So let's bring this back to just use value and instead I'm gonna show you what I did in my code. So what I did originally, you can choose which option you like. What I did was I went into Features, Transactions, API, Use Get Transactions. Inside of here I modified the data. So this is what I did.
I returned data.map, I got the transaction inside of here and I spread the transaction and I only modify the amount by adding convert amount from milli units so make sure you add this import right here and I simply pass the transaction.amount instead. So that is what I did. And now I don't care about my formatting function, right? Let me just refresh to see if this is still working. There we go.
So you can see how now it's working just fine because this is kind of closer to the backend, let's say in some way, right? This is right here in use query. You can of course find many different ways to transform data with React query. There's also something called select. It's not here, it's outside.
So here you can add select, for example, you would get the data. So you could do that here if you want to. They have a couple of blog posts about converting data. Right. I think this is completely fine for our case, right?
You can also play around with memorization or you can do the formatting thing, right? Whatever you like. But I think for the tutorial, this is more than enough and it kind of gives me confidence that my data will behave exactly how I want it regardless of where I use this hook because I know my data will be normalized here. Great so we resolved that now let's go ahead and let's see what's going on with our little bug there. So I think if I'm gonna keep my terminal open to see what's going on.
I'm gonna create a new transaction. And I think I picked 12th of May. I picked an account. I wrote something. I added something and I clicked create a transaction and transaction seems to have been created right here, but it's not loaded here.
And if I go into API transactions I also only have two. Alright so something is wrong so I'm gonna go ahead and debug that in the next chapter For now I think it's enough for us to see these transactions right here. Alright, so I realized what is the bug. No need to debug it in the next chapter. So this is actually quite important to mention in case you're encountering this.
There is no bug, nothing is happening. So the records actually do exist. But if you remember inside of our use get transactions here, we have this from and to params and we are passing empty ones at the moment. So if you remember our API for transactions inside of the global GET right here, I think close the other ones, inside of the first GET one. If we don't have FROM and TO, we default to the last 30 days.
Which means if you create a transaction in the future it's not going to be displayed here. So go ahead and try and create a transaction in the last 30 days. So for me it's the 9th of May so if I choose the 1st of May it's not gonna have any problems so let me show you this test and let's use this much, test, create a transaction, there we go. It's right here. Great, so we can continue developing on this.
I thought that we're gonna have to debug something, but we don't. Great, so what I wanna do now is I want to modify this category and this account field so that we can easily switch it, right? I want to click here, for example, and also if something is uncategorized, I want to display uncategorized option here. So let's go ahead inside of our columns for transactions where we just added the account, the last one. And let's go ahead and do the account column first.
So inside of the transactions here, we're going to create a new file account-column.esx like this. And let's go ahead and do the following. Let's import useOpenAccount, my apologies, right here. And let's go ahead and import CN from at slash lib slash utils. I think we're going to need this.
Let's write type props here. And let's make it an id of string, account of string or null, and account id of string or null as well. Let's export const account column. And let's make this id account and account id and let's just assign the props here. And now inside of here, I want to create a div which will very simply either say account or, well, it has to have an account actually.
There is almost no chance that the account doesn't exist. So we can do this. Account always has to exist. And this will be account ID. And let me not misspell account.
There we go. So account always has to exist. We don't have to worry about that here. So I'm gonna go ahead and give this a class name of flex-item-center-cursor-pointer-and-hover-underline like this. And now what we can do is we can also import we can use this use open account here so let's do that const on open on open account let's give it a alias, right?
So it's more readable so const on click very simply it's gonna call on open account with the account ID So we are reusing that hook to edit any account. So we can add this on click here. There we go. And now we can go back inside of our columns. We can find the account here, find the cell and instead what we're gonna do is we're gonna use that account column from .slash account column so just make sure you've added an import for the account column here and pass in the id to be row.original.id account row.original.account and account id row.original.accountid like this And let's see if we are missing anything inside of here.
We actually don't need the ID property. So we can remove this and we can remove the CN import. And now we should get an error here, but my TypeScript is slow today. Whoops. So I should get an error here.
There we go. So remove that. And now if I refresh here, When I hover, there is an underline and I should be opening this but it looks like it's not opening. So let's see. Is there any error being thrown?
There is not. Alright, so let's see what's going on. Row original account ID. So I'm going to go ahead and do the following. I'm going to console.log the account ID and see if that changes anything.
So let me refresh this and let me keep my console open. Alright so we do have account ID but it is not opening. Let's see accounts. So Let's see accounts. So this is opening just fine.
All right, so something is a bit off, it seems. All right, so this is the issue. It is very very unusual. So I did some changes here because I could not understand why the account was not opening. Well, it's our fault.
More specifically, it's my fault. I didn't look at the import. We are importing from transactions hooks because we copied the entire features from accounts including the hooks. We need this hook but I am importing this one. Use open account.
So we don't need that, we need the one from accounts. My apologies, I was losing my mind over why this is not working, but let's try it out now. So if I refresh there we go and I can rename this to cash too and okay so now we have to go back I believe inside of our features, accounts, API, use edit account there we go, invalidate summary and transactions so let's now add transactions inside So if we try this again now. Let me pick an account here. So cash 23 save changes.
There we go. So we can now easily modify an account if we want to. And now let's do the same thing for the category. So I'm going to go inside of my app folder, dashboard transactions, and I'm going to copy and paste the account column and I'm going to call this category column. Inside of here, we're going to have category and we're going to have category ID, but the thing with this is that it can be, undefined.
So we're going to add or null here. And we are also going to have the ID here, which is something we didn't have in the last one. So inside of here, we're going to have an ID. We're going to have a category and we're going to have a category ID like this. And instead of use open account, this time we're gonna have an import use open trend, category from features, categories hooks, use open category.
Let's add it here. Let's alias it to on open category and we're gonna pass in the category ID inside. But only if we have category ID like this. And inside of here we are going to write category or uncategorized and we are also gonna say if we don't have category in that case we are going to render a triangle alert icon from Lucid React. So we kind of warn the user.
Let's give this a class name of MR2Size4Shrink0. And let's also add the import for CN from Libby Utils. Let me just separate this from the features above and let's rename this to category column, of course. So I'm gonna go ahead and turn these into hard code from hard coded classes into dynamic. So this will be the default ones.
And then I'm going to add, if we don't have transaction, sorry, if I don't have category, In that case, it's going to be text rows 500 like this. There we go. Let's go inside of columns and the same way we used account column, let's find the category. There we go. So find the accessory key category, find the cell and change this to use this but instead it's going to be category column.
So import category column just below the account column here. This is going to be category and this will be category ID and we are also missing the ID which will be row.original.id. You're going to see why we need the ID in a moment. So and now let's also prepare ourselves by going inside of features, categories, API, use edit category and go ahead and add invalidate transactions here so we can remove this part and we can also do it in use delete category so we can do that here as well like that and we can do the same thing in accounts delete account same thing transactions all right And now let's go ahead and check this out. So if I refresh here and if I select food, it opens food, if I rename it, There we go.
All categories are now renamed. Perfect. But we still need to handle the case if category doesn't exist. And we're gonna go ahead and do that next. So we can't yet do it because we are missing a hook and the hook we are missing is features, transactions, hooks, this one.
So right now we have use open account let's create use open transaction finally. So this was the hook that caused that problem because it's named exactly the same because we copied it. So open transaction state here and everything else can stay exactly the same and now we're gonna go inside of this components and we are going to modify the edit account sheet to be edit transaction sheet. There we go. Let's go ahead and let's rename it so edit transaction sheet and let's immediately go inside of providers, sheet provider and let's add it here.
So import, edit, transaction, like that and let's add it right here. So we don't forget that. Great, and now inside of here, we're not gonna use useOpenAccount, so we can remove that. Instead, let's add import useOpenTransaction from ./.hooks or features transactions like this. So we're basically gonna have to replace all instances of anything account based like this.
So let's go and remove use get account in favor of use get transaction. So I'm gonna change this to Features, Transactions, like this. Let's find where we use GetAccount, we use it right here. Let's modify this to be TransactionQuery. And let's use it right here.
Let's see if we're using it anywhere else. And we are using it right here. So let's go ahead and modify that. So if we have transactionQuery.data, in that case, we're gonna have account ID be transactionQuery data account ID. And then we're gonna have category ID then we're going to have amount but we're gonna do amount dot to string like this then we're gonna have date So we're gonna check if we have the date.
In that case, we're gonna parse that as an actual date like this. So let me go ahead and collapse this. Otherwise, it's just gonna be your basic new date. Next one is gonna be payee, so that's gonna be transaction query data payee. And last one is going to be notes like this.
So let's go ahead and just make this all empty. So account ID, empty, category ID, amount, date, payee and notes like this. There we go. So those are our default values here. Let's see what else we have.
So we have use edit account. Let's remove use edit account in favor of use edit transaction from features transactions api so let's see where that is there we go use edit the transaction we don't have to modify the naming here, that's fine. We have use delete account, let's remove that as well in favor of use delete transaction like this, from features, transactions, like that and let's use it here the naming is fine as well great now we have to modify the form values so we no longer need insert account schema instead we need insert transaction schema So we're going to modify this to be transaction schema.omit ID true. So not pick, but omit, right? There we go.
So that should resolve a couple of errors here. Now let's modify the naming here. So it's not going to be edit account, but instead edit transaction. Inside of here it's going to say edit an existing transaction. There we go.
We are no longer going to be using the account form, instead we're going to be using the transaction form from .slash transaction form, or you can rename it to features, transactions, components. Whoops. Transaction form. And let me just move it with these ones and I can now remove account form. If I search for account, there we go.
I only have this instance it looks like. Okay, four others. So first let's go ahead and do this. You are about to delete this transaction. Let's see, where else do we have this?
Account ID is fine. Great, this is fine, perfect. So now we have to resolve this. So the default values seems to have some issues, so we'll resolve that in a moment. What we have to do first is we have to also add a key here, ID, so it destroys and re-renders this form on every chain.
Actually, we might not need that. I think that was from my previous exercise that I did. All right so this is what we have to do now. We have to go inside of our new transaction sheet and we have to do this. Remember, we need the category and the account options.
And let's go ahead and copy all of that. And let's add it to the code right here. So I'm gonna do it just after our transaction hooks. We add all of these here. And we can actually copy the imports.
It might be easier. They are right here. So accounts and categories, or you can just manually add these. So let me separate these from transactions. Let me save.
And there we go. I no longer have any of these errors right here. Now let me extend my isPending. So besides this, edit and delete, we are also going to have the transaction query is loading, we are also going to have category mutation is pending, and we are also going to have account mutation is pending. And for the is loading, we are going to have transaction query is loading, category query is loading, or account query is loading.
So a bunch of options like this, there we go. So now let's go ahead and let's add all of those things to the transaction form, or we can just copy from here. The props should be exactly the same. And don't forget to bring back ID because I just removed it. There we go.
So make sure you have ID inside of here. There we go. But we also need to pass in the default values, right? So let's add default values to be default values right here. And this is the place where we are getting some errors.
So the property date is not assignable it seems. So let me see if it's maybe because of this what if I just mark this as undefined okay so it's because of that okay so can I just write new date here Okay that seems to solve it so it just must not be a string? All right there we go. So I think that we are ready to try this out. Of course I have these errors now this is again TypeScript losing its mind okay it's good now so this was a very big component you can of course copy it from my github if you're unsure and we are not using onDelete anywhere so let's go ahead and do that so we have onSubmit but we don't have onDelete because this is an edit method.
There we go onDelete. Let's try this out now. Before we can try it out we have to go inside of app, dashboard, transactions. So inside of columns here but more specifically we need our actions. So the last one right here.
So this is the last place to replace the account instances. So let's import useOpenTransaction here. Let's see where we are using it. We are using it right here. It should be throwing an error.
There we go. It's throwing an error. Now it's no longer throwing an error and the last one use delete account, replace that with use delete transaction, there we go let's add that here and everything else should be exactly the same and let's just change this to say you are about to delete this transaction. There we go I think that should be it so now if I go ahead and click edit there we go I can now edit this perfect So that's exactly what I wanted to do. Great!
So let's go ahead and try something else now. So now I'm gonna go ahead and create a new one. So make sure it's in the last 30 days. But this time I'm not going to select a category there we go so now it says uncategorized so now when I try and open this nothing happens so what I want to happen is that when I click on something uncategorized I want the edit to open So we're gonna go back inside of the columns, find the category column. So that's why we are passing in the ID.
So that inside of here I can add an open on open transaction. Use open transaction like this, right? So I want another feature here, use open transaction. And then else, if we don't have, we're going to use on open transaction here with the ID from the props. Right?
Because we are passing it. So now if I finally refresh and if I click on categorize, there we go. I can now select the category, I can save and it is resolved. Perfect. Let's go ahead and let's try individual delete from actions.
Confirm. There we go. Let's try and open this and delete from here. There we go. And let's select these three and click delete.
There we go. Works perfectly. And now what I want to do is, since we copied the entire features thing from accounts, let's go ahead and right-click. Let's click Find in Folder and let's search for account. Alright, so account ID is fine.
That's completely fine, right? Use get accounts, that's fine. Use create account, all of those things are fine. So I'm looking for something that might tell me that I forgot to rename something. But all of these are just options to you know fill the account field so that's completely fine.
Now I just want to check inside of my app folder dashboard transactions so I'm gonna right-click find in folder again account so account column that's completely fine. Columns itself uses the account column, that's completely fine as well. So nothing strange here. Perfect. So we now have a working transaction history.
But one thing I think we forgot to do, so let me again try something in the last 30 days. And I'm gonna call this, for example, search, and let's give this a price. So if I try and search this, nothing happens. I cannot filter here. That's because we forgot inside of our app folder, dashboard transactions page, we forgot to add a proper filter key.
So let's make that payee like this. So now if I write search, it's this one. If I write something else, it does not exist. Amazing, amazing job. You've wrapped up the transactions page.
Well, not exactly because in the next thing what we have to do is we have to enable file upload via CSV so that's what we're gonna do in the next chapter we are gonna use that bulk create endpoint. Great great job!