diff --git a/src/app/[locale]/admin/users/UserForm.tsx b/src/app/[locale]/admin/users/UserForm.tsx index 5e7ab78..9876410 100644 --- a/src/app/[locale]/admin/users/UserForm.tsx +++ b/src/app/[locale]/admin/users/UserForm.tsx @@ -18,7 +18,8 @@ import { useI18n, useCurrentLocale } from '@/locales/client'; import { registerUser, updateUser } from '@/app/actions/user'; const formSchema = z.object({ - name: z.string().min(2, "Name must be at least 2 characters."), + name: z.string().min(2, "Full name must be at least 2 characters."), + nickname: z.string().optional(), email: z.string().email("Invalid email address."), role: z.enum(['User', 'Admin']), }); @@ -40,6 +41,7 @@ export default function UserForm({ initialData, isEditMode = false }: UserFormPr resolver: zodResolver(formSchema), defaultValues: { name: initialData?.name || '', + nickname: initialData?.nickname || '', email: initialData?.email || '', role: initialData?.role || 'User', }, @@ -85,7 +87,20 @@ export default function UserForm({ initialData, isEditMode = false }: UserFormPr name="name" render={({ field }) => ( - {t('admin.users.form_name_label')} + {t('admin.users.form_full_name_label')} + + + + + + )} + /> + ( + + {t('admin.users.form_nickname_label')} diff --git a/src/app/[locale]/admin/users/page.tsx b/src/app/[locale]/admin/users/page.tsx index 3ff938a..5fa3b60 100644 --- a/src/app/[locale]/admin/users/page.tsx +++ b/src/app/[locale]/admin/users/page.tsx @@ -102,7 +102,8 @@ export default function AdminUserManagementPage() { - {t('admin.users.table_header_name')} + {t('admin.users.table_header_full_name')} + {t('admin.users.table_header_nickname')} {t('admin.users.table_header_email')} {t('admin.users.table_header_role')} {t('admin.users.table_header_actions')} @@ -112,6 +113,7 @@ export default function AdminUserManagementPage() { {users.map((user) => ( {user.name} + {user.nickname} {user.email} {user.role} diff --git a/src/app/[locale]/dashboard/profile/page.tsx b/src/app/[locale]/dashboard/profile/page.tsx index 515dd95..39968ec 100644 --- a/src/app/[locale]/dashboard/profile/page.tsx +++ b/src/app/[locale]/dashboard/profile/page.tsx @@ -15,6 +15,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ const mockUserProfile = { name: 'Alice Wonderland', + nickname: 'Alice', email: 'alice@example.com', avatarUrl: 'https://placehold.co/100x100.png?text=AW', bio: "Lover of imaginative play and sharing joy. I have a collection of classic storybooks and dress-up costumes.", @@ -29,6 +30,7 @@ export default function ProfilePage() { const changeLocale = useChangeLocale(); const [name, setName] = useState(mockUserProfile.name); + const [nickname, setNickname] = useState(mockUserProfile.nickname); const [email, setEmail] = useState(mockUserProfile.email); const [avatarUrl, setAvatarUrl] = useState(mockUserProfile.avatarUrl); const [bio, setBio] = useState(mockUserProfile.bio); @@ -52,7 +54,7 @@ export default function ProfilePage() { e.preventDefault(); setIsLoading(true); await new Promise(resolve => setTimeout(resolve, 1000)); - console.log("Updating profile:", { name, avatarUrl, bio, phone, location }); + console.log("Updating profile:", { name, nickname, avatarUrl, bio, phone, location }); toast({ title: t('dashboard.profile.save_button'), description: "Your profile information has been saved." }); setIsLoading(false); }; @@ -117,14 +119,19 @@ export default function ProfilePage() { {t('dashboard.profile.full_name_label')} - setName(e.target.value)} placeholder="Your Nickname" disabled={isLoading} /> + setName(e.target.value)} placeholder="Your Full Name" required disabled={isLoading} /> - {t('dashboard.profile.email_label')} - + {t('dashboard.profile.nickname_label')} + setNickname(e.target.value)} placeholder="Your Nickname" disabled={isLoading} /> + + {t('dashboard.profile.email_label')} + + + {t('dashboard.profile.bio_label')} setBio(e.target.value)} placeholder="Tell us a bit about yourself and your toys..." rows={3} disabled={isLoading}/> diff --git a/src/app/[locale]/register/page.tsx b/src/app/[locale]/register/page.tsx index 75e0ada..85651fd 100644 --- a/src/app/[locale]/register/page.tsx +++ b/src/app/[locale]/register/page.tsx @@ -20,6 +20,7 @@ export default function RegisterPage() { const locale = useCurrentLocale(); const [name, setName] = useState(''); + const [nickname, setNickname] = useState(''); const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); @@ -39,7 +40,7 @@ export default function RegisterPage() { return; } - const result = await registerUser({ name, email }); + const result = await registerUser({ name, nickname, email }); if (result.success && result.user) { // Mock login after successful registration @@ -85,16 +86,27 @@ export default function RegisterPage() { - {t('register.name_label')} + {t('register.full_name_label')} setName(e.target.value)} required disabled={isLoading} /> + + + {t('register.nickname_label')} + setNickname(e.target.value)} + disabled={isLoading} + /> {t('login.email_label')} diff --git a/src/app/actions/user.ts b/src/app/actions/user.ts index 035d628..b52d5c3 100644 --- a/src/app/actions/user.ts +++ b/src/app/actions/user.ts @@ -13,11 +13,11 @@ interface RegisterUserResult { user?: User; } -export async function registerUser(data: { name: string; email: string; password?: string, role?: 'Admin' | 'User' }): Promise { - const { name, email, role = 'User' } = data; +export async function registerUser(data: { name: string; nickname?: string; email: string; password?: string, role?: 'Admin' | 'User' }): Promise { + const { name, nickname, email, role = 'User' } = data; if (!name || !email) { - return { success: false, message: 'Name and email are required.' }; + return { success: false, message: 'Full name and email are required.' }; } try { @@ -29,6 +29,7 @@ export async function registerUser(data: { name: string; email: string; password const newUser: User = { id: `user-${randomUUID()}`, name, + nickname, email, role: role, avatarUrl: '', @@ -36,7 +37,7 @@ export async function registerUser(data: { name: string; email: string; password }; const stmt = db.prepare( - 'INSERT INTO users (id, name, email, role, avatarUrl, bio) VALUES (@id, @name, @email, @role, @avatarUrl, @bio)' + 'INSERT INTO users (id, name, nickname, email, role, avatarUrl, bio) VALUES (@id, @name, @nickname, @email, @role, @avatarUrl, @bio)' ); stmt.run(newUser); @@ -67,12 +68,13 @@ export async function updateUser(user: User): Promise { } const stmt = db.prepare( - 'UPDATE users SET name = @name, email = @email, role = @role, avatarUrl = @avatarUrl, bio = @bio WHERE id = @id' + 'UPDATE users SET name = @name, nickname = @nickname, email = @email, role = @role, avatarUrl = @avatarUrl, bio = @bio WHERE id = @id' ); stmt.run({ id: user.id, name: user.name, + nickname: user.nickname ?? null, email: user.email, role: user.role, avatarUrl: user.avatarUrl ?? '', diff --git a/src/data/operations.ts b/src/data/operations.ts index 2a2c2c9..37e6384 100644 --- a/src/data/operations.ts +++ b/src/data/operations.ts @@ -57,7 +57,7 @@ export function getOwnerProfile(ownerId: string) { // --- USER OPERATIONS --- export function getAllUsers(): User[] { - const stmt = db.prepare('SELECT id, name, email, role, avatarUrl, bio FROM users'); + const stmt = db.prepare('SELECT id, name, nickname, email, role, avatarUrl, bio FROM users'); return stmt.all() as User[]; } diff --git a/src/lib/db.ts b/src/lib/db.ts index 388769c..2d28d87 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -22,6 +22,7 @@ function initDb() { CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, name TEXT NOT NULL, + nickname TEXT, email TEXT NOT NULL UNIQUE, role TEXT, avatarUrl TEXT, @@ -50,8 +51,8 @@ function initDb() { // Use INSERT OR IGNORE to only add data if the primary key doesn't exist. // This prevents errors on subsequent runs and adds missing users/toys without overwriting. const insertUser = db.prepare(` - INSERT OR IGNORE INTO users (id, name, email, role, avatarUrl, bio) - VALUES (@id, @name, @email, @role, @avatarUrl, @bio) + INSERT OR IGNORE INTO users (id, name, nickname, email, role, avatarUrl, bio) + VALUES (@id, @name, @nickname, @email, @role, @avatarUrl, @bio) `); const insertToy = db.prepare(` @@ -64,6 +65,7 @@ function initDb() { insertUser.run({ id: user.id, name: user.name, + nickname: user.nickname ?? null, email: user.email, role: user.role ?? 'User', avatarUrl: user.avatarUrl ?? null, diff --git a/src/lib/mockData.ts b/src/lib/mockData.ts index 942cf2f..4c662b0 100644 --- a/src/lib/mockData.ts +++ b/src/lib/mockData.ts @@ -5,13 +5,13 @@ import { addDays, formatISO, subDays } from 'date-fns'; const today = new Date(); export const rawUsers: User[] = [ - { id: 'user1', name: 'Alice Wonderland', email: 'user@example.com', role: 'Admin', avatarUrl: 'https://placehold.co/100x100.png?text=AW', bio: "Lover of imaginative play and sharing joy. I have a collection of classic storybooks and dress-up costumes that my kids have outgrown but still have lots of life left in them!" }, - { id: 'user2', name: 'Bob The Builder', email: 'user2@example.com', role: 'User', avatarUrl: 'https://placehold.co/100x100.png?text=BT', bio: "Can we fix it? Yes, we can! Sharing my collection of construction toys, tools, and playsets. Always happy to help another budding builder." }, - { id: 'user3', name: 'Carol Danvers', email: 'user3@example.com', role: 'User', avatarUrl: 'https://placehold.co/100x100.png?text=CD', bio: "Higher, further, faster. Sharing toys that inspire adventure, courage, and exploration. My collection includes superhero action figures and space-themed playsets." }, - { id: 'user4', name: 'Charlie Brown', email: 'user4@example.com', role: 'User' }, - { id: 'user5', name: 'Diana Prince', email: 'user5@example.com', role: 'User' }, - { id: 'user6', name: 'Edward Nigma', email: 'user6@example.com', role: 'User' }, - { id: 'admin-main', name: 'Main Admin', email: 'admin@example.com', role: 'Admin' }, + { id: 'user1', name: 'Alice Wonderland', nickname: 'Alice', email: 'user@example.com', role: 'Admin', avatarUrl: 'https://placehold.co/100x100.png?text=AW', bio: "Lover of imaginative play and sharing joy. I have a collection of classic storybooks and dress-up costumes that my kids have outgrown but still have lots of life left in them!" }, + { id: 'user2', name: 'Bob The Builder', nickname: 'Bob', email: 'user2@example.com', role: 'User', avatarUrl: 'https://placehold.co/100x100.png?text=BT', bio: "Can we fix it? Yes, we can! Sharing my collection of construction toys, tools, and playsets. Always happy to help another budding builder." }, + { id: 'user3', name: 'Carol Danvers', nickname: 'Captain Marvel', email: 'user3@example.com', role: 'User', avatarUrl: 'https://placehold.co/100x100.png?text=CD', bio: "Higher, further, faster. Sharing toys that inspire adventure, courage, and exploration. My collection includes superhero action figures and space-themed playsets." }, + { id: 'user4', name: 'Charlie Brown', nickname: 'Chuck', email: 'user4@example.com', role: 'User' }, + { id: 'user5', name: 'Diana Prince', nickname: 'Wonder Woman', email: 'user5@example.com', role: 'User' }, + { id: 'user6', name: 'Edward Nigma', nickname: 'Riddler', email: 'user6@example.com', role: 'User' }, + { id: 'admin-main', name: 'Main Admin', nickname: 'Head Honcho', email: 'admin@example.com', role: 'Admin' }, ]; export const rawToys: Omit[] = [ diff --git a/src/locales/en.ts b/src/locales/en.ts index ad76f3c..331d7a4 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -31,7 +31,8 @@ export default { 'login.invalid_credentials_toast_user': 'Invalid email or password. (Hint: user@example.com / password or admin@example.com / passwordadmin)', 'register.create_account': 'Create Your Account', 'register.description': 'Join ToyShare and start sharing the fun!', - 'register.name_label': 'Nickname', + 'register.full_name_label': 'Full Name', + 'register.nickname_label': 'Nickname (Optional)', 'register.confirm_password_label': 'Confirm Password', 'register.submit_button': 'Create Account', 'register.loading_button': 'Registering...', @@ -96,7 +97,8 @@ export default { 'dashboard.profile.personal_info_title': 'Personal Information', 'dashboard.profile.personal_info_description': 'Update your publicly visible profile information.', 'dashboard.profile.avatar_url_label': 'Avatar URL', - 'dashboard.profile.full_name_label': 'Nickname', + 'dashboard.profile.full_name_label': 'Full Name', + 'dashboard.profile.nickname_label': 'Nickname', 'dashboard.profile.email_label': 'Email Address (Read-only)', 'dashboard.profile.bio_label': 'Bio', 'dashboard.profile.phone_label': 'Phone Number', @@ -230,7 +232,8 @@ export default { 'admin.users.description': 'View and manage user accounts and permissions.', 'admin.users.add_user_button': 'Add New User', 'admin.users.back_to_users_button': 'Back to User List', - 'admin.users.table_header_name': 'Nickname', + 'admin.users.table_header_full_name': 'Full Name', + 'admin.users.table_header_nickname': 'Nickname', 'admin.users.table_header_email': 'Email', 'admin.users.table_header_role': 'Role', 'admin.users.table_header_actions': 'Actions', @@ -241,7 +244,8 @@ export default { 'admin.users.add_user_description': 'Create a new user account and assign a role.', 'admin.users.edit_user_title': 'Edit User', 'admin.users.edit_user_description': "Update the user's details and role.", - 'admin.users.form_name_label': 'Nickname', + 'admin.users.form_full_name_label': 'Full Name', + 'admin.users.form_nickname_label': 'Nickname (Optional)', 'admin.users.form_email_label': 'Email Address', 'admin.users.form_role_label': 'Role', 'admin.users.form_role_user': 'User', diff --git a/src/locales/zh-TW.ts b/src/locales/zh-TW.ts index ca565e8..4d85716 100644 --- a/src/locales/zh-TW.ts +++ b/src/locales/zh-TW.ts @@ -31,7 +31,8 @@ export default { 'login.invalid_credentials_toast_user': '無效的電子郵件或密碼。(提示: user@example.com / password 或 admin@example.com / passwordadmin)', 'register.create_account': '建立您的帳戶', 'register.description': '加入 ToyShare,開始分享樂趣!', - 'register.name_label': '暱稱', + 'register.full_name_label': '全名', + 'register.nickname_label': '暱稱 (選填)', 'register.confirm_password_label': '確認密碼', 'register.submit_button': '建立帳戶', 'register.loading_button': '註冊中...', @@ -96,7 +97,8 @@ export default { 'dashboard.profile.personal_info_title': '個人資訊', 'dashboard.profile.personal_info_description': '更新您公開顯示的個人資料資訊。', 'dashboard.profile.avatar_url_label': '頭像 URL', - 'dashboard.profile.full_name_label': '暱稱', + 'dashboard.profile.full_name_label': '全名', + 'dashboard.profile.nickname_label': '暱稱', 'dashboard.profile.email_label': '電子郵件地址 (唯讀)', 'dashboard.profile.bio_label': '簡介', 'dashboard.profile.phone_label': '電話號碼', @@ -230,7 +232,8 @@ export default { 'admin.users.description': '查看和管理使用者帳戶及權限。', 'admin.users.add_user_button': '新增使用者', 'admin.users.back_to_users_button': '返回使用者列表', - 'admin.users.table_header_name': '暱稱', + 'admin.users.table_header_full_name': '全名', + 'admin.users.table_header_nickname': '暱稱', 'admin.users.table_header_email': '電子郵件', 'admin.users.table_header_role': '角色', 'admin.users.table_header_actions': '操作', @@ -241,7 +244,8 @@ export default { 'admin.users.add_user_description': '建立一個新的使用者帳戶並分配角色。', 'admin.users.edit_user_title': '編輯使用者', 'admin.users.edit_user_description': '更新使用者的詳細資訊和角色。', - 'admin.users.form_name_label': '暱稱', + 'admin.users.form_full_name_label': '全名', + 'admin.users.form_nickname_label': '暱稱 (選填)', 'admin.users.form_email_label': '電子郵件地址', 'admin.users.form_role_label': '角色', 'admin.users.form_role_user': '使用者', diff --git a/src/types/index.ts b/src/types/index.ts index c1aa5b1..68ce1b3 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -23,7 +23,8 @@ export interface Toy { export interface User { id: string; - name: string; + name: string; // This is now Full Name + nickname?: string; email: string; role?: 'Admin' | 'User'; avatarUrl?: string;