From bfadc22765b04d007aa850970a0229cf4b150343 Mon Sep 17 00:00:00 2001 From: Owen Lin <106612301+owenowenisme@users.noreply.github.com> Date: Tue, 4 Mar 2025 14:51:41 +0800 Subject: [PATCH] added user setting page, enable profile update (#31) Signed-off-by: owenowenisme --- frontend/app/layout.tsx | 2 + frontend/app/user/layout.tsx | 91 +++++++++++++++ frontend/app/user/page.tsx | 76 +++++++++++++ frontend/app/user/setting/page.tsx | 121 ++++++++++++++++++++ frontend/components/GlobalNav/index.tsx | 8 +- frontend/module/api/user.ts | 2 +- frontend/module/zustand/user/store.ts | 2 + frontend/next.config.js | 1 + frontend/package.json | 6 + frontend/pnpm-lock.yaml | 129 ++++++++++++++++++++++ frontend/tailwind.config.ts | 6 + frontend/types/user.ts | 1 + frontend/ui/{TextArea => Input}/index.tsx | 16 +-- frontend/ui/Label/index.tsx | 26 +++++ frontend/ui/Toast/index.tsx | 27 +++++ 15 files changed, 505 insertions(+), 9 deletions(-) create mode 100644 frontend/app/user/layout.tsx create mode 100644 frontend/app/user/page.tsx create mode 100644 frontend/app/user/setting/page.tsx rename frontend/ui/{TextArea => Input}/index.tsx (85%) create mode 100644 frontend/ui/Label/index.tsx create mode 100644 frontend/ui/Toast/index.tsx diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 9f094a4..b5d6254 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,5 +1,6 @@ import '@/styles/globals.css'; import { GlobalNav } from '@/components/GlobalNav'; +import { Toaster } from '@/ui/Toast'; export default function RootLayout({ children, }: { @@ -11,6 +12,7 @@ export default function RootLayout({
{children}
+
diff --git a/frontend/app/user/layout.tsx b/frontend/app/user/layout.tsx new file mode 100644 index 0000000..2a8cd72 --- /dev/null +++ b/frontend/app/user/layout.tsx @@ -0,0 +1,91 @@ +'use client'; +import { useAuthentication } from '@/hooks/useAuthentication'; +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +import Image from 'next/image'; +import { Button } from '@/ui/Button'; +import { Icon } from '@/ui/icons'; +export default function UserLayout({ + children, +}: { + children: React.ReactNode; +}) { + const { currentUser, handleRefreshProfile } = useAuthentication(); + const router = useRouter(); + + useEffect(() => { + if (!currentUser) { + handleRefreshProfile(); + } + }, []); + + if (!currentUser) { + return ( +
+

請先登入

+
+ ); + } + const UserBar = () => { + return ( +
+
+
+
+ Profile +
+
+
+
{currentUser?.userName}
+
+ + 科系{' '} + {currentUser?.department == null + ? '尚未設定' + : currentUser?.department} +
+
+
+
+
+ +
|
+ +
|
+ +
+
+
+ ); + }; + + return ( +
+ + {children} +
+ ); +} diff --git a/frontend/app/user/page.tsx b/frontend/app/user/page.tsx new file mode 100644 index 0000000..496102f --- /dev/null +++ b/frontend/app/user/page.tsx @@ -0,0 +1,76 @@ +'use client'; +import { useAuthentication } from '@/hooks/useAuthentication'; +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +import { Button } from '@/ui/Button'; +import Image from 'next/image'; + +export default function SettingPage() { + const { currentUser, handleRefreshProfile } = useAuthentication(); + const router = useRouter(); + + useEffect(() => { + if (!currentUser) { + handleRefreshProfile(); + } + }, []); + + if (!currentUser) { + return ( +
+

請先登入

+
+ ); + } + + return ( +
+

個人設定

+ +
+
+

個人資料

+
+
+ Profile +
+
+
+ +

{currentUser.userName}

+
+
+ +

{currentUser.email}

+
+
+
+
+ +
+

帳號設定

+
+ +
+
+
+
+ ); +} diff --git a/frontend/app/user/setting/page.tsx b/frontend/app/user/setting/page.tsx new file mode 100644 index 0000000..7a5c7b4 --- /dev/null +++ b/frontend/app/user/setting/page.tsx @@ -0,0 +1,121 @@ +'use client'; +import { useAuthentication } from '@/hooks/useAuthentication'; +import { Input } from '@/ui/Input'; +import { Label } from '@/ui/Label'; +import { Button } from '@/ui/Button'; +import Image from 'next/image'; +import { useState } from 'react'; +import { z } from 'zod'; +import { userAPI } from '@/module/api'; +import { toast } from 'sonner'; + +const userSchema = z.object({ + userName: z.string().min(1, '姓名不能為空').max(50, '姓名不能超過50個字'), + department: z.string().min(1, '系所不能為空').max(100, '系所不能超過100個字'), +}); + +export default function SettingPage() { + const { currentUser, handleRefreshProfile } = useAuthentication(); + const [userName, setUserName] = useState(currentUser?.userName || ''); + const [department, setDepartment] = useState(currentUser?.department || ''); + const [errors, setErrors] = useState<{ + userName?: string; + department?: string; + }>({}); + + const validateField = (field: 'userName' | 'department', value: string) => { + try { + userSchema.shape[field].parse(value); + setErrors((prev) => ({ ...prev, [field]: undefined })); + } catch (error) { + if (error instanceof z.ZodError) { + setErrors((prev) => ({ ...prev, [field]: error.errors[0].message })); + } + } + }; + + const handleSave = async () => { + const res = await userAPI.updateProfile({ + username: userName, + department: department, + }); + if (res.data.status === 'success') { + toast('修改成功'); + handleRefreshProfile(); + } else { + toast('修改失敗'); + } + }; + + return ( +
+
+
+
+ + { + setUserName(e.target.value); + validateField('userName', e.target.value); + }} + variant={errors.userName ? 'error' : 'default'} + className="w-full" + /> + {errors.userName && ( + {errors.userName} + )} +
+
+ + { + setDepartment(e.target.value); + validateField('department', e.target.value); + }} + variant={errors.department ? 'error' : 'default'} + className="w-full" + /> + {errors.department && ( + {errors.department} + )} +
+
+ + +
+
+
+
+
+ user + +
+
+
+
+ +
+ ); +} diff --git a/frontend/components/GlobalNav/index.tsx b/frontend/components/GlobalNav/index.tsx index 7ae50e5..962adcc 100644 --- a/frontend/components/GlobalNav/index.tsx +++ b/frontend/components/GlobalNav/index.tsx @@ -86,7 +86,13 @@ export const GlobalNav = () => {
- logo +
diff --git a/frontend/module/api/user.ts b/frontend/module/api/user.ts index 17950a4..4444b96 100644 --- a/frontend/module/api/user.ts +++ b/frontend/module/api/user.ts @@ -2,7 +2,7 @@ import { axiosInstance } from './axios'; interface UserUpdateData { username?: string; - email?: string; + department?: string; avatar?: string; } diff --git a/frontend/module/zustand/user/store.ts b/frontend/module/zustand/user/store.ts index ae2c851..8b8a282 100644 --- a/frontend/module/zustand/user/store.ts +++ b/frontend/module/zustand/user/store.ts @@ -34,6 +34,7 @@ async function login() { userName: profile.data.data.username, email: profile.data.data.email, avatar: profile.data.data.avatar, + department: profile.data.data.department, isProfileCompleted: profile.data.data.is_profile_completed, }; useUserStore.getState().setUser(userData); @@ -53,6 +54,7 @@ async function refreshProfile() { userName: profile.data.data.username, email: profile.data.data.email, avatar: profile.data.data.avatar, + department: profile.data.data.department, isProfileCompleted: profile.data.data.is_profile_completed, }; useUserStore.getState().setUser(userData); diff --git a/frontend/next.config.js b/frontend/next.config.js index 55f6572..0008853 100755 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -37,6 +37,7 @@ const nextConfig = { pathname: '/**', }, ], + domains: ['lh3.googleusercontent.com'], }, }; diff --git a/frontend/package.json b/frontend/package.json index d1ba4a0..80b0128 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,22 +25,28 @@ "@heroicons/react": "2.1.3", "@radix-ui/react-avatar": "1.1.1", "@radix-ui/react-dropdown-menu": "2.1.6", + "@radix-ui/react-label": "2.1.2", + "@radix-ui/react-toast": "1.2.6", "@svgr/webpack": "8.1.0", "axios": "1.7.9", + "class-variance-authority": "0.7.1", "clsx": "2.1.1", "date-fns": "3.6.0", "dinero.js": "2.0.0-alpha.10", "lucide-react": "0.475.0", "ms": "3.0.0-canary.1", "next": "14.3.0-canary.33", + "next-themes": "0.4.4", "react": "18.2.0", "react-dom": "18.2.0", "react-dropzone": "14.2.10", "server-only": "0.0.1", + "sonner": "2.0.1", "styled-components": "6.1.8", "tailwind-merge": "3.0.2", "use-count-up": "3.0.1", "vercel": "34.0.0", + "zod": "3.24.2", "zustand": "5.0.2" }, "devDependencies": { diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a989e1b..7479db6 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -17,12 +17,21 @@ importers: '@radix-ui/react-dropdown-menu': specifier: 2.1.6 version: 2.1.6(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-label': + specifier: 2.1.2 + version: 2.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toast': + specifier: 1.2.6 + version: 1.2.6(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@svgr/webpack': specifier: 8.1.0 version: 8.1.0(typescript@5.4.5) axios: specifier: 1.7.9 version: 1.7.9 + class-variance-authority: + specifier: 0.7.1 + version: 0.7.1 clsx: specifier: 2.1.1 version: 2.1.1 @@ -41,6 +50,9 @@ importers: next: specifier: 14.3.0-canary.33 version: 14.3.0-canary.33(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + next-themes: + specifier: 0.4.4 + version: 0.4.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react: specifier: 18.2.0 version: 18.2.0 @@ -53,6 +65,9 @@ importers: server-only: specifier: 0.0.1 version: 0.0.1 + sonner: + specifier: 2.0.1 + version: 2.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) styled-components: specifier: 6.1.8 version: 6.1.8(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -65,6 +80,9 @@ importers: vercel: specifier: 34.0.0 version: 34.0.0 + zod: + specifier: 3.24.2 + version: 3.24.2 zustand: specifier: 5.0.2 version: 5.0.2(@types/react@18.2.79)(react@18.2.0) @@ -1163,6 +1181,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-label@2.1.2': + resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-menu@2.1.6': resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} peerDependencies: @@ -1272,6 +1303,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-toast@1.2.6': + resolution: {integrity: sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-use-callback-ref@1.1.0': resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} peerDependencies: @@ -1326,6 +1370,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-visually-hidden@1.1.2': + resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} @@ -1883,6 +1940,9 @@ packages: cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-cursor@4.0.0: resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3195,6 +3255,12 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-themes@0.4.4: + resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + next@14.3.0-canary.33: resolution: {integrity: sha512-LkFdhhlZ7G86Yq89keeDbkI1WkXuj2Lphdau6FenuUNRhLrIbJg0NIDT2fgI68yi+d035YPIMQYPOL0OrBcevg==} engines: {node: '>=18.17.0'} @@ -3803,6 +3869,12 @@ packages: snake-case@3.0.4: resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==} + sonner@2.0.1: + resolution: {integrity: sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==} + peerDependencies: + react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -4238,6 +4310,9 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod@3.24.2: + resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zustand@5.0.2: resolution: {integrity: sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw==} engines: {node: '>=12.20.0'} @@ -5378,6 +5453,15 @@ snapshots: optionalDependencies: '@types/react': 18.2.79 + '@radix-ui/react-label@2.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.79 + '@types/react-dom': 18.2.25 + '@radix-ui/react-menu@2.1.6(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': dependencies: '@radix-ui/primitive': 1.1.1 @@ -5491,6 +5575,26 @@ snapshots: optionalDependencies: '@types/react': 18.2.79 + '@radix-ui/react-toast@1.2.6(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.79)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.79)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.79)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.79)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.79)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.79 + '@types/react-dom': 18.2.25 + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.2.79)(react@18.2.0)': dependencies: react: 18.2.0 @@ -5531,6 +5635,15 @@ snapshots: optionalDependencies: '@types/react': 18.2.79 + '@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.25)(@types/react@18.2.79)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.79 + '@types/react-dom': 18.2.25 + '@radix-ui/rect@1.1.0': {} '@rollup/pluginutils@4.2.1': @@ -6240,6 +6353,10 @@ snapshots: cjs-module-lexer@1.2.3: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-cursor@4.0.0: dependencies: restore-cursor: 4.0.0 @@ -7614,6 +7731,11 @@ snapshots: natural-compare@1.4.0: {} + next-themes@0.4.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + next@14.3.0-canary.33(@babel/core@7.26.9)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): dependencies: '@next/env': 14.3.0-canary.33 @@ -8183,6 +8305,11 @@ snapshots: dot-case: 3.0.4 tslib: 2.5.0 + sonner@2.0.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + source-map-js@1.2.0: {} stat-mode@0.3.0: {} @@ -8717,6 +8844,8 @@ snapshots: yocto-queue@0.1.0: {} + zod@3.24.2: {} + zustand@5.0.2(@types/react@18.2.79)(react@18.2.0): optionalDependencies: '@types/react': 18.2.79 diff --git a/frontend/tailwind.config.ts b/frontend/tailwind.config.ts index 3115316..e4745a4 100644 --- a/frontend/tailwind.config.ts +++ b/frontend/tailwind.config.ts @@ -134,6 +134,12 @@ export default { 'text-underline-position': 'from-font', 'text-decoration-skip-ink': 'none', }, + '.text-large-content': { + '@apply font-noto-inter font-normal text-[18px] leading-[32px] text-left': + {}, + 'text-underline-position': 'from-font', + 'text-decoration-skip-ink': 'none', + }, // Paragraph - Main content text '.text-p': { '@apply font-noto-inter font-normal text-[16px] leading-[32px] text-left': diff --git a/frontend/types/user.ts b/frontend/types/user.ts index f8f1ff2..bf8546d 100644 --- a/frontend/types/user.ts +++ b/frontend/types/user.ts @@ -3,6 +3,7 @@ export interface User { avatar: string; email: string; isProfileCompleted: boolean; + department: string; } export default User; diff --git a/frontend/ui/TextArea/index.tsx b/frontend/ui/Input/index.tsx similarity index 85% rename from frontend/ui/TextArea/index.tsx rename to frontend/ui/Input/index.tsx index 0e1a094..cd2e7ff 100644 --- a/frontend/ui/TextArea/index.tsx +++ b/frontend/ui/Input/index.tsx @@ -1,26 +1,27 @@ import { cn } from '@/lib/utils'; import React, { ChangeEvent } from 'react'; -interface TextAreaProps { +interface InputProps { // The current text value of the textarea value: string; - onChange: (e: ChangeEvent) => void; + onChange?: (e: ChangeEvent) => void; className?: string; placeholder?: string; - variant?: 'default' | 'error'; disabled?: boolean; + variant?: 'default' | 'error'; } -export const TextArea = ({ +export const Input = ({ value, onChange, placeholder = 'Type your message here', variant = 'default', disabled = false, -}: TextAreaProps) => { + className, +}: InputProps) => { return ( // Renders a