I see this error with the app, reported by NextJS, please fix it. The er

This commit is contained in:
Indigo Tang 2025-07-06 12:47:10 +00:00
parent 7e09e249a7
commit c63e4d3648
11 changed files with 83 additions and 34 deletions

View File

@ -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 }) => (
<FormItem>
<FormLabel>{t('admin.users.form_name_label')}</FormLabel>
<FormLabel>{t('admin.users.form_full_name_label')}</FormLabel>
<FormControl>
<Input placeholder="e.g., John Doe" {...field} disabled={isSubmitting} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="nickname"
render={({ field }) => (
<FormItem>
<FormLabel>{t('admin.users.form_nickname_label')}</FormLabel>
<FormControl>
<Input placeholder="e.g., Johnny" {...field} disabled={isSubmitting} />
</FormControl>

View File

@ -102,7 +102,8 @@ export default function AdminUserManagementPage() {
<Table>
<TableHeader>
<TableRow>
<TableHead>{t('admin.users.table_header_name')}</TableHead>
<TableHead>{t('admin.users.table_header_full_name')}</TableHead>
<TableHead>{t('admin.users.table_header_nickname')}</TableHead>
<TableHead>{t('admin.users.table_header_email')}</TableHead>
<TableHead>{t('admin.users.table_header_role')}</TableHead>
<TableHead className="text-right">{t('admin.users.table_header_actions')}</TableHead>
@ -112,6 +113,7 @@ export default function AdminUserManagementPage() {
{users.map((user) => (
<TableRow key={user.id}>
<TableCell className="font-medium">{user.name}</TableCell>
<TableCell>{user.nickname}</TableCell>
<TableCell>{user.email}</TableCell>
<TableCell>{user.role}</TableCell>
<TableCell className="text-right space-x-2">

View File

@ -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,13 +119,18 @@ export default function ProfilePage() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-1">
<Label htmlFor="name">{t('dashboard.profile.full_name_label')}</Label>
<Input id="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="Your Nickname" disabled={isLoading} />
<Input id="name" value={name} onChange={(e) => setName(e.target.value)} placeholder="Your Full Name" required disabled={isLoading} />
</div>
<div className="space-y-1">
<Label htmlFor="nickname">{t('dashboard.profile.nickname_label')}</Label>
<Input id="nickname" value={nickname} onChange={(e) => setNickname(e.target.value)} placeholder="Your Nickname" disabled={isLoading} />
</div>
</div>
<div className="space-y-1">
<Label htmlFor="email">{t('dashboard.profile.email_label')}</Label>
<Input id="email" type="email" value={email} readOnly disabled className="bg-muted/50 cursor-not-allowed" />
</div>
</div>
<div className="space-y-1">
<Label htmlFor="bio">{t('dashboard.profile.bio_label')}</Label>

View File

@ -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() {
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="space-y-2">
<Label htmlFor="name">{t('register.name_label')}</Label>
<Label htmlFor="name">{t('register.full_name_label')}</Label>
<Input
id="name"
type="text"
placeholder="e.g., Johnny"
placeholder="e.g., John Doe"
value={name}
onChange={(e) => setName(e.target.value)}
required
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<Label htmlFor="nickname">{t('register.nickname_label')}</Label>
<Input
id="nickname"
type="text"
placeholder="e.g., Johnny"
value={nickname}
onChange={(e) => setNickname(e.target.value)}
disabled={isLoading}
/>
</div>
<div className="space-y-2">
<Label htmlFor="email">{t('login.email_label')}</Label>

View File

@ -13,11 +13,11 @@ interface RegisterUserResult {
user?: User;
}
export async function registerUser(data: { name: string; email: string; password?: string, role?: 'Admin' | 'User' }): Promise<RegisterUserResult> {
const { name, email, role = 'User' } = data;
export async function registerUser(data: { name: string; nickname?: string; email: string; password?: string, role?: 'Admin' | 'User' }): Promise<RegisterUserResult> {
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<UpdateUserResult> {
}
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 ?? '',

View File

@ -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[];
}

View File

@ -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,

View File

@ -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<Toy, 'ownerName' | 'dataAiHint'>[] = [

View File

@ -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',

View File

@ -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': '使用者',

View File

@ -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;