diff --git a/app/(sub-page)/my/page.tsx b/app/(sub-page)/my/page.tsx index f4075c0c..a1bf7f11 100644 --- a/app/(sub-page)/my/page.tsx +++ b/app/(sub-page)/my/page.tsx @@ -1,14 +1,20 @@ import LectureSearch from '@/app/ui/lecture/lecture-search'; import TakenLecture from '@/app/ui/lecture/taken-lecture'; +import UserInfoNavigator, { UserInfoNavigatorSkeleton } from '@/app/ui/user/user-info-navigator/user-info-navigator'; import ContentContainer from '@/app/ui/view/atom/content-container'; import Drawer from '@/app/ui/view/molecule/drawer/drawer'; import { DIALOG_KEY } from '@/app/utils/key/dialog.key'; +import { Suspense } from 'react'; export default function MyPage() { return ( <> -
정보칸
+
+ }> + + +
diff --git a/app/mocks/handlers/user-handler.mock.ts b/app/mocks/handlers/user-handler.mock.ts index bde7ac3a..af26d9d7 100644 --- a/app/mocks/handlers/user-handler.mock.ts +++ b/app/mocks/handlers/user-handler.mock.ts @@ -33,12 +33,12 @@ export const userHandlers = [ }), http.get(`${API_PATH.user}`, async ({ request }) => { const accessToken = request.headers.get('Authorization')?.replace('Bearer ', ''); - if (!accessToken) { + if (accessToken === 'undefined' || !accessToken) { return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 }); } const userInfo = mockDatabase.getUserInfo(mockDecryptToken(accessToken).authId); - await delay(500); + await delay(3000); if (!userInfo) { return HttpResponse.json({ status: 401, message: 'Unauthorized' }, { status: 401 }); diff --git a/app/ui/user/user-info-navigator/user-info-navigator.tsx b/app/ui/user/user-info-navigator/user-info-navigator.tsx new file mode 100644 index 00000000..e975e6bc --- /dev/null +++ b/app/ui/user/user-info-navigator/user-info-navigator.tsx @@ -0,0 +1,41 @@ +import Avatar from '../../view/atom/avatar/avatar'; +import Button from '../../view/atom/button/button'; +import { getUserInfo } from '@/app/business/user/user.query'; +import Skeleton from '../../view/atom/skeleton'; + +export default async function UserInfoNavigator() { + const userInfo = await getUserInfo(); + + return ( +
+ + +
+ {userInfo.studentName} + +
+
{userInfo.major}
+
{userInfo.studentNumber}
+ +
+
+
+
+
+ ); +} + +export function UserInfoNavigatorSkeleton() { + return ( +
+ +
+ + + +
+
+ ); +} diff --git a/app/ui/view/atom/avatar/avatar.stories.tsx b/app/ui/view/atom/avatar/avatar.stories.tsx new file mode 100644 index 00000000..e548910e --- /dev/null +++ b/app/ui/view/atom/avatar/avatar.stories.tsx @@ -0,0 +1,17 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import Avatar from './avatar'; + +const meta = { + title: 'ui/view/atom/Avatar', + component: Avatar, +} as Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + src: './assets/profile-image.png', + }, +}; diff --git a/app/ui/view/atom/avatar/avatar.tsx b/app/ui/view/atom/avatar/avatar.tsx new file mode 100644 index 00000000..5ab47206 --- /dev/null +++ b/app/ui/view/atom/avatar/avatar.tsx @@ -0,0 +1,57 @@ +'use client'; + +import * as React from 'react'; +import * as AvatarPrimitive from '@radix-ui/react-avatar'; + +import { cn } from '@/app/utils/shadcn/utils'; + +interface AvatarProps { + src: string; + fallback?: React.ReactNode; + alt?: string; + className?: string; +} + +export default function Avatar({ src, fallback, alt, className }: AvatarProps) { + return ( + + + {fallback} + + ); +} + +const AvatarRoot = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarRoot.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; diff --git a/app/ui/view/atom/skeleton.tsx b/app/ui/view/atom/skeleton.tsx new file mode 100644 index 00000000..4cfa96e4 --- /dev/null +++ b/app/ui/view/atom/skeleton.tsx @@ -0,0 +1,5 @@ +import { cn } from '@/app/utils/shadcn/utils'; + +export default function Skeleton({ className, ...props }: React.HTMLAttributes) { + return
; +} diff --git a/package-lock.json b/package-lock.json index 9f39b401..3ace1a6b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@heroicons/react": "^2.1.1", "@mswjs/http-middleware": "^0.9.2", "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-slot": "^1.0.2", @@ -3926,6 +3927,32 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz", + "integrity": "sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-callback-ref": "1.0.1", + "@radix-ui/react-use-layout-effect": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", diff --git a/package.json b/package.json index 5973ac8f..fee2e12a 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@heroicons/react": "^2.1.1", "@mswjs/http-middleware": "^0.9.2", "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-slot": "^1.0.2", diff --git a/public/assets/profile-image.png b/public/assets/profile-image.png new file mode 100644 index 00000000..c53f181c Binary files /dev/null and b/public/assets/profile-image.png differ