refactor: extract empty state and update basepath to /app/
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -1,10 +1,8 @@
|
|||||||
import type { EventInfo } from './types';
|
import type { EventInfo } from './types';
|
||||||
import { FileQuestionMark } from 'lucide-react';
|
|
||||||
import PlaceholderImage from '@/assets/event-placeholder.png';
|
import PlaceholderImage from '@/assets/event-placeholder.png';
|
||||||
import { useGetEvents } from '@/hooks/data/useGetEvents';
|
import { useGetEvents } from '@/hooks/data/useGetEvents';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { DialogTrigger } from '../ui/dialog';
|
import { DialogTrigger } from '../ui/dialog';
|
||||||
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty';
|
|
||||||
import { EventGridView } from './event-grid.view';
|
import { EventGridView } from './event-grid.view';
|
||||||
import { KycDialogContainer } from './kyc/kyc.dialog.container';
|
import { KycDialogContainer } from './kyc/kyc.dialog.container';
|
||||||
|
|
||||||
@@ -25,11 +23,9 @@ export function EventGridContainer() {
|
|||||||
} satisfies EventInfo));
|
} satisfies EventInfo));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{allEvents.length > 0 && (
|
|
||||||
<EventGridView
|
<EventGridView
|
||||||
events={allEvents}
|
events={allEvents}
|
||||||
assembleFooter={eventInfo => (eventInfo.isJoined
|
footer={eventInfo => (eventInfo.isJoined
|
||||||
? <Button className="w-full" disabled>已加入</Button>
|
? <Button className="w-full" disabled>已加入</Button>
|
||||||
: (
|
: (
|
||||||
<KycDialogContainer eventIdToJoin={eventInfo.eventId}>
|
<KycDialogContainer eventIdToJoin={eventInfo.eventId}>
|
||||||
@@ -40,20 +36,5 @@ export function EventGridContainer() {
|
|||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{
|
|
||||||
allEvents.length === 0 && (
|
|
||||||
<Empty className="h-full">
|
|
||||||
<EmptyHeader>
|
|
||||||
<EmptyMedia variant="icon">
|
|
||||||
<FileQuestionMark />
|
|
||||||
</EmptyMedia>
|
|
||||||
<EmptyTitle>暂无活动</EmptyTitle>
|
|
||||||
<EmptyDescription>前面的区域 以后再来探索吧</EmptyDescription>
|
|
||||||
</EmptyHeader>
|
|
||||||
</Empty>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
16
client/cms/src/components/events/event-grid.empty.tsx
Normal file
16
client/cms/src/components/events/event-grid.empty.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { FileQuestionMark } from 'lucide-react';
|
||||||
|
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from '../ui/empty';
|
||||||
|
|
||||||
|
export function EventGridEmpty() {
|
||||||
|
return (
|
||||||
|
<Empty className="h-full">
|
||||||
|
<EmptyHeader>
|
||||||
|
<EmptyMedia variant="icon">
|
||||||
|
<FileQuestionMark />
|
||||||
|
</EmptyMedia>
|
||||||
|
<EmptyTitle>暂无活动</EmptyTitle>
|
||||||
|
<EmptyDescription>前面的区域 以后再来探索吧</EmptyDescription>
|
||||||
|
</EmptyHeader>
|
||||||
|
</Empty>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,12 +1,18 @@
|
|||||||
import type { EventInfo } from './types';
|
import type { EventInfo } from './types';
|
||||||
import { EventCardView } from './event-card.view';
|
import { EventCardView } from './event-card.view';
|
||||||
|
import { EventGridEmpty } from './event-grid.empty';
|
||||||
|
|
||||||
export function EventGridView({ events, assembleFooter }: { events: EventInfo[]; assembleFooter: (event: EventInfo) => React.ReactNode }) {
|
export function EventGridView({ events, footer }: { events: EventInfo[]; footer: (event: EventInfo) => React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4">
|
<>
|
||||||
|
{events.length > 0 && (
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 h-full">
|
||||||
{events.map(event => (
|
{events.map(event => (
|
||||||
<EventCardView key={event.eventId} eventInfo={event} actionFooter={assembleFooter(event)} />
|
<EventCardView key={event.eventId} eventInfo={event} actionFooter={footer(event)} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
{events.length === 0 && <EventGridEmpty />}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
|
|
||||||
export function configInternalApiClient() {
|
export function configInternalApiClient() {
|
||||||
client.setConfig({
|
client.setConfig({
|
||||||
baseUrl: '/api/v1/',
|
baseUrl: '/app/api/v1/',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Api-Version': 'latest',
|
'X-Api-Version': 'latest',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { createRouter } from '@tanstack/react-router';
|
|||||||
import { routeTree } from '../routeTree.gen';
|
import { routeTree } from '../routeTree.gen';
|
||||||
|
|
||||||
// Create a new router instance
|
// Create a new router instance
|
||||||
export const router = createRouter({ routeTree });
|
export const router = createRouter({ routeTree, basepath: '/app/' });
|
||||||
|
|
||||||
// Register the router instance for type safety
|
// Register the router instance for type safety
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
|
|||||||
@@ -60,14 +60,25 @@ export const Primary: Story = {
|
|||||||
endTime: new Date('2026-06-14T04:00:00.000Z'),
|
endTime: new Date('2026-06-14T04:00:00.000Z'),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
assembleFooter: () => <Button className="w-full">加入活动</Button>,
|
footer: () => <Button className="w-full">加入活动</Button>,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Skeleton: Story = {
|
export const Empty: Story = {
|
||||||
|
decorators: [Story => <div className="h-screen">{Story()}</div>],
|
||||||
|
args: {
|
||||||
|
events: [],
|
||||||
|
footer: () => <Button className="w-full">加入活动</Button>,
|
||||||
|
},
|
||||||
|
parameters: {
|
||||||
|
layout: 'fullscreen',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Loading: Story = {
|
||||||
render: () => <EventGridSkeleton />,
|
render: () => <EventGridSkeleton />,
|
||||||
args: {
|
args: {
|
||||||
events: [],
|
events: [],
|
||||||
assembleFooter: () => <UiSkeleton className="w-full" />,
|
footer: () => <UiSkeleton className="w-full" />,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(file
|
|||||||
|
|
||||||
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: '/app/',
|
||||||
plugins: [tanstackRouter({
|
plugins: [tanstackRouter({
|
||||||
target: 'react',
|
target: 'react',
|
||||||
autoCodeSplitting: true,
|
autoCodeSplitting: true,
|
||||||
@@ -26,7 +27,7 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api': 'http://10.0.0.10:8000',
|
'/app/api': 'http://10.0.0.10:8000',
|
||||||
},
|
},
|
||||||
host: '0.0.0.0',
|
host: '0.0.0.0',
|
||||||
port: 5173,
|
port: 5173,
|
||||||
|
|||||||
Reference in New Issue
Block a user