Skip to content

Commit

Permalink
Migrate txns to Firestore
Browse files Browse the repository at this point in the history
  • Loading branch information
amitsingh-007 committed Dec 21, 2024
1 parent 2f7e6ea commit 3d046c5
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 86 deletions.
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default tseslint.config(
'unicorn/no-array-reduce': 'off',
'unicorn/explicit-length-check': 'off',
'unicorn/no-useless-undefined': 'off',
'unicorn/no-array-for-each': 'off',
},
},
// ? NOTE: always keep this at last; prettier eslint config.
Expand Down
2 changes: 1 addition & 1 deletion src/app/add-card/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CardBrandEnum, TCardBrand } from '@/types/firestore';
import { CardBrandEnum, TCardBrand } from '@/types';
import { z } from 'zod';

export const cardBrandList: { id: TCardBrand; name: string }[] = [
Expand Down
10 changes: 5 additions & 5 deletions src/app/add-transaction/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ const AddTransaction = () => {
},
});

const { data: cardData } = trpc.card.getAll.useQuery();
const { data: cards } = trpc.card.getAll.useQuery();
const { mutateAsync: saveCardTransaction, isLoading } =
trpc.transaction.add.useMutation();

Expand All @@ -73,15 +73,15 @@ const AddTransaction = () => {
};

const cardOptions = useMemo(() => {
if (!cardData) {
if (!cards) {
return [];
}
return Object.entries(cardData).map(([cardId, card]) => ({
id: cardId,
return cards.map((card) => ({
id: card.id,
name: card.cardName,
cardBrand: getCardBrand(card.cardBrand),
}));
}, [cardData]);
}, [cards]);

return (
<>
Expand Down
30 changes: 14 additions & 16 deletions src/app/dashboard-history.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,22 +41,20 @@ const DashboardHistory = () => {
<Skeleton className="rounded-xl h-[104px]" />
</Card>
))}
{recentTransactionsData.map(
({ cardDetails, transaction, transactionId }) => (
<Card key={transactionId}>
<CardHeader className="flex flex-row justify-between p-4">
<CardName
cardBrandId={cardDetails.cardBrand}
cardName={cardDetails.cardName}
/>
<span>{getFormattedPrice(transaction.amount)}</span>
</CardHeader>
<CardFooter className="p-4 pt-0">
{dayjs(transaction.date).format('DD MMM YYYY')}
</CardFooter>
</Card>
)
)}
{recentTransactionsData.map(({ cardDetails, transaction }) => (
<Card key={transaction.id}>
<CardHeader className="flex flex-row justify-between p-4">
<CardName
cardBrandId={cardDetails.cardBrand}
cardName={cardDetails.cardName}
/>
<span>{getFormattedPrice(transaction.amount)}</span>
</CardHeader>
<CardFooter className="p-4 pt-0">
{dayjs(transaction.date).format('DD MMM YYYY')}
</CardFooter>
</Card>
))}
{hasNextPage && (
<Button
onClick={() => fetchNextPage()}
Expand Down
12 changes: 6 additions & 6 deletions src/app/dashboard-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,19 @@ const DahsboardTable = () => {
</TableBody>
) : (
<TableBody>
{monthCardsData.map(({ cardId, cardDetails, transaction }) => {
const cardBrand = getCardBrand(cardDetails.cardBrand);
{monthCardsData.map(({ card, transaction }) => {
const cardBrand = getCardBrand(card.cardBrand);

return (
<TableRow key={cardId}>
<TableRow key={card.id}>
<TableCell className="font-medium">
<CardName
cardBrandId={cardBrand?.id}
cardName={cardDetails.cardName}
cardName={card.cardName}
/>
</TableCell>
<TableCell>
<BillingDate billingDate={cardDetails.cardBillingDate} />
<BillingDate billingDate={card.cardBillingDate} />
</TableCell>
<TableCell>
{getFormattedPrice(transaction?.amount)}
Expand All @@ -169,7 +169,7 @@ const DahsboardTable = () => {
<DropdownMenuItem
disabled={!!transaction}
onClick={() => {
router.push(`/add-transaction?cardId=${cardId}`);
router.push(`/add-transaction?cardId=${card.id}`);
}}
>
Record transaction
Expand Down
22 changes: 10 additions & 12 deletions src/app/saved-cards/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,14 @@ import CardChip from './card-chip';
export default function MyCards() {
const { user } = useUser();

const { data: cardData, isLoading } = trpc.card.getAll.useQuery();
const { data: cards, isLoading } = trpc.card.getAll.useQuery();

const sortedCards = useMemo(() => {
if (!cardData) {
if (!cards) {
return [];
}
return Object.entries(cardData)
.map(([cardId, card]) => ({ ...card, cardId }))
.sort((a, b) => a.cardName.localeCompare(b.cardName));
}, [cardData]);
return cards.sort((a, b) => a.cardName.localeCompare(b.cardName));
}, [cards]);

return (
<>
Expand All @@ -43,16 +41,16 @@ export default function MyCards() {
<Skeleton className="rounded-xl h-[210px]" />
</Card>
))
: sortedCards.map((cardDetails) => {
const cardBrand = getCardBrand(cardDetails.cardBrand);
: sortedCards.map((card) => {
const cardBrand = getCardBrand(card.cardBrand);

return (
<Card key={cardDetails.cardId} className="max-w-md">
<Card key={card.id} className="max-w-md">
<CardHeader className="pb-4">
<CardTitle>
<CardName
cardBrandId={cardBrand?.id}
cardName={cardDetails.cardName}
cardName={card.cardName}
/>
</CardTitle>
</CardHeader>
Expand All @@ -61,14 +59,14 @@ export default function MyCards() {
<Nfc className="h-6" />
</CardContent>
<CardContent className="flex items-center gap-1 tracking-[5px] text-lg font-medium pb-4">
**** **** **** {cardDetails.cardLastDigits}
**** **** **** {card.cardLastDigits}
</CardContent>
<CardFooter className="flex items-center justify-between">
<p className="uppercase text-lg font-semibold tracking-widest">
{user?.displayName}
</p>
<BillingDate
billingDate={cardDetails.cardBillingDate}
billingDate={card.cardBillingDate}
ordinalClassName="font-medium"
/>
</CardFooter>
Expand Down
31 changes: 13 additions & 18 deletions src/app/utils.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,47 @@
import { TCardData, TCardTransaction } from '@/types/firestore';
import { TCard, TCardTransaction } from '@/types/firestore';
import { InfiniteData } from '@tanstack/react-query';

export const getMergedCardsData = (
cardsData: Record<string, TCardData> | undefined,
cards: TCard[] | undefined,
cardTransactions: TCardTransaction[] | undefined
) => {
if (!cardsData) {
if (!cards?.length) {
return { monthCardsData: [], total: 0 };
}
const cardTransactionMap = cardTransactions?.length
? new Map(cardTransactions.map((txn) => [txn.cardId, txn]))
: new Map<string, TCardTransaction>();

let total = 0;
const monthCardsData = Object.entries(cardsData)
.map(([cardId, cardDetails]) => {
const transaction = cardTransactionMap.get(cardId);
const monthCardsData = cards
.map((card) => {
const transaction = cardTransactionMap.get(card.id);
total += transaction?.amount || 0;

return {
cardId: cardId,
cardDetails,
transaction,
};
return { card, transaction };
})
.sort(
(a, b) => a.cardDetails.cardBillingDate - b.cardDetails.cardBillingDate
);
.sort((a, b) => a.card.cardBillingDate - b.card.cardBillingDate);
return { monthCardsData, total };
};

export const getMergedTxnData = (
cardsData: Record<string, TCardData> | undefined,
cards: TCard[] | undefined,
cardTransactions: InfiniteData<TCardTransaction[] | undefined> | undefined
) => {
if (!cardsData || !cardTransactions) {
if (!cards?.length || !cardTransactions) {
return [];
}

const cardsMap = Object.fromEntries(cards.map((card) => [card.id, card]));

return cardTransactions.pages
.flatMap((page) => {
if (!page?.length) {
return [];
}
return page.map((transaction) => {
return {
transactionId: transaction.id,
cardDetails: cardsData[transaction.cardId],
cardDetails: cardsMap[transaction.cardId],
transaction,
};
});
Expand Down
8 changes: 5 additions & 3 deletions src/server/routers/card-router.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import { CardDataRecordSchema, CardDataSchema } from '@/types/firestore';
import { z } from 'zod';
import { addCard, getAllCards } from '../services/card-service';
import { protectedProcedure } from './procedures';
import { t } from './trpc';
import { CardFormSchema } from '@/types/form';
import { CardSchema } from '@/types/firestore';

const cardRouter = t.router({
getAll: protectedProcedure
.output(CardDataRecordSchema)
.output(z.array(CardSchema))
.query(async ({ ctx }) => getAllCards(ctx.user)),

add: protectedProcedure
.input(CardDataSchema)
.input(CardFormSchema)
.mutation(async ({ ctx, input }) => addCard(ctx.user, input)),
});

Expand Down
10 changes: 5 additions & 5 deletions src/server/services/card-service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { COLLECTION } from '@/types/database';
import { UserRecord } from 'firebase-admin/auth';
import { appendToList, fetch } from '../firebase-admin/database';
import { TCardData } from '@/types/firestore';
import { addToCollection, fetch } from '../firebase-admin/firestore';
import { TCardForm } from '@/types/form';

export const getAllCards = async (user: UserRecord) => {
return fetch({ relPath: COLLECTION.CARDS, user });
return fetch({ user, collection: COLLECTION.CARDS });
};

export const addCard = async (user: UserRecord, card: TCardData) => {
await appendToList(user, COLLECTION.CARDS, card);
export const addCard = async (user: UserRecord, data: TCardForm) => {
await addToCollection(user, COLLECTION.CARDS, data);
};
6 changes: 3 additions & 3 deletions src/types/database.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UserRecord } from 'firebase-admin/auth';
import { OrderByDirection, WhereFilterOp } from 'firebase-admin/firestore';
import { CardDataSchema, CardTransactionSchema, TCardData } from './firestore';
import { CardSchema, CardTransactionSchema, TCard } from './firestore';
import { z } from 'zod';

export enum COLLECTION {
Expand All @@ -9,13 +9,13 @@ export enum COLLECTION {
}

export const CollectionSchema = {
[COLLECTION.CARDS]: CardDataSchema,
[COLLECTION.CARDS]: CardSchema,
[COLLECTION.TRANSACTIONS]: CardTransactionSchema,
};

type TFetchCard = {
relPath: COLLECTION.CARDS;
orderByChild?: keyof TCardData;
orderByChild?: keyof TCard;
};

export type TFetch = {
Expand Down
21 changes: 4 additions & 17 deletions src/types/firestore.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,14 @@
import { z } from 'zod';
import { CardBrandEnum } from '.';

export const CardBrandEnum = z.enum([
'icici',
'hdfc',
'sbi',
'kotak',
'axis',
'indusind',
'hsbc',
'niyo',
'onecard',
]);
export type TCardBrand = z.infer<typeof CardBrandEnum>;

export const CardDataSchema = z.object({
export const CardSchema = z.object({
id: z.string(),
cardBrand: CardBrandEnum,
cardName: z.string(),
cardLastDigits: z.number(),
cardBillingDate: z.number(),
});
export type TCardData = z.infer<typeof CardDataSchema>;

export const CardDataRecordSchema = z.record(z.string(), CardDataSchema);
export type TCard = z.infer<typeof CardSchema>;

export const CardTransactionSchema = z.object({
id: z.string(),
Expand Down
9 changes: 9 additions & 0 deletions src/types/form.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
import { z } from 'zod';
import { CardBrandEnum } from '.';

export const CardFormSchema = z.object({
cardBrand: CardBrandEnum,
cardName: z.string(),
cardLastDigits: z.number(),
cardBillingDate: z.number(),
});
export type TCardForm = z.infer<typeof CardFormSchema>;

export const CardTransactionFormSchema = z.object({
cardId: z.string(),
Expand Down
14 changes: 14 additions & 0 deletions src/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from 'zod';

export const CardBrandEnum = z.enum([
'icici',
'hdfc',
'sbi',
'kotak',
'axis',
'indusind',
'hsbc',
'niyo',
'onecard',
]);
export type TCardBrand = z.infer<typeof CardBrandEnum>;

0 comments on commit 3d046c5

Please sign in to comment.