diff --git a/client/cms/.storybook/preview.tsx b/client/cms/.storybook/preview.tsx
index ab9f5f5..b9c2ec1 100644
--- a/client/cms/.storybook/preview.tsx
+++ b/client/cms/.storybook/preview.tsx
@@ -1,9 +1,21 @@
-import type { Preview } from '@storybook/react-vite';
+import type { Decorator, Preview } from '@storybook/react-vite';
import { ThemeProvider } from '../src/components/theme-provider';
import '../src/index.css';
+import { createRootRoute, createRouter, RouterProvider } from '@tanstack/react-router';
+
+const RouterDecorator: Decorator = (Story) => {
+ const rootRoute = createRootRoute({ component: () => });
+ const routeTree = rootRoute;
+ const router = createRouter({ routeTree });
+ return ;
+};
+
+const ThemeDecorator: Decorator = (Story) => {
+ return ;
+};
const preview: Preview = {
- decorators: [(Story) => ],
+ decorators: [RouterDecorator, ThemeDecorator],
parameters: {
controls: {
matchers: {
diff --git a/client/cms/src/components/hoc/with-fallback.tsx b/client/cms/src/components/hoc/with-fallback.tsx
deleted file mode 100644
index 5d4c0e6..0000000
--- a/client/cms/src/components/hoc/with-fallback.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import type { ReactNode } from 'react';
-import React, { Suspense } from 'react';
-
-export function withFallback
(
- Component: React.ComponentType
,
- fallback: ReactNode,
-) {
- const Wrapped: React.FC
= (props) => {
- return (
-
-
-
- );
- };
-
- Wrapped.displayName = `withFallback(${Component.displayName! || Component.name || 'Component'
- })`;
-
- return Wrapped;
-}
diff --git a/client/cms/src/components/sidebar/app-sidebar.tsx b/client/cms/src/components/sidebar/app-sidebar.view.tsx
similarity index 81%
rename from client/cms/src/components/sidebar/app-sidebar.tsx
rename to client/cms/src/components/sidebar/app-sidebar.view.tsx
index 5b971d5..f6f1017 100644
--- a/client/cms/src/components/sidebar/app-sidebar.tsx
+++ b/client/cms/src/components/sidebar/app-sidebar.view.tsx
@@ -1,8 +1,8 @@
import type { NavData } from '@/lib/navData';
import * as React from 'react';
import NixOSLogo from '@/assets/nixos.svg?react';
-import { NavMain } from '@/components/sidebar/nav-main';
-import { NavSecondary } from '@/components/sidebar/nav-secondary';
+import { NavMain } from '@/components/sidebar/nav-main.view';
+import { NavSecondary } from '@/components/sidebar/nav-secondary.view';
import {
Sidebar,
SidebarContent,
@@ -12,9 +12,8 @@ import {
SidebarMenuButton,
SidebarMenuItem,
} from '@/components/ui/sidebar';
-import { NavUser } from './nav-user';
-export function AppSidebar({ navData, ...props }: React.ComponentProps & { navData: NavData }) {
+export function AppSidebar({ navData, footerWidget, ...props }: React.ComponentProps & { navData: NavData; footerWidget: React.ReactNode }) {
return (
@@ -37,7 +36,7 @@ export function AppSidebar({ navData, ...props }: React.ComponentProps
-
+ {footerWidget}
);
diff --git a/client/cms/src/components/sidebar/nav-main.tsx b/client/cms/src/components/sidebar/nav-main.view.tsx
similarity index 100%
rename from client/cms/src/components/sidebar/nav-main.tsx
rename to client/cms/src/components/sidebar/nav-main.view.tsx
diff --git a/client/cms/src/components/sidebar/nav-secondary.tsx b/client/cms/src/components/sidebar/nav-secondary.view.tsx
similarity index 100%
rename from client/cms/src/components/sidebar/nav-secondary.tsx
rename to client/cms/src/components/sidebar/nav-secondary.view.tsx
diff --git a/client/cms/src/components/sidebar/nav-user.container.tsx b/client/cms/src/components/sidebar/nav-user.container.tsx
new file mode 100644
index 0000000..3e65113
--- /dev/null
+++ b/client/cms/src/components/sidebar/nav-user.container.tsx
@@ -0,0 +1,11 @@
+import { useUserInfo } from '@/hooks/data/useUserInfo';
+import { NavUserView } from './nav-user.view';
+
+export function NavUserContainer() {
+ const { data } = useUserInfo();
+ return (
+
+ );
+}
diff --git a/client/cms/src/components/sidebar/nav-user.skeletion.tsx b/client/cms/src/components/sidebar/nav-user.skeletion.tsx
new file mode 100644
index 0000000..de9d3c7
--- /dev/null
+++ b/client/cms/src/components/sidebar/nav-user.skeletion.tsx
@@ -0,0 +1,18 @@
+import { IconDotsVertical } from '@tabler/icons-react';
+import { SidebarMenuButton } from '../ui/sidebar';
+import { Skeleton } from '../ui/skeleton';
+
+export function NavUserSkeleton() {
+ return (
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/client/cms/src/components/sidebar/nav-user.tsx b/client/cms/src/components/sidebar/nav-user.view.tsx
similarity index 81%
rename from client/cms/src/components/sidebar/nav-user.tsx
rename to client/cms/src/components/sidebar/nav-user.view.tsx
index b60a467..6ba97ff 100644
--- a/client/cms/src/components/sidebar/nav-user.tsx
+++ b/client/cms/src/components/sidebar/nav-user.view.tsx
@@ -1,5 +1,6 @@
-import { identicon } from '@dicebear/collection';
+import type { ServiceUserUserInfoData } from '@/client';
+import { identicon } from '@dicebear/collection';
import { createAvatar } from '@dicebear/core';
import {
IconDotsVertical,
@@ -25,15 +26,10 @@ import {
SidebarMenuItem,
useSidebar,
} from '@/components/ui/sidebar';
-import { useUserInfo } from '@/hooks/data/useUserInfo';
import { logout } from '@/lib/token';
-import { withFallback } from '../hoc/with-fallback';
-import { Skeleton } from '../ui/skeleton';
-export function NavUser_() {
+export function NavUserView({ user }: { user: ServiceUserUserInfoData }) {
const { isMobile } = useSidebar();
- const { data } = useUserInfo();
- const user = data.data!;
const IdentIcon = useMemo(() => {
const avatar = createAvatar(identicon, {
@@ -94,20 +90,3 @@ export function NavUser_() {
);
}
-
-function NavUserSkeleton() {
- return (
-
-
-
-
-
-
-
-
- );
-}
-
-export const NavUser = withFallback(NavUser_, );
diff --git a/client/cms/src/routes/_workbenchLayout.tsx b/client/cms/src/routes/_workbenchLayout.tsx
index 4bf19ef..65a366f 100644
--- a/client/cms/src/routes/_workbenchLayout.tsx
+++ b/client/cms/src/routes/_workbenchLayout.tsx
@@ -1,5 +1,8 @@
import { createFileRoute, Outlet, useRouterState } from '@tanstack/react-router';
-import { AppSidebar } from '@/components/sidebar/app-sidebar';
+import { Suspense } from 'react';
+import { AppSidebar } from '@/components/sidebar/app-sidebar.view';
+import { NavUserContainer } from '@/components/sidebar/nav-user.container';
+import { NavUserSkeleton } from '@/components/sidebar/nav-user.skeletion';
import { SiteHeader } from '@/components/site-header';
import { SidebarInset, SidebarProvider } from '@/components/ui/sidebar';
import { navData } from '@/lib/navData';
@@ -27,7 +30,15 @@ function RouteComponent() {
} as React.CSSProperties
}
>
-
+ }>
+
+
+ )}
+ variant="inset"
+ />
diff --git a/client/cms/src/stories/exampleUser.ts b/client/cms/src/stories/exampleUser.ts
new file mode 100644
index 0000000..40800ff
--- /dev/null
+++ b/client/cms/src/stories/exampleUser.ts
@@ -0,0 +1,8 @@
+export const user = {
+ username: 'nvirellia',
+ nickname: 'Noa Virellia',
+ subtitle: '天生骄傲',
+ email: 'noa@requiem.garden',
+ bio: '',
+ avatar: 'https://avatars.githubusercontent.com/u/54884471?v=4',
+};
diff --git a/client/cms/src/stories/nav-user.stories.tsx b/client/cms/src/stories/nav-user.stories.tsx
deleted file mode 100644
index 679366a..0000000
--- a/client/cms/src/stories/nav-user.stories.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { NavUser_ } from '@/components/sidebar/nav-user';
-
-const meta = {
- title: 'NavUser',
- component: NavUser_,
-} satisfies Meta
;
-
-export default meta;
-type Story = StoryObj;
-
-export const Primary: Story = {};
diff --git a/client/cms/src/stories/profile/edit-profile.dialog.stories.tsx b/client/cms/src/stories/profile/edit-profile.dialog.stories.tsx
index d3dcebf..e67e929 100644
--- a/client/cms/src/stories/profile/edit-profile.dialog.stories.tsx
+++ b/client/cms/src/stories/profile/edit-profile.dialog.stories.tsx
@@ -1,5 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { EditProfileDialogView } from '@/components/profile/edit-profile.dialog.view';
+import { user } from '../exampleUser';
const meta = {
title: 'Profile/EditDialog',
@@ -27,14 +28,7 @@ type Story = StoryObj;
export const Primary: Story = {
args: {
- user: {
- username: 'nvirellia',
- nickname: 'Noa Virellia',
- subtitle: '天生骄傲',
- email: 'noa@requiem.garden',
- bio: '',
- avatar: 'https://avatars.githubusercontent.com/u/54884471?v=4',
- },
+ user,
updateProfile: async () => { },
},
parameters: {
diff --git a/client/cms/src/stories/profile/profile.stories.tsx b/client/cms/src/stories/profile/profile.stories.tsx
index 8f84d18..7590c49 100644
--- a/client/cms/src/stories/profile/profile.stories.tsx
+++ b/client/cms/src/stories/profile/profile.stories.tsx
@@ -3,6 +3,7 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ProfileError } from '@/components/profile/profile.error';
import { ProfileSkeleton } from '@/components/profile/profile.skeleton';
import { ProfileView } from '@/components/profile/profile.view';
+import { user } from '../exampleUser';
const queryClient = new QueryClient();
@@ -23,20 +24,12 @@ type Story = StoryObj;
export const Primary: Story = {
args: {
- user: {
- username: 'nvirellia',
- nickname: 'Noa Virellia',
- subtitle: '天生骄傲',
- email: 'noa@requiem.garden',
- bio: '',
- avatar: 'https://avatars.githubusercontent.com/u/54884471?v=4',
- },
+ user,
onSaveBio: async () => Promise.resolve(),
},
-
};
-export const Skeleton: Story = {
+export const Loading: Story = {
render: () => ,
args: {
user: {},
diff --git a/client/cms/src/stories/sidebar.stories.tsx b/client/cms/src/stories/sidebar.stories.tsx
new file mode 100644
index 0000000..06dfae0
--- /dev/null
+++ b/client/cms/src/stories/sidebar.stories.tsx
@@ -0,0 +1,43 @@
+import type { Meta, StoryObj } from '@storybook/react-vite';
+import { AppSidebar } from '@/components/sidebar/app-sidebar.view';
+import { NavUserSkeleton } from '@/components/sidebar/nav-user.skeletion';
+import { NavUserView } from '@/components/sidebar/nav-user.view';
+import { SidebarProvider } from '@/components/ui/sidebar';
+import { navData } from '@/lib/navData';
+import { user } from './exampleUser';
+
+const meta = {
+ title: 'Navigation/Sidebar',
+ component: AppSidebar,
+ decorators: [
+ Story => (
+
+
+
+ ),
+ ],
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+export const Primary: Story = {
+ args: {
+ navData,
+ footerWidget: ,
+ },
+};
+
+export const Loading: Story = {
+ args: {
+ navData,
+ footerWidget: ,
+ },
+};