Skip to content

Commit

Permalink
change transaction card ui
Browse files Browse the repository at this point in the history
  • Loading branch information
breeg554 committed Sep 20, 2024
1 parent 0c142ce commit 14d633c
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import { OrganizationController } from '../organization.controller';
import { HttpStatus } from '@nestjs/common';
import { v4 as uuidv4 } from 'uuid';
import { beforeEachCreateUser } from '~/tests/beforeEachCreateUser';
import {
itShouldReturnNotFound,
itShouldReturnUnauthorized,
} from '~/tests/it-should-return';
import { itShouldReturn401, itShouldReturn404 } from '~/tests/it-should-return';
import { createSecretFixture } from '~/tests-fixtures/secret.fixture';

describe(OrganizationController.name, () => {
Expand All @@ -18,7 +15,7 @@ describe(OrganizationController.name, () => {
describe('(GET)', () => {
const createGetRequest = () => setup.appRequest().get('/organizations');

itShouldReturnUnauthorized(createGetRequest);
itShouldReturn401('when unauthorized', createGetRequest);

it('should return 200', async () => {
await createOrganizationFixture().saveInDB(
Expand Down Expand Up @@ -54,14 +51,9 @@ describe(OrganizationController.name, () => {
.appRequest()
.get(`/organizations/${organizationFixture.organization.name}`);

itShouldReturnUnauthorized(createGetRequest);
itShouldReturnNotFound(
() =>
setup.withSession(
createUserFixture({ id: uuidv4() }),
createGetRequest,
),
{ description: 'should return 404 if user does not belong to org' },
itShouldReturn401('when unauthorized', createGetRequest);
itShouldReturn404('when user does not belong to org', () =>
setup.withSession(createUserFixture({ id: uuidv4() }), createGetRequest),
);

it('should return 200', async () => {
Expand All @@ -80,7 +72,7 @@ describe(OrganizationController.name, () => {
const createPostRequest = () =>
setup.appRequest().post('/organizations').send({ name: 'NEW ORG' });

itShouldReturnUnauthorized(createPostRequest);
itShouldReturn401('when unauthorized', createPostRequest);

it('should return 201', async () => {
const response = await setup.withSession(userFixture, createPostRequest);
Expand Down Expand Up @@ -117,8 +109,8 @@ describe(OrganizationController.name, () => {
.appRequest()
.get(`/organizations/${organizationFixture.organization.name}/users`);

itShouldReturnUnauthorized(createGetRequest);
itShouldReturnNotFound(() =>
itShouldReturn401('when unauthorized', createGetRequest);
itShouldReturn404('when user does not belong to org', () =>
setup.withSession(createUserFixture({ id: uuidv4() }), createGetRequest),
);

Expand Down Expand Up @@ -149,8 +141,8 @@ describe(OrganizationController.name, () => {
.post(`/organizations/${organizationFixture.organization.name}/secrets`)
.send({ name: 'NEW SECRET', value: 'NEW SECRET VALUE' });

itShouldReturnUnauthorized(createPostRequest);
itShouldReturnNotFound(() =>
itShouldReturn401('when unauthorized', createPostRequest);
itShouldReturn404('when user does not belong to org', () =>
setup.withSession(createUserFixture({ id: uuidv4() }), createPostRequest),
);

Expand Down Expand Up @@ -186,8 +178,8 @@ describe(OrganizationController.name, () => {
`/organizations/${organizationFixture.organization.name}/secrets/${secretFixture.secret.name}`,
);

itShouldReturnUnauthorized(createGetRequest);
itShouldReturnNotFound(() =>
itShouldReturn401('when unauthorized', createGetRequest);
itShouldReturn404('when user does not belong to org', () =>
setup.withSession(createUserFixture({ id: uuidv4() }), createGetRequest),
);

Expand Down
27 changes: 18 additions & 9 deletions apps/api/tests/it-should-return.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,38 @@
import supertest from 'supertest';
import { HttpStatus } from '@nestjs/common';

export function itShouldReturnNotFound(
export function itShouldReturn404(
description: string,
request: () => supertest.Test,
args?: { description?: string },
) {
it(args?.description ?? `should return 404`, async () => {
it(`should return 404 ${description}`, async () => {
await request().expect(HttpStatus.NOT_FOUND);
});
}

export function itShouldReturnUnauthorized(request: () => supertest.Test) {
it('should return 401', async () => {
export function itShouldReturn401(
description: string,
request: () => supertest.Test,
) {
it(`should return 401 ${description}`, async () => {
await request().expect(HttpStatus.UNAUTHORIZED);
});
}

export function itShouldReturnOk(request: () => supertest.Test) {
it('should return 200', async () => {
export function itShouldReturn200(
description: string,
request: () => supertest.Test,
) {
it(`should return 200 ${description}`, async () => {
await request().expect(HttpStatus.OK);
});
}

export function itShouldReturnCreated(request: () => supertest.Test) {
it('should return 201', async () => {
export function itShouldReturn201(
description: string,
request: () => supertest.Test,
) {
it(`should return 201 ${description}`, async () => {
await request().expect(HttpStatus.CREATED);
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

import { GetTransactionDto } from '~/api/Transaction/transactionApi.types';
import { Category } from '~/utils/Category';

interface CategoryCircleProps {
data: GetTransactionDto['categories'][0];
}
export function CategoryCircle({ data }: CategoryCircleProps) {
const categoryItem = new Category(data);

return (
<p
className="w-6 h-6 rounded-full border border-input text-xs flex justify-center items-center bg-white"
aria-label={categoryItem.name}
>
{categoryItem.icon}
</p>
);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import { GetTransactionDto } from '~/api/Transaction/transactionApi.types';
import { CategoryBadge } from '~/dashboard/organization/receipts/components/CategoryBadge';
import { CategoryCircle } from '~/dashboard/organization/receipts/components/CategoryCircle';
import { useDeleteTransaction } from '~/dashboard/organization/receipts/transactions.hooks';
import { ClientDate } from '~/dates/ClientDate';
import {
Expand All @@ -17,7 +17,7 @@ import { ItemList } from '~/list/ItemList';
import { routes } from '~/routes';
import { Skeleton } from '~/ui/skeleton';
import { cn } from '~/utils/cn';
import { CustomDate } from '~/utils/CustomDate';
import { customDate } from '~/utils/CustomDate';

interface ReceiptsListProps {
transactions: GetTransactionDto[];
Expand Down Expand Up @@ -56,8 +56,8 @@ function ReceiptsListItem({ data, onDelete }: ReceiptsListItemProps) {
};

return (
<article className="border border-input rounded-xl p-4 flex flex-col gap-3">
<header className="flex gap-2 justify-between">
<article className="border border-input rounded-xl flex flex-col">
<header className="flex gap-2 justify-between p-4">
<div className="flex gap-2 items-center">
<h1
className="text-foreground font-semibold text-lg line-clamp-1"
Expand All @@ -84,11 +84,11 @@ function ReceiptsListItem({ data, onDelete }: ReceiptsListItemProps) {
</div>
</header>

<main className="grid grid-cols-[2fr_3fr_4fr] gap-1">
<section className="flex flex-col divide-y border-t border-input p-4">
<ReceiptListItemRow>
<ReceiptListItemRowName>Items</ReceiptListItemRowName>
<ReceiptListItemRowContent>
{data.items.length}
<ReceiptListItemRowName>Total</ReceiptListItemRowName>
<ReceiptListItemRowContent title={data.price.formatted}>
{data.price.formatted}
</ReceiptListItemRowContent>
</ReceiptListItemRow>

Expand All @@ -98,31 +98,36 @@ function ReceiptsListItem({ data, onDelete }: ReceiptsListItemProps) {
<ClientDate
fallback={<Skeleton className="w-[95px] h-4 rounded-xl" />}
>
{new CustomDate(data.date).format('dd MMM yyy')}
{customDate(data.date).format('dd MMM yyy')}
</ClientDate>
</ReceiptListItemRowContent>
</ReceiptListItemRow>

<ReceiptListItemRow>
<ReceiptListItemRowName>Total</ReceiptListItemRowName>
<ReceiptListItemRowContent title={data.price.formatted}>
{data.price.formatted}
</ReceiptListItemRowContent>
</ReceiptListItemRow>

<ReceiptListItemRow className="col-span-3">
<ReceiptListItemRowName>Author</ReceiptListItemRowName>
<ReceiptListItemRowContent title={data.author.email}>
{data.author.email}
</ReceiptListItemRowContent>
</ReceiptListItemRow>

<ItemList
items={data.categories}
className="flex flex-wrap gap-1 pt-3 col-span-3"
renderItem={(item) => <CategoryBadge data={item} />}
/>
</main>
<ReceiptListItemRow>
<ReceiptListItemRowName>Items</ReceiptListItemRowName>
<ReceiptListItemRowContent>
{data.items.length}
</ReceiptListItemRowContent>
</ReceiptListItemRow>

<ReceiptListItemRow>
<ReceiptListItemRowName>Categories</ReceiptListItemRowName>
<ItemList
items={data.categories}
className="relative grow flex justify-end -space-x-2 overflow-hidden"
renderItem={(item) => <CategoryCircle data={item} />}
>
<div className="absolute bottom-0 left-0 bg-gradient-to-l from-transparent to-white w-20 h-6 pointer-events-none" />
</ItemList>
</ReceiptListItemRow>
</section>
</article>
);
}
Expand All @@ -133,7 +138,10 @@ function ReceiptListItemRow({
...rest
}: React.HTMLAttributes<HTMLParagraphElement>) {
return (
<div className={cn('flex flex-col', className)} {...rest}>
<div
className={cn('flex gap-4 py-1 items-center justify-between', className)}
{...rest}
>
{children}
</div>
);
Expand All @@ -158,7 +166,7 @@ function ReceiptListItemRowName({
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span
className={cn('text-muted-foreground text-xs shrink-0', className)}
className={cn('text-muted-foreground text-xs shrink-0 w-fit', className)}
{...rest}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ export const ScanPage = () => {
}}
>
<DialogDrawerHeader>
<DialogDrawerTitle>Take or upload photo</DialogDrawerTitle>
<DialogDrawerTitle>Upload photo</DialogDrawerTitle>
<DialogDrawerDescription>
Send a photo of your receipt to automatically create a transaction.
Send a photo of your receipt to automatically extract the items.
</DialogDrawerDescription>
</DialogDrawerHeader>

<DialogDrawerBody>
<ReceiptRetriever
onRetrieve={retrieve}
triggers={({ takePhoto, uploadPhoto }) => (
triggers={({ uploadPhoto }) => (
<div className="flex gap-2 items-center">
<Button
variant="outline"
Expand All @@ -62,14 +62,14 @@ export const ScanPage = () => {
Upload a photo
</Button>

<Button
type="button"
variant="outline"
className="h-[58px] w-full"
onClick={takePhoto}
>
Take a photo
</Button>
{/*<Button*/}
{/* type="button"*/}
{/* variant="outline"*/}
{/* className="h-[58px] w-full"*/}
{/* onClick={takePhoto}*/}
{/*>*/}
{/* Take a photo*/}
{/*</Button>*/}
</div>
)}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export function DescriptionRowContent({
...rest
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<p className={cn('text-foreground line-clamp-1', className)} {...rest}>
<p
className={cn('text-foreground line-clamp-1 text-sm', className)}
{...rest}
>
{children}
</p>
);
Expand All @@ -36,7 +39,10 @@ export function DescriptionRowName({
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span
className={cn('text-muted-foreground text-sm shrink-0', className)}
className={cn(
'text-muted-foreground text-sm shrink-0 text-xs',
className,
)}
{...rest}
>
{children}
Expand Down
2 changes: 2 additions & 0 deletions apps/web/app/utils/CustomDate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,5 @@ export class CustomDate {
return new CustomDate(setHours(this.date, hours));
}
}

export const customDate = (date: DateType): CustomDate => new CustomDate(date);

0 comments on commit 14d633c

Please sign in to comment.