Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: setup grant page story #85

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions src/app/grants/[grantId]/grantees/[granteeId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import { getGrantDetails } from '@/grants/data/get-grant-details';
import { getGranteeDetails } from '@/grants/data/get-grantee-details';

import { GranteePageLayout } from '@/grants/pages/grantee-page-layout';

interface Props {
children: React.ReactNode;
params: { grantId: string; granteeId: string };
}

const GranteePage = ({ children, params: { grantId, granteeId } }: Props) => {
const GranteePage = async ({
children,
params: { grantId, granteeId },
}: Props) => {
const baseHref = `/grants/${grantId}/grantees/${granteeId}/projects`;

const grant = await getGrantDetails(grantId);
const grantee = await getGranteeDetails(grant.data.id);

return (
<GranteePageLayout grantId={grantId} granteeId={granteeId}>
<GranteePageLayout baseHref={baseHref} grantee={grantee.data}>
{children}
</GranteePageLayout>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
export { GranteeProjectTabsPage as default } from '@/grants/pages/grantee-project-tabs-page';
import { getGranteeProject } from '@/grants/data/get-grantee-project';
import { GranteeProjectStats } from '@/grants/components/grantee-project/project-stats';

interface Props {
params: {
projectId: string;
tab: string;
};
}

const GranteeProjectTabsPage = async ({
params: { projectId, tab },
}: Props) => {
const { data } = await getGranteeProject(projectId);

const projectTab = data.tabs.find((t) => t.tab === tab);
if (!projectTab) return null;

return <GranteeProjectStats stats={projectTab.stats} />;
};

export default GranteeProjectTabsPage;
22 changes: 21 additions & 1 deletion src/app/grants/[grantId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
export { GrantPageLayout as default } from '@/grants/pages/grant-page-layout';
import { getGrantDetails } from '@/grants/data/get-grant-details';

import { GrantPageLayout } from '@/grants/pages/grant-page-layout';

interface Props {
children: React.ReactNode;
list: React.ReactNode;
params: { grantId: string };
}

const Layout = async ({ children, list, params: { grantId } }: Props) => {
const { data } = await getGrantDetails(grantId);

return (
<GrantPageLayout grant={data} list={list}>
{children}
</GrantPageLayout>
);
};

export default Layout;
4 changes: 2 additions & 2 deletions src/grants/components/grant-stat-item/grantee-stat-item.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { cn } from '@/shared/utils/cn';

import { GranteeStat } from '@/grants/core/schemas';
import { GranteeProjectStat } from '@/grants/core/schemas';

interface Props {
granteeStat: GranteeStat;
granteeStat: GranteeProjectStat;
level?: number;
}

Expand Down
4 changes: 3 additions & 1 deletion src/grants/components/grantee-list/grantee-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { useParams } from 'next/navigation';
import { useMemo } from 'react';

import { cn } from '@nextui-org/react';

import { VirtualWrapper } from '@/shared/components/virtual-wrapper';

import { GranteeListItem } from './item';
Expand Down Expand Up @@ -35,7 +37,7 @@ export const GranteeList = () => {
<div className="flex flex-col gap-4">
<VirtualWrapper count={grantees.length}>
{(index) => (
<div className="pt-8">
<div className={cn({ 'pt-8': index > 0 })}>
<GranteeListItem grantee={grantees[index]} />
</div>
)}
Expand Down
2 changes: 1 addition & 1 deletion src/grants/components/grantee-list/item/client-wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const ClientWrapper = ({
className={cn(
'bg-white/5',
{
'bg-gradient-to-l from-[#0D0D0D] to-primary': isActive,
'is-active': isActive,
},
className,
)}
Expand Down
4 changes: 2 additions & 2 deletions src/grants/components/grantee-project/project-stats.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { GranteeStat } from '@/grants/core/schemas';
import { GranteeProjectStat } from '@/grants/core/schemas';
import { GranteeStatItem } from '@/grants/components/grant-stat-item';

interface Props {
stats: GranteeStat[];
stats: GranteeProjectStat[];
}

export const GranteeProjectStats = ({ stats }: Props) => {
Expand Down
2 changes: 2 additions & 0 deletions src/grants/core/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ export const GRANT_TEST_IDS = {
} as const;

export const GRANT_QUERY_URLS = {
GRANT_DETAILS: `${MW_URL}/grants/details`,
GRANT_LIST: `${MW_URL}/grants/list`,
GRANTEE_DETAILS: `${MW_URL}/grantees/details`,
GRANTEE_LIST: `${MW_URL}/grantees/list`,
GRANTEE_PROJECT: `${MW_URL}/grantees/project`,
} as const;
3 changes: 3 additions & 0 deletions src/grants/core/query-keys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const grantQueryKeys = {

return [...grantQueryKeys.all, 'list', searchParams] as const;
},
details: (grantId: string) => {
return [...grantQueryKeys.all, 'grant', grantId] as const;
},
grantees: (grantId: string, params: string | Record<string, string>) => {
const searchParams =
typeof params === 'string'
Expand Down
23 changes: 16 additions & 7 deletions src/grants/core/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@ export const grantSchema = z.object({
discord: z.string().nullable(),
granteesCount: z.number(),
});

export type Grant = z.infer<typeof grantSchema>;

export const grantDTOSchema = genericResponseSchema.extend({
data: grantSchema,
});
export type GrantDTO = z.infer<typeof grantDTOSchema>;

export const granteeSchema = z.object({
id: z.string(),
name: z.string(),
Expand All @@ -40,9 +44,13 @@ export const granteeSchema = z.object({
fundingDate: z.number(),
projects: z.array(z.string()),
});

export type Grantee = z.infer<typeof granteeSchema>;

export const granteeDTOSchema = genericResponseSchema.extend({
data: granteeSchema,
});
export type GranteeDTO = z.infer<typeof granteeDTOSchema>;

export const grantListQueryPageSchema = z.object({
page: z.number().optional(),
data: z.array(grantSchema),
Expand All @@ -64,15 +72,16 @@ type BaseStat = z.infer<typeof baseStatSchema> & {
stats?: BaseStat[];
};

export const granteeStatSchema: z.ZodType<BaseStat> = baseStatSchema.extend({
stats: z.lazy(() => granteeStatSchema.array()).optional(),
});
export type GranteeStat = z.infer<typeof granteeStatSchema>;
export const granteeProjectStatSchema: z.ZodType<BaseStat> =
baseStatSchema.extend({
stats: z.lazy(() => granteeProjectStatSchema.array()).optional(),
});
export type GranteeProjectStat = z.infer<typeof granteeProjectStatSchema>;

export const granteeTabItemSchema = z.object({
label: z.string(),
tab: z.string(),
stats: z.array(granteeStatSchema),
stats: z.array(granteeProjectStatSchema),
});

export const granteeProjectSchema = z.object({
Expand Down
13 changes: 13 additions & 0 deletions src/grants/data/get-grant-details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { mwGET } from '@/shared/utils/mw-get';

import { GRANT_QUERY_URLS } from '@/grants/core/constants';
import { grantDTOSchema } from '@/grants/core/schemas';

export const getGrantDetails = async (grantId: string) => {
return mwGET({
url: `${GRANT_QUERY_URLS.GRANT_DETAILS}/${grantId}`,
label: 'getGrant',
responseSchema: grantDTOSchema,
options: { next: { revalidate: 3600 } },
});
};
13 changes: 13 additions & 0 deletions src/grants/data/get-grantee-details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { mwGET } from '@/shared/utils/mw-get';

import { GRANT_QUERY_URLS } from '@/grants/core/constants';
import { granteeDTOSchema } from '@/grants/core/schemas';

export const getGranteeDetails = async (granteeId: string) => {
return mwGET({
url: `${GRANT_QUERY_URLS.GRANTEE_DETAILS}/${granteeId}`,
label: 'getGranteeDetails',
responseSchema: granteeDTOSchema,
options: { next: { revalidate: 3600 } },
});
};
14 changes: 14 additions & 0 deletions src/grants/hooks/use-grant-details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { useQuery } from '@tanstack/react-query';

import { QUERY_STALETIME } from '@/shared/core/constants';

import { grantQueryKeys } from '@/grants/core/query-keys';
import { getGrantDetails } from '@/grants/data/get-grant-details';

export const useGrantDetails = (grantId: string) => {
return useQuery({
queryKey: grantQueryKeys.details(grantId),
queryFn: () => getGrantDetails(grantId),
staleTime: QUERY_STALETIME.DEFAULT,
});
};
75 changes: 75 additions & 0 deletions src/grants/pages/grant-page-layout.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Meta, StoryObj } from '@storybook/react';

import {
MockInfiniteQueryResult,
MockQueryResult,
} from '@/shared/testutils/misc';

import { Grantee } from '@/grants/core/schemas';
import { GranteeList } from '@/grants/components/grantee-list';
import { GranteeProjectStats } from '@/grants/components/grantee-project/project-stats';

import { fakeGrant } from '@/grants/testutils/fake-grant';
import { fakeGrantee, fakeGrantees } from '@/grants/testutils/fake-grantee';
import { fakeGranteeProject } from '@/grants/testutils/fake-grantee-project';
import { mockGranteeListQuery } from '@/grants/testutils/mock-grantee-list-query';
import { mockGranteeProjectQuery } from '@/grants/testutils/mock-grantee-project-query';

import { GrantPageLayout } from '@/grants/pages/grant-page-layout';
import { GranteePageLayout } from '@/grants/pages/grantee-page-layout';

const grantProgram = fakeGrant();
const grantee: Grantee = fakeGrantee();
const grantees = fakeGrantees({ firstId: grantee.id });
const project = fakeGranteeProject();

console.log({
granteeId: grantee.id,
granteesFirstId: grantees[0].id,
});

const meta: Meta<typeof GrantPageLayout> = {
title: 'grants/pages/grant-program',
component: GrantPageLayout,
args: {
grant: grantProgram,
list: <GranteeList />,
},
};

export default meta;
type Story = StoryObj<typeof GrantPageLayout>;

export const Default: Story = {
args: {
children: (
<GranteePageLayout
baseHref={`/grants/${grantProgram.id}/grantees/${grantee.id}/projects`}
grantee={grantee}
>
<GranteeProjectStats stats={project.tabs[0].stats} />
</GranteePageLayout>
),
},
parameters: {
nextjs: {
navigation: {
segments: [
['grantId', grantProgram.id],
['granteeId', grantee.id],
['projectId', grantee.projects[0]],
['tab', project.tabs[0].tab],
],
},
},

msw: {
handlers: [
mockGranteeListQuery(MockInfiniteQueryResult.SUCCESS, {
data: grantees,
}),
mockGranteeProjectQuery(MockQueryResult.SUCCESS),
],
},
},
};
24 changes: 10 additions & 14 deletions src/grants/pages/grant-page-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import { Divider } from '@/shared/components/divider';

import { Grant } from '@/grants/core/schemas';
import { GrantBackButton } from '@/grants/components/grant-back-button';
import { GrantCard } from '@/grants/components/grant-card';

import { fakeGrant } from '@/grants/testutils/fake-grant';

interface Props {
children: React.ReactNode;
grant: Grant;
list: React.ReactNode;
params: { grantId: string };
children: React.ReactNode;
}

export const GrantPageLayout = ({
children,
list,
params: { grantId },
}: Props) => {
// TODO: fetch grant using grantId
const grant = fakeGrant();

export const GrantPageLayout = ({ children, list, grant }: Props) => {
return (
<div className="flex flex-col gap-8 p-8">
<div className="flex flex-col gap-6 p-8">
<GrantBackButton fallbackUrl="/grants" />

<GrantCard grant={grant} />

<span>{`Grantee List of grant#${grantId}`}</span>
<span>{`Grantee List of ${grant.name}`}</span>

<Divider />

<div className="flex gap-8">
<div className="w-full shrink-0 lg:w-4/12">{list}</div>
Expand Down
13 changes: 4 additions & 9 deletions src/grants/pages/grantee-page-layout.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { Grantee } from '@/grants/core/schemas';
import { GranteeCard } from '@/grants/components/grantee-card/grantee-card';
import { ProjectSelections } from '@/grants/components/grantee-project/project-selections';
import { ProjectTabSelection } from '@/grants/components/grantee-project/project-tab-selection';

import { fakeGrantee } from '@/grants/testutils/fake-grantee';

interface Props {
grantId: string;
granteeId: string;
baseHref: string;
grantee: Grantee;
children: React.ReactNode;
}

export const GranteePageLayout = ({ grantId, granteeId, children }: Props) => {
const baseHref = `/grants/${grantId}/grantees/${granteeId}/projects`;

// TODO: fetch grantee
const grantee = fakeGrantee();
export const GranteePageLayout = ({ baseHref, grantee, children }: Props) => {
const projects = grantee.projects;
const hasProject = projects.length > 0;

Expand Down
Loading
Loading