From dbdae213e39b82ff8ee565df0774bd2f72f06140 Mon Sep 17 00:00:00 2001 From: joonhoekim <26rote@gmail.com> Date: Fri, 22 Aug 2025 03:11:57 +0000 Subject: (김준회) 로그인 에러 토스트 메시지 개선, 중공업 유저의 협력사 유저 추가/수정시 전화번호 사용 반영 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/admin-users/service.ts | 20 +++++----- lib/admin-users/table/add-ausers-dialog.tsx | 50 ++++++++++++++++++++++++- lib/admin-users/table/ausers-table.tsx | 5 +-- lib/admin-users/table/update-auser-sheet.tsx | 56 ++++++++++++++++++++++++++-- lib/admin-users/validations.ts | 33 +++++++++++++++- 5 files changed, 142 insertions(+), 22 deletions(-) (limited to 'lib/admin-users') diff --git a/lib/admin-users/service.ts b/lib/admin-users/service.ts index 44111bef..b67aef20 100644 --- a/lib/admin-users/service.ts +++ b/lib/admin-users/service.ts @@ -5,8 +5,7 @@ import db from "@/db/db"; import logger from '@/lib/logger'; import { Role, roles, users, userView, type User, type UserView } from "@/db/schema/users"; // User 테이블 -import { type Company } from "@/db/schema/companies"; // User 테이블 -import { asc, desc, ilike, inArray, and, gte, lte, not, or, eq } from "drizzle-orm"; +import { asc, desc, ilike, and, or, eq } from "drizzle-orm"; import { headers } from 'next/headers'; // 레포지토리 함수들 (예시) - 아래처럼 작성했다고 가정 @@ -99,7 +98,7 @@ export async function getUsers(input: GetUsersSchema) { const pageCount = Math.ceil(total / input.perPage); return { data, pageCount }; - } catch (err) { + } catch { return { data: [], pageCount: 0 }; } }, @@ -185,7 +184,7 @@ export async function findUserById(id: number) { // } // } -export async function createAdminUser(input: CreateUserSchema & { language?: string }) { +export async function createAdminUser(input: CreateUserSchema & { language?: string; phone?: string }) { unstable_noStore(); // Next.js 캐싱 방지 try { @@ -252,6 +251,7 @@ export async function createAdminUser(input: CreateUserSchema & { language?: str const [newUser] = await insertUser(tx, { name: input.name, email: input.email, + phone: input.phone, // 전화번호 필드 추가 domain: input.domain, companyId: input.companyId ?? null, // 기타 필요한 필드 추가 @@ -300,7 +300,7 @@ export async function getUserCountGroupByCompany() { return obj; }); return result; - } catch (err) { + } catch { return {}; } }, @@ -335,8 +335,7 @@ export async function getUserCountGroupByRole() { // 여기서 result를 반환해 줘야 함! return result; - } catch (err) { - console.error("getUserCountGroupByRole error:", err); + } catch { return {}; } }, @@ -349,7 +348,7 @@ export async function getUserCountGroupByRole() { /** * 단건 업데이트 */ -export async function modifiUser(input: UpdateUserSchema & { id: number } & { language?: string }) { +export async function modifiUser(input: UpdateUserSchema & { id: number } & { language?: string; phone?: string }) { unstable_noStore(); try { @@ -363,6 +362,7 @@ export async function modifiUser(input: UpdateUserSchema & { id: number } & { la name: input.name, companyId: input.companyId, email: input.email, + phone: input.phone, // 전화번호 필드 추가 }); // 2) roles가 함께 왔다면, 기존 roles 삭제 → 새 roles 삽입 @@ -507,7 +507,7 @@ export async function removeUsers(input: { ids: number[] }) { export async function getAllCompanies(): Promise { try { return await findAllCompanies(); // Company[] - } catch (err) { + } catch { throw new Error("Failed to get companies"); } } @@ -515,7 +515,7 @@ export async function getAllCompanies(): Promise { export async function getAllRoles(): Promise { try { return await findAllRoles(); - } catch (err) { + } catch { throw new Error("Failed to get roles"); } } diff --git a/lib/admin-users/table/add-ausers-dialog.tsx b/lib/admin-users/table/add-ausers-dialog.tsx index 64941965..3b29adcf 100644 --- a/lib/admin-users/table/add-ausers-dialog.tsx +++ b/lib/admin-users/table/add-ausers-dialog.tsx @@ -47,12 +47,31 @@ import { Check, ChevronsUpDown, Loader } from "lucide-react" import { cn } from "@/lib/utils" import { toast } from "sonner" import { Vendor } from "@/db/schema/vendors" +import { FormDescription } from "@/components/ui/form" const languageOptions = [ { value: "ko", label: "한국어" }, { value: "en", label: "English" }, ] +// Phone validation helper +const validatePhoneNumber = (phone: string): boolean => { + if (!phone) return true; // Optional field + // Basic international phone number validation + const cleanPhone = phone.replace(/[\s\-\(\)]/g, ''); + return /^\+\d{3,20}$/.test(cleanPhone); +}; + +// Get phone placeholder +const getPhonePlaceholder = (): string => { + return "+82-10-1234-5678"; +}; + +// Get phone description +const getPhoneDescription = (): string => { + return "국제 전화번호를 입력하세요. (예: +82-10-1234-5678)"; +}; + export function AddUserDialog() { const [open, setOpen] = React.useState(false) @@ -74,11 +93,12 @@ export function AddUserDialog() { }, []) // react-hook-form 세팅 - const form = useForm({ + const form = useForm({ resolver: zodResolver(createUserSchema), defaultValues: { name: "", email: "", + phone: "", // Add phone field companyId: null, language:'en', // roles는 array, 여기서는 단일 선택 시 [role]로 담음 @@ -89,9 +109,11 @@ export function AddUserDialog() { }) - async function onSubmit(data: CreateUserSchema & { language?: string }) { + async function onSubmit(data: CreateUserSchema & { language?: string; phone?: string }) { data.domain = "partners" + // Phone validation is now handled by zod schema + // 만약 단일 Select로 role을 정했다면, data.roles = ["manager"] 이런 식 startAddTransition(async ()=> { const result = await createAdminUser(data) @@ -171,6 +193,30 @@ export function AddUserDialog() { )} /> + {/* 전화번호 - 새로 추가 */} + ( + + Phone Number + + + + + {getPhoneDescription()} + + + + )} + /> + {/* 회사 선택 (companyId) */} { + if (!phone) return true; // Optional field + // Basic international phone number validation + const cleanPhone = phone.replace(/[\s\-\(\)]/g, ''); + return /^\+\d{3,20}$/.test(cleanPhone); +}; + +// Get phone placeholder +const getPhonePlaceholder = (): string => { + return "+82-10-1234-5678"; +}; + +// Get phone description +const getPhoneDescription = (): string => { + return "국제 전화번호를 입력하세요. (예: +82-10-1234-5678)"; +}; + export function UpdateAuserSheet({ user, ...props }: UpdateAuserSheetProps) { const [isUpdatePending, startUpdateTransition] = React.useTransition() // 1) RHF 설정 - const form = useForm({ + const form = useForm({ resolver: zodResolver(updateUserSchema), defaultValues: { name: user?.user_name ?? "", email: user?.user_email ?? "", + phone: user?.user_phone ?? "", // Add phone field companyId: user?.company_id ?? null, roles: user?.roles ?? [], language:'en', @@ -72,15 +92,19 @@ export function UpdateAuserSheet({ user, ...props }: UpdateAuserSheetProps) { form.reset({ name: user.user_name, email: user.user_email, + phone: user.user_phone || "", // Add phone field companyId: user.company_id, roles: user.roles, + language: 'en', // You might want to get this from user object }) } }, [user, form]) // 3) onSubmit - async function onSubmit(input: UpdateUserSchema & { language?: string }) { + async function onSubmit(input: UpdateUserSchema & { language?: string; phone?: string }) { + // Phone validation is now handled by zod schema + startUpdateTransition(async () => { if (!user) return @@ -147,6 +171,30 @@ export function UpdateAuserSheet({ user, ...props }: UpdateAuserSheetProps) { )} /> + {/* 전화번호 - 새로 추가 */} + ( + + Phone Number + + + + + {getPhoneDescription()} + + + + )} + /> + {/* roles */} { + if (!phone) return false; // Optional field + // Remove spaces, hyphens, and parentheses for validation + const cleanPhone = phone.replace(/[\s\-\(\)]/g, ''); + // Basic international phone number validation + return /^\+\d{3,20}$/.test(cleanPhone); + }, + { + message: "올바른 국제 전화번호 형식이 아닙니다. +로 시작하는 3-20자리 번호를 입력해주세요. (예: +82-10-1234-5678)" + } + ), // 전화번호 필드 추가 }); @@ -75,7 +90,21 @@ export const updateUserSchema = z.object({ domain: z.enum(users.domain.enumValues).optional(), companyId: z.number().nullable().optional(), roles: z.array(z.string()).optional(), - language: z.enum(["ko", "en"]).optional(), + language: z.enum(["ko", "en"]).optional(), + phone: z + .string() + .refine( + (phone) => { + if (!phone) return false; // Optional field + // Remove spaces, hyphens, and parentheses for validation + const cleanPhone = phone.replace(/[\s\-\(\)]/g, ''); + // Basic international phone number validation + return /^\+\d{3,20}$/.test(cleanPhone); + }, + { + message: "올바른 국제 전화번호 형식이 아닙니다. +로 시작하는 3-20자리 번호를 입력해주세요. (예: +82-10-1234-5678)" + } + ), // 전화번호 필드 추가 }); export type GetUsersSchema = Awaited> -- cgit v1.2.3