diff --git a/backend-node/src/controllers/adminController.ts b/backend-node/src/controllers/adminController.ts index 97dcab9d..72dc368a 100644 --- a/backend-node/src/controllers/adminController.ts +++ b/backend-node/src/controllers/adminController.ts @@ -2744,6 +2744,30 @@ export const saveUser = async (req: AuthenticatedRequest, res: Response) => { return; } + // ๐Ÿ”’ ์‹ ๊ทœ ๋“ฑ๋ก(POST) ์‹œ user_id ์ค‘๋ณต ์ฒดํฌ (ํƒ€ ํšŒ์‚ฌ ๊ณ„์ • ๋ฎ์–ด์“ฐ๊ธฐ ๋ฐฉ์ง€) + if (!isUpdate) { + const existingById = await queryOne<{ user_id: string; company_code: string; user_name: string }>( + `SELECT user_id, company_code, user_name FROM user_info WHERE user_id = $1`, + [userData.userId] + ); + if (existingById) { + logger.warn(`์‹ ๊ทœ ์‚ฌ์šฉ์ž ๋“ฑ๋ก ์ฐจ๋‹จ - ์ด๋ฏธ ์กด์žฌํ•˜๋Š” user_id`, { + userId: userData.userId, + existingCompany: existingById.company_code, + requestCompany: userData.companyCode, + }); + res.status(409).json({ + success: false, + message: `์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ์•„์ด๋””์ž…๋‹ˆ๋‹ค: ${userData.userId}`, + error: { + code: "DUPLICATE_USER_ID", + details: `user_id '${userData.userId}' already exists (company: ${existingById.company_code})`, + }, + }); + return; + } + } + // ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” (๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ œ๊ณต๋œ ๊ฒฝ์šฐ์—๋งŒ) let encryptedPassword = null; if (userData.userPassword) { @@ -4161,11 +4185,31 @@ export const saveUserWithDept = async ( // 1. ๊ธฐ์กด ์‚ฌ์šฉ์ž ํ™•์ธ const existingUser = await client.query( - "SELECT user_id FROM user_info WHERE user_id = $1", + "SELECT user_id, company_code FROM user_info WHERE user_id = $1", [userInfo.user_id] ); const isExistingUser = existingUser.rows.length > 0; + // ๐Ÿ”’ ์‹ ๊ทœ ๋“ฑ๋ก(isUpdate=false) ์š”์ฒญ์ธ๋ฐ ์ด๋ฏธ ์กด์žฌํ•˜๋ฉด ์ค‘๋ณต ์ฐจ๋‹จ (ํƒ€ ํšŒ์‚ฌ ๊ณ„์ • ๋ฎ์–ด์“ฐ๊ธฐ ๋ฐฉ์ง€) + if (!isUpdate && isExistingUser) { + await client.query("ROLLBACK"); + const existingCompany = existingUser.rows[0]?.company_code; + logger.warn(`์‹ ๊ทœ ์‚ฌ์šฉ์ž(+๋ถ€์„œ) ๋“ฑ๋ก ์ฐจ๋‹จ - ์ด๋ฏธ ์กด์žฌํ•˜๋Š” user_id`, { + userId: userInfo.user_id, + existingCompany, + requestCompany: companyCode, + }); + res.status(409).json({ + success: false, + message: `์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ์•„์ด๋””์ž…๋‹ˆ๋‹ค: ${userInfo.user_id}`, + error: { + code: "DUPLICATE_USER_ID", + details: `user_id '${userInfo.user_id}' already exists (company: ${existingCompany})`, + }, + }); + return; + } + // 2. ๋น„๋ฐ€๋ฒˆํ˜ธ ์•”ํ˜ธํ™” (์ƒˆ ์‚ฌ์šฉ์ž์ด๊ฑฐ๋‚˜ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ์ œ๊ณต๋œ ๊ฒฝ์šฐ) let encryptedPassword = null; if (userInfo.user_password) { diff --git a/backend-node/src/controllers/packagingController.ts b/backend-node/src/controllers/packagingController.ts index 4974c80f..d87c0c7e 100644 --- a/backend-node/src/controllers/packagingController.ts +++ b/backend-node/src/controllers/packagingController.ts @@ -45,7 +45,7 @@ export async function createPkgUnit( const { pkg_code, pkg_name, pkg_type, status, width_mm, length_mm, height_mm, - self_weight_kg, max_load_kg, volume_l, remarks, + self_weight_kg, max_load_kg, volume_l, remarks, item_number, } = req.body; if (!pkg_code || !pkg_name) { @@ -64,12 +64,12 @@ export async function createPkgUnit( const result = await pool.query( `INSERT INTO pkg_unit - (company_code, pkg_code, pkg_name, pkg_type, status, - width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, writer) - VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) + (id, company_code, pkg_code, pkg_name, pkg_type, status, + width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, item_number, writer, created_date) + VALUES (gen_random_uuid()::text, $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14, NOW()) RETURNING *`, [companyCode, pkg_code, pkg_name, pkg_type, status || "ACTIVE", - width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, + width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, item_number || pkg_code, req.user!.userId] ); @@ -92,7 +92,7 @@ export async function updatePkgUnit( const { pkg_name, pkg_type, status, width_mm, length_mm, height_mm, - self_weight_kg, max_load_kg, volume_l, remarks, + self_weight_kg, max_load_kg, volume_l, remarks, item_number, } = req.body; const result = await pool.query( @@ -100,12 +100,14 @@ export async function updatePkgUnit( pkg_name=$1, pkg_type=$2, status=$3, width_mm=$4, length_mm=$5, height_mm=$6, self_weight_kg=$7, max_load_kg=$8, volume_l=$9, remarks=$10, - updated_date=NOW(), writer=$11 - WHERE id=$12 AND company_code=$13 + item_number=COALESCE($11, item_number), + updated_date=NOW(), writer=$12 + WHERE id=$13 AND company_code=$14 RETURNING *`, [pkg_name, pkg_type, status, width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, volume_l, remarks, + item_number, req.user!.userId, id, companyCode] ); @@ -286,7 +288,7 @@ export async function createLoadingUnit( const { loading_code, loading_name, loading_type, status, width_mm, length_mm, height_mm, - self_weight_kg, max_load_kg, max_stack, remarks, + self_weight_kg, max_load_kg, max_stack, remarks, item_number, } = req.body; if (!loading_code || !loading_name) { @@ -305,12 +307,12 @@ export async function createLoadingUnit( const result = await pool.query( `INSERT INTO loading_unit - (company_code, loading_code, loading_name, loading_type, status, - width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, writer) - VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) + (id, company_code, loading_code, loading_name, loading_type, status, + width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, item_number, writer, created_date) + VALUES (gen_random_uuid()::text, $1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14, NOW()) RETURNING *`, [companyCode, loading_code, loading_name, loading_type, status || "ACTIVE", - width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, + width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, item_number || loading_code, req.user!.userId] ); @@ -333,7 +335,7 @@ export async function updateLoadingUnit( const { loading_name, loading_type, status, width_mm, length_mm, height_mm, - self_weight_kg, max_load_kg, max_stack, remarks, + self_weight_kg, max_load_kg, max_stack, remarks, item_number, } = req.body; const result = await pool.query( @@ -341,12 +343,14 @@ export async function updateLoadingUnit( loading_name=$1, loading_type=$2, status=$3, width_mm=$4, length_mm=$5, height_mm=$6, self_weight_kg=$7, max_load_kg=$8, max_stack=$9, remarks=$10, - updated_date=NOW(), writer=$11 - WHERE id=$12 AND company_code=$13 + item_number=COALESCE($11, item_number), + updated_date=NOW(), writer=$12 + WHERE id=$13 AND company_code=$14 RETURNING *`, [loading_name, loading_type, status, width_mm, length_mm, height_mm, self_weight_kg, max_load_kg, max_stack, remarks, + item_number, req.user!.userId, id, companyCode] ); diff --git a/backend-node/src/controllers/tableManagementController.ts b/backend-node/src/controllers/tableManagementController.ts index 24ad771d..2028dbb8 100644 --- a/backend-node/src/controllers/tableManagementController.ts +++ b/backend-node/src/controllers/tableManagementController.ts @@ -949,14 +949,25 @@ export async function addTableData( const tableManagementService = new TableManagementService(); - // ๐Ÿ†• ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: company_code ์ž๋™ ์ถ”๊ฐ€ (ํ…Œ์ด๋ธ”์— company_code ์ปฌ๋Ÿผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ) + // ๐Ÿ†• ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: company_code ๊ฐ•์ œ ์„ค์ • (ํ…Œ์ด๋ธ”์— company_code ์ปฌ๋Ÿผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ) + // ์ผ๋ฐ˜ ๊ด€๋ฆฌ์ž๋Š” ์ž์‹ ์˜ company_code๋กœ ๊ฐ•์ œ, ์Šˆํผ๊ด€๋ฆฌ์ž(*)๋Š” ํด๋ผ์ด์–ธํŠธ ๊ฐ’ ํ—ˆ์šฉ(ํ”„๋ฆฌ๋ทฐ์šฉ) const companyCode = req.user?.companyCode; - if (companyCode && !data.company_code) { - // ํ…Œ์ด๋ธ”์— company_code ์ปฌ๋Ÿผ์ด ์žˆ๋Š”์ง€ ํ™•์ธ + if (companyCode) { const hasCompanyCodeColumn = await tableManagementService.hasColumn(tableName, "company_code"); if (hasCompanyCodeColumn) { - data.company_code = companyCode; - logger.info(`๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: company_code ์ž๋™ ์ถ”๊ฐ€ - ${companyCode}`); + if (companyCode === "*") { + // ์Šˆํผ๊ด€๋ฆฌ์ž: ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ธ company_code ํ—ˆ์šฉ, ์—†์œผ๋ฉด '*' + if (!data.company_code) { + data.company_code = companyCode; + logger.info(`๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: company_code ์ž๋™ ์ถ”๊ฐ€ - ${companyCode}`); + } + } else { + // ์ผ๋ฐ˜ ๊ด€๋ฆฌ์ž: ํ•ญ์ƒ ์ž์‹ ์˜ company_code๋กœ ๊ฐ•์ œ (ํƒ€ ํšŒ์‚ฌ ์ฃผ์ž… ๋ฐฉ์ง€) + if (data.company_code && data.company_code !== companyCode) { + logger.warn(`๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ: ํƒ€ ํšŒ์‚ฌ company_code ์ฃผ์ž… ์‹œ๋„ ์ฐจ๋‹จ - ์š”์ฒญ๊ฐ’: ${data.company_code}, ๊ฐ•์ œ๊ฐ’: ${companyCode}`); + } + data.company_code = companyCode; + } } } @@ -1115,6 +1126,37 @@ export async function editTableData( const tableManagementService = new TableManagementService(); const companyCode = req.user?.companyCode || "*"; + // ๐Ÿ”’ ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ๋ณด์•ˆ: ์ผ๋ฐ˜ ๊ด€๋ฆฌ์ž๋Š” ํƒ€ ํšŒ์‚ฌ ๋ฐ์ดํ„ฐ ์ˆ˜์ • ๋ถˆ๊ฐ€ + company_code ๋ณ€๊ฒฝ ์ฐจ๋‹จ + if (companyCode !== "*") { + const hasCompanyCodeColumn = await tableManagementService.hasColumn(tableName, "company_code"); + if (hasCompanyCodeColumn) { + // 1. ์›๋ณธ ๋ฐ์ดํ„ฐ์˜ company_code ํ™•์ธ + if (originalData?.id) { + try { + const existing = await tableManagementService.getTableData(tableName, { + page: 1, size: 1, + search: { id: String(originalData.id) }, + }); + const existingRow = existing.data?.[0]; + if (existingRow && existingRow.company_code && existingRow.company_code !== companyCode && existingRow.company_code !== "*") { + logger.warn(`๐Ÿ”’ ํƒ€ ํšŒ์‚ฌ ๋ฐ์ดํ„ฐ ์ˆ˜์ • ์ฐจ๋‹จ: ${tableName} id=${originalData.id}, ์š”์ฒญ์ž=${companyCode}, ๋ฐ์ดํ„ฐ์†Œ์†=${existingRow.company_code}`); + res.status(403).json({ + success: false, + message: "ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ •ํ•  ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค.", + error: { code: "FORBIDDEN_COMPANY", details: "cross-company edit blocked" }, + }); + return; + } + } catch { /* skip ๊ฒ€์ฆ ์‹คํŒจ ์‹œ ์›๋ž˜ ํ๋ฆ„ ์ง„ํ–‰ */ } + } + // 2. updatedData์— company_code ๋ณ€๊ฒฝ ์‹œ๋„๊ฐ€ ์žˆ์œผ๋ฉด ์ œ๊ฑฐ + if (updatedData.company_code && updatedData.company_code !== companyCode) { + logger.warn(`๐Ÿ”’ company_code ๋ณ€๊ฒฝ ์‹œ๋„ ์ฐจ๋‹จ: ${tableName}, ์š”์ฒญ๊ฐ’=${updatedData.company_code}`); + delete updatedData.company_code; + } + } + } + // ํšŒ์‚ฌ๋ณ„ NOT NULL ์†Œํ”„ํŠธ ์ œ์•ฝ์กฐ๊ฑด ๊ฒ€์ฆ (์ˆ˜์ • ๋ฐ์ดํ„ฐ ๋Œ€์ƒ) const notNullViolations = await tableManagementService.validateNotNullConstraints( tableName, diff --git a/backend-node/src/services/numberingRuleService.ts b/backend-node/src/services/numberingRuleService.ts index 1179edbe..c1573d77 100644 --- a/backend-node/src/services/numberingRuleService.ts +++ b/backend-node/src/services/numberingRuleService.ts @@ -502,7 +502,8 @@ class NumberingRuleService { let baseSequence = currentCounter; - // 2. ๊ทœ์น™์— tableName/columnName์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๋Œ€์ƒ ํ…Œ์ด๋ธ”์—์„œ MAX ์กฐํšŒ + // 2. ๊ทœ์น™์— tableName/columnName์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ๋Œ€์ƒ ํ…Œ์ด๋ธ”์—์„œ MAX๋งŒ ์‚ฌ์šฉ (์ˆœ์ˆ˜ MAX+1 ๋ฐฉ์‹) + // - ์‚ญ์ œ๋œ ๋ฒˆํ˜ธ ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅ (์นด์šดํ„ฐ ๊ฐ’ ๋ฌด์‹œ) if (rule.tableName && rule.columnName) { try { const sortedParts = [...rule.parts].sort((a: any, b: any) => a.order - b.order); @@ -515,12 +516,10 @@ class NumberingRuleService { psInfo.prefix, psInfo.suffix, psInfo.seqLength, companyCode ); - if (maxFromTable > baseSequence) { - logger.info("ํ…Œ์ด๋ธ” ๋‚ด ์ตœ๋Œ€๊ฐ’์ด ์นด์šดํ„ฐ๋ณด๋‹ค ๋†’์Œ โ†’ ๋™๊ธฐํ™”", { - ruleId, companyCode, currentCounter, maxFromTable, - }); - baseSequence = maxFromTable; - } + logger.info("ํ…Œ์ด๋ธ” MAX ๊ธฐ์ค€ ์ฑ„๋ฒˆ", { + ruleId, companyCode, currentCounter, maxFromTable, + }); + baseSequence = maxFromTable; } } catch (error: any) { logger.warn("ํ…Œ์ด๋ธ” ๊ธฐ๋ฐ˜ MAX ์กฐํšŒ ์‹คํŒจ, ์นด์šดํ„ฐ ๊ธฐ๋ฐ˜ ํด๋ฐฑ", { @@ -1422,7 +1421,7 @@ class NumberingRuleService { ? 0 : await this.getSequenceForPrefix(pool, ruleId, companyCode, prefixKey); - // ๋Œ€์ƒ ํ…Œ์ด๋ธ”์—์„œ ์‹ค์ œ ์ตœ๋Œ€ ์‹œํ€€์Šค ์กฐํšŒ + // ๋Œ€์ƒ ํ…Œ์ด๋ธ”์—์„œ ์‹ค์ œ ์ตœ๋Œ€ ์‹œํ€€์Šค๋งŒ ์‚ฌ์šฉ (์ˆœ์ˆ˜ MAX+1) let baseSeq = currentSeq; if (rule.tableName && rule.columnName) { try { @@ -1436,13 +1435,10 @@ class NumberingRuleService { psInfo.prefix, psInfo.suffix, psInfo.seqLength, companyCode ); - - if (maxFromTable > baseSeq) { - logger.info("๋ฏธ๋ฆฌ๋ณด๊ธฐ: ํ…Œ์ด๋ธ” ๋‚ด ์ตœ๋Œ€๊ฐ’์ด ์นด์šดํ„ฐ๋ณด๋‹ค ๋†’์Œ", { - ruleId, companyCode, currentSeq, maxFromTable, - }); - baseSeq = maxFromTable; - } + logger.info("๋ฏธ๋ฆฌ๋ณด๊ธฐ: ํ…Œ์ด๋ธ” MAX ๊ธฐ์ค€ ์ฑ„๋ฒˆ", { + ruleId, companyCode, currentSeq, maxFromTable, + }); + baseSeq = maxFromTable; } } catch (error: any) { logger.warn("๋ฏธ๋ฆฌ๋ณด๊ธฐ: ํ…Œ์ด๋ธ” ๊ธฐ๋ฐ˜ MAX ์กฐํšŒ ์‹คํŒจ, ์นด์šดํ„ฐ ๊ธฐ๋ฐ˜ ํด๋ฐฑ", { diff --git a/frontend/app/(main)/COMPANY_10/master-data/company/page.tsx b/frontend/app/(main)/COMPANY_10/master-data/company/page.tsx index 9d7f2dea..0e4de0cd 100644 --- a/frontend/app/(main)/COMPANY_10/master-data/company/page.tsx +++ b/frontend/app/(main)/COMPANY_10/master-data/company/page.tsx @@ -167,6 +167,9 @@ export default function CompanyPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_10/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_10/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_10/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_10/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_10/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_10/sales/order/page.tsx b/frontend/app/(main)/COMPANY_10/sales/order/page.tsx index 8f137c7a..76d6a061 100644 --- a/frontend/app/(main)/COMPANY_10/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_10/sales/order/page.tsx @@ -1013,7 +1013,19 @@ export default function SalesOrderPage() { ) : ( - filteredFlatRows.map((row) => { + ts.groupData(filteredFlatRows).map((row: any) => { + // ๊ทธ๋ฃน ์š”์•ฝ ํ–‰ ๋ Œ๋”๋ง + if (row._isGroupSummary) { + return ( + + + {row._groupLabel || "ํ•ฉ๊ณ„"}: {row._count ? `${row._count}๊ฑด` : ""} + {row.qty ? ` ยท ์ˆ˜๋Ÿ‰ ${Number(row.qty).toLocaleString()}` : ""} + {row.amount ? ` ยท ๊ธˆ์•ก ${Number(row.amount).toLocaleString()}` : ""} + + + ); + } const isChecked = checkedIds.includes(row.id); return ( >({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_16/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_16/master-data/department/page.tsx index 23f70f2b..33ea1535 100644 --- a/frontend/app/(main)/COMPANY_16/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_16/master-data/department/page.tsx @@ -79,6 +79,9 @@ export default function DepartmentPage() { const [userModalOpen, setUserModalOpen] = useState(false); const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ (true: ํ™•์ธ๋จ, false: ๋ฏธํ™•์ธ/์ค‘๋ณต) + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); const [formErrors, setFormErrors] = useState>({}); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); // ์ˆ˜์ • ๋ชจ๋“œ๋Š” ๊ธฐ์กด ID ์‚ฌ์šฉ } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_16/sales/order/page.tsx b/frontend/app/(main)/COMPANY_16/sales/order/page.tsx index 8f137c7a..76d6a061 100644 --- a/frontend/app/(main)/COMPANY_16/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_16/sales/order/page.tsx @@ -1013,7 +1013,19 @@ export default function SalesOrderPage() { ) : ( - filteredFlatRows.map((row) => { + ts.groupData(filteredFlatRows).map((row: any) => { + // ๊ทธ๋ฃน ์š”์•ฝ ํ–‰ ๋ Œ๋”๋ง + if (row._isGroupSummary) { + return ( + + + {row._groupLabel || "ํ•ฉ๊ณ„"}: {row._count ? `${row._count}๊ฑด` : ""} + {row.qty ? ` ยท ์ˆ˜๋Ÿ‰ ${Number(row.qty).toLocaleString()}` : ""} + {row.amount ? ` ยท ๊ธˆ์•ก ${Number(row.amount).toLocaleString()}` : ""} + + + ); + } const isChecked = checkedIds.includes(row.id); return ( >({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_29/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_29/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_29/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_29/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_29/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_29/sales/order/page.tsx b/frontend/app/(main)/COMPANY_29/sales/order/page.tsx index 8f137c7a..76d6a061 100644 --- a/frontend/app/(main)/COMPANY_29/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_29/sales/order/page.tsx @@ -1013,7 +1013,19 @@ export default function SalesOrderPage() { ) : ( - filteredFlatRows.map((row) => { + ts.groupData(filteredFlatRows).map((row: any) => { + // ๊ทธ๋ฃน ์š”์•ฝ ํ–‰ ๋ Œ๋”๋ง + if (row._isGroupSummary) { + return ( + + + {row._groupLabel || "ํ•ฉ๊ณ„"}: {row._count ? `${row._count}๊ฑด` : ""} + {row.qty ? ` ยท ์ˆ˜๋Ÿ‰ ${Number(row.qty).toLocaleString()}` : ""} + {row.amount ? ` ยท ๊ธˆ์•ก ${Number(row.amount).toLocaleString()}` : ""} + + + ); + } const isChecked = checkedIds.includes(row.id); return ( >({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_30/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_30/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_30/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_30/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_30/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_7/logistics/packaging/page.tsx b/frontend/app/(main)/COMPANY_7/logistics/packaging/page.tsx index 6ae340aa..942002e3 100644 --- a/frontend/app/(main)/COMPANY_7/logistics/packaging/page.tsx +++ b/frontend/app/(main)/COMPANY_7/logistics/packaging/page.tsx @@ -37,7 +37,7 @@ const GRID_COLUMNS = [ { key: "pkg_name", label: "ํฌ์žฅ๋ช…" }, { key: "pkg_type", label: "์œ ํ˜•" }, { key: "size", label: "ํฌ๊ธฐ(mm)" }, - { key: "max_weight", label: "์ตœ๋Œ€์ค‘๋Ÿ‰" }, + { key: "max_load_kg", label: "์ตœ๋Œ€์ค‘๋Ÿ‰" }, { key: "status", label: "์ƒํƒœ" }, ]; @@ -51,7 +51,7 @@ const LOADING_TYPE_LABEL: Record = { ALU_PALLET: "์•Œ๋ฃจ๋ฏธ๋Š„ํŒŒ๋ ›ํŠธ", CONTAINER: "์ปจํ…Œ์ด๋„ˆ", STEEL_BOX: "์ฒ ์žฌํ•จ", CAGE: "์ผ€์ด์ง€", ETC: "๊ธฐํƒ€", }; -const STATUS_LABEL: Record = { ACTIVE: "์‚ฌ์šฉ", INACTIVE: "๋ฏธ์‚ฌ์šฉ" }; +const STATUS_LABEL: Record = { ACTIVE: "์‚ฌ์šฉ", INACTIVE: "๋ฏธ์‚ฌ์šฉ", UNREGISTERED: "๋ฏธ๋“ฑ๋ก" }; const getStatusColor = (s: string) => s === "ACTIVE" ? "bg-success/10 text-success" : "bg-muted text-muted-foreground"; const fmtSize = (w: any, l: any, h: any) => { @@ -118,20 +118,80 @@ export default function PackagingPage() { const [saving, setSaving] = useState(false); - // --- ๋ฐ์ดํ„ฐ ๋กœ๋“œ --- + // --- ๋ฐ์ดํ„ฐ ๋กœ๋“œ (item_info ๊ธฐ๋ฐ˜ + pkg_unit/loading_unit LEFT JOIN ๋ฐฉ์‹) --- const fetchPkgUnits = useCallback(async () => { setPkgLoading(true); try { - const res = await getPkgUnits(); - if (res.success) setPkgUnits(res.data); + const [itemRes, pkgRes] = await Promise.all([ + getItemsByDivision("ํฌ์žฅ์žฌ"), + getPkgUnits(), + ]); + const items = itemRes.success ? itemRes.data : []; + const existing = pkgRes.success ? pkgRes.data : []; + // item_info ๊ธฐ์ค€์œผ๋กœ ๋ณ‘ํ•ฉ: item_number ๋งค์นญ๋˜๋Š” pkg_unit์ด ์žˆ์œผ๋ฉด ๊ทธ ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ, ์—†์œผ๋ฉด ๋ฏธ๋“ฑ๋ก placeholder + const merged: PkgUnit[] = items.map((item) => { + const match = existing.find((p) => p.item_number === item.item_number || p.pkg_code === item.item_number); + if (match) return { ...match, pkg_code: match.pkg_code || item.item_number, pkg_name: match.pkg_name || item.item_name }; + const dims = parseSpecDimensions(item.size); + return { + id: "", + company_code: "", + pkg_code: item.item_number, + pkg_name: item.item_name, + pkg_type: "", + status: "INACTIVE", + width_mm: dims.w || null, + length_mm: dims.l || null, + height_mm: dims.h || null, + self_weight_kg: null, + max_load_kg: null, + volume_l: null, + remarks: null, + created_date: "", + writer: null, + item_number: item.item_number, + } as PkgUnit; + }); + // ํ’ˆ๋ชฉ์ •๋ณด์— ์—†์ง€๋งŒ pkg_unit์—๋งŒ ์žˆ๋Š” ํ•ญ๋ชฉ (๊ณ ์•„) ์ถ”๊ฐ€ + const orphans = existing.filter((p) => !items.some((i) => i.item_number === p.item_number || i.item_number === p.pkg_code)); + setPkgUnits([...merged, ...orphans]); } catch { /* ignore */ } finally { setPkgLoading(false); } }, []); const fetchLoadingUnits = useCallback(async () => { setLoadingLoading(true); try { - const res = await getLoadingUnits(); - if (res.success) setLoadingUnits(res.data); + const [itemRes, luRes] = await Promise.all([ + getItemsByDivision("์ ์žฌํ•จ"), + getLoadingUnits(), + ]); + const items = itemRes.success ? itemRes.data : []; + const existing = luRes.success ? luRes.data : []; + const merged: LoadingUnit[] = items.map((item) => { + const match = existing.find((l) => l.item_number === item.item_number || l.loading_code === item.item_number); + if (match) return { ...match, loading_code: match.loading_code || item.item_number, loading_name: match.loading_name || item.item_name }; + const dims = parseSpecDimensions(item.size); + return { + id: "", + company_code: "", + loading_code: item.item_number, + loading_name: item.item_name, + loading_type: "", + status: "INACTIVE", + width_mm: dims.w || null, + length_mm: dims.l || null, + height_mm: dims.h || null, + self_weight_kg: null, + max_load_kg: null, + max_stack: null, + remarks: null, + created_date: "", + writer: null, + item_number: item.item_number, + } as LoadingUnit; + }); + const orphans = existing.filter((l) => !items.some((i) => i.item_number === l.item_number || i.item_number === l.loading_code)); + setLoadingUnits([...merged, ...orphans]); } catch { /* ignore */ } finally { setLoadingLoading(false); } }, []); @@ -177,12 +237,21 @@ export default function PackagingPage() { }); // --- ํฌ์žฅ์žฌ ๋“ฑ๋ก/์ˆ˜์ • ๋ชจ๋‹ฌ --- - const openPkgModal = async (mode: "create" | "edit") => { - setPkgModalMode(mode); - if (mode === "edit" && selectedPkg) { - setPkgForm({ ...selectedPkg }); + // row: ๋ฆฌ์ŠคํŠธ์—์„œ ํด๋ฆญํ•œ ํ–‰. id๊ฐ€ ๋นˆ ๋ฌธ์ž์—ด์ด๋ฉด ๋ฏธ๋“ฑ๋ก ์ƒํƒœ(item_info๋งŒ ์žˆ์Œ) โ†’ ๋“ฑ๋ก ๋ชจ๋‹ฌ + const openPkgModal = async (mode: "create" | "edit", row?: PkgUnit) => { + const target = row || selectedPkg; + const isRegistered = !!(target && target.id); + const actualMode: "create" | "edit" = isRegistered ? "edit" : "create"; + setPkgModalMode(actualMode); + if (target) { + // ๊ธฐ์กด ๋ฐ์ดํ„ฐ or ํ’ˆ๋ชฉ์ •๋ณด ๊ธฐ๋ฐ˜ ์ดˆ๊ธฐ๊ฐ’ ์„ธํŒ… + setPkgForm({ + ...target, + status: target.status || "ACTIVE", + item_number: target.item_number || target.pkg_code || "", + }); } else { - setPkgForm({ pkg_code: "", pkg_name: "", pkg_type: "", status: "ACTIVE", width_mm: "", length_mm: "", height_mm: "", self_weight_kg: "", max_load_kg: "", volume_l: "", remarks: "" }); + setPkgForm({ pkg_code: "", pkg_name: "", pkg_type: "", status: "ACTIVE", width_mm: "", length_mm: "", height_mm: "", self_weight_kg: "", max_load_kg: "", volume_l: "", remarks: "", item_number: "" }); } setPkgItemPopoverOpen(false); try { @@ -232,12 +301,19 @@ export default function PackagingPage() { }; // --- ์ ์žฌํ•จ ๋“ฑ๋ก/์ˆ˜์ • ๋ชจ๋‹ฌ --- - const openLoadModal = async (mode: "create" | "edit") => { - setLoadModalMode(mode); - if (mode === "edit" && selectedLoading) { - setLoadForm({ ...selectedLoading }); + const openLoadModal = async (mode: "create" | "edit", row?: LoadingUnit) => { + const target = row || selectedLoading; + const isRegistered = !!(target && target.id); + const actualMode: "create" | "edit" = isRegistered ? "edit" : "create"; + setLoadModalMode(actualMode); + if (target) { + setLoadForm({ + ...target, + status: target.status || "ACTIVE", + item_number: target.item_number || target.loading_code || "", + }); } else { - setLoadForm({ loading_code: "", loading_name: "", loading_type: "", status: "ACTIVE", width_mm: "", length_mm: "", height_mm: "", self_weight_kg: "", max_load_kg: "", max_stack: "", remarks: "" }); + setLoadForm({ loading_code: "", loading_name: "", loading_type: "", status: "ACTIVE", width_mm: "", length_mm: "", height_mm: "", self_weight_kg: "", max_load_kg: "", max_stack: "", remarks: "", item_number: "" }); } setLoadItemPopoverOpen(false); try { @@ -442,12 +518,8 @@ export default function PackagingPage() {
)} - + {/* ๋ฉ”์ธ ๋ฆฌ์ŠคํŠธ๊ฐ€ ํ’ˆ๋ชฉ์ •๋ณด ๊ธฐ๋ฐ˜์œผ๋กœ ์ž๋™ ๊ตฌ์„ฑ๋˜๋ฏ€๋กœ ๋“ฑ๋ก ๋ฒ„ํŠผ ์ œ๊ฑฐ. + ๋ฏธ๋“ฑ๋ก ์ƒํƒœ์˜ ํ’ˆ๋ชฉ์„ ์ง์ ‘ ํด๋ฆญํ•˜๋ฉด ๋“ฑ๋ก ๋ชจ๋‹ฌ์ด ์—ด๋ฆผ. */} @@ -466,22 +538,30 @@ export default function PackagingPage() { size: { width: "w-[100px]", render: (_v: any, row: any) => fmtSize(row.width_mm, row.length_mm, row.height_mm) }, max_weight: { width: "w-[80px]", align: "right", render: (v: any) => Number(v || 0) > 0 ? `${v}kg` : "-" }, max_load_kg: { width: "w-[80px]", align: "right", render: (v: any) => Number(v || 0) > 0 ? `${v}kg` : "-" }, - status: { width: "w-[60px]", align: "center", render: (v: any) => ( - - {STATUS_LABEL[v] || v} - - )}, + status: { width: "w-[60px]", align: "center", render: (v: any, row: any) => { + const isUnreg = !row.id; + const cls = isUnreg ? "bg-warning/10 text-warning" : getStatusColor(v); + const label = isUnreg ? "๋ฏธ๋“ฑ๋ก" : (STATUS_LABEL[v] || v); + return {label}; + }}, }; return { key: col.key, label: col.label, ...renderMap[col.key] }; })} data={ts.groupData(filteredPkgUnits)} - rowKey={(row) => String(row.id)} + rowKey={(row) => row.id ? `reg-${row.id}` : `new-${row.pkg_code || row.item_number}`} loading={pkgLoading} - emptyMessage="๋“ฑ๋ก๋œ ํฌ์žฅ์žฌ๊ฐ€ ์—†์–ด์š”" - selectedId={selectedPkg ? String(selectedPkg.id) : null} + emptyMessage="ํฌ์žฅ์žฌ ํ’ˆ๋ชฉ์ด ์—†์–ด์š”. ํ’ˆ๋ชฉ์ •๋ณด์—์„œ ๊ด€๋ฆฌํ’ˆ๋ชฉ์„ 'ํฌ์žฅ์žฌ'๋กœ ๋“ฑ๋กํ•ด์ฃผ์„ธ์š”." + selectedId={selectedPkg ? (selectedPkg.id ? `reg-${selectedPkg.id}` : `new-${selectedPkg.pkg_code || selectedPkg.item_number}`) : null} onSelect={(id) => { - const pkg = filteredPkgUnits.find((p) => String(p.id) === id); - if (pkg) selectPkg(pkg); + const pkg = filteredPkgUnits.find((p) => (p.id ? `reg-${p.id}` : `new-${p.pkg_code || p.item_number}`) === id); + if (pkg) { + if (!pkg.id) { + // ๋ฏธ๋“ฑ๋ก โ†’ ๋ฐ”๋กœ ๋“ฑ๋ก ๋ชจ๋‹ฌ + openPkgModal("create", pkg); + } else { + selectPkg(pkg); + } + } }} showPagination={false} draggableColumns @@ -592,14 +672,24 @@ export default function PackagingPage() {
- ) : filteredLoadingUnits.map((l) => ( + ) : filteredLoadingUnits.map((l) => { + const isUnreg = !l.id; + const rowKey = l.id ? `reg-${l.id}` : `new-${l.loading_code || l.item_number}`; + const selKey = selectedLoading ? (selectedLoading.id ? `reg-${selectedLoading.id}` : `new-${selectedLoading.loading_code || selectedLoading.item_number}`) : null; + return ( selectLoading(l)} + onClick={() => { + if (isUnreg) { + openLoadModal("create", l); + } else { + selectLoading(l); + } + }} > {l.loading_code} {l.loading_name} @@ -607,12 +697,13 @@ export default function PackagingPage() { {fmtSize(l.width_mm, l.length_mm, l.height_mm)} {Number(l.max_load_kg || 0) > 0 ? `${l.max_load_kg}kg` : "-"} - - {STATUS_LABEL[l.status] || l.status} + + {isUnreg ? "๋ฏธ๋“ฑ๋ก" : (STATUS_LABEL[l.status] || l.status)} - ))} + ); + })}
@@ -699,35 +790,9 @@ export default function PackagingPage() { {pkgModalMode === "create" && (
- - - - - - { - const item = pkgItemOptions.find((i) => i.id === value); - if (!item) return 0; - const target = `${item.item_number} ${item.item_name} ${item.size || ""}`.toLowerCase(); - return target.includes(search.toLowerCase()) ? 1 : 0; - }}> - - - ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค - {pkgItemOptions.map((item) => ( - onPkgItemSelect(item)} className="text-xs"> - - {item.item_name} - {item.item_number} - {item.size && {item.size}} - - ))} - - - - +
+ {pkgForm.pkg_code ? `${pkgForm.pkg_name} (${pkgForm.pkg_code})` : "-"} +
)}
@@ -781,35 +846,9 @@ export default function PackagingPage() { {loadModalMode === "create" && (
- - - - - - { - const item = loadItemOptions.find((i) => i.id === value); - if (!item) return 0; - const target = `${item.item_number} ${item.item_name} ${item.size || ""}`.toLowerCase(); - return target.includes(search.toLowerCase()) ? 1 : 0; - }}> - - - ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค - {loadItemOptions.map((item) => ( - onLoadItemSelect(item)} className="text-xs"> - - {item.item_name} - {item.item_number} - {item.size && {item.size}} - - ))} - - - - +
+ {loadForm.loading_code ? `${loadForm.loading_name} (${loadForm.loading_code})` : "-"} +
)}
diff --git a/frontend/app/(main)/COMPANY_7/master-data/company/page.tsx b/frontend/app/(main)/COMPANY_7/master-data/company/page.tsx index 9d7f2dea..0e4de0cd 100644 --- a/frontend/app/(main)/COMPANY_7/master-data/company/page.tsx +++ b/frontend/app/(main)/COMPANY_7/master-data/company/page.tsx @@ -167,6 +167,9 @@ export default function CompanyPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_7/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_7/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_7/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_7/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_7/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_7/sales/order/page.tsx b/frontend/app/(main)/COMPANY_7/sales/order/page.tsx index 8f137c7a..76d6a061 100644 --- a/frontend/app/(main)/COMPANY_7/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_7/sales/order/page.tsx @@ -1013,7 +1013,19 @@ export default function SalesOrderPage() { ) : ( - filteredFlatRows.map((row) => { + ts.groupData(filteredFlatRows).map((row: any) => { + // ๊ทธ๋ฃน ์š”์•ฝ ํ–‰ ๋ Œ๋”๋ง + if (row._isGroupSummary) { + return ( + + + {row._groupLabel || "ํ•ฉ๊ณ„"}: {row._count ? `${row._count}๊ฑด` : ""} + {row.qty ? ` ยท ์ˆ˜๋Ÿ‰ ${Number(row.qty).toLocaleString()}` : ""} + {row.amount ? ` ยท ๊ธˆ์•ก ${Number(row.amount).toLocaleString()}` : ""} + + + ); + } const isChecked = checkedIds.includes(row.id); return ( >({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_8/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_8/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_8/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_8/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_8/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/app/(main)/COMPANY_8/sales/order/page.tsx b/frontend/app/(main)/COMPANY_8/sales/order/page.tsx index 8f137c7a..76d6a061 100644 --- a/frontend/app/(main)/COMPANY_8/sales/order/page.tsx +++ b/frontend/app/(main)/COMPANY_8/sales/order/page.tsx @@ -1013,7 +1013,19 @@ export default function SalesOrderPage() { ) : ( - filteredFlatRows.map((row) => { + ts.groupData(filteredFlatRows).map((row: any) => { + // ๊ทธ๋ฃน ์š”์•ฝ ํ–‰ ๋ Œ๋”๋ง + if (row._isGroupSummary) { + return ( + + + {row._groupLabel || "ํ•ฉ๊ณ„"}: {row._count ? `${row._count}๊ฑด` : ""} + {row.qty ? ` ยท ์ˆ˜๋Ÿ‰ ${Number(row.qty).toLocaleString()}` : ""} + {row.amount ? ` ยท ๊ธˆ์•ก ${Number(row.amount).toLocaleString()}` : ""} + + + ); + } const isChecked = checkedIds.includes(row.id); return ( >({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ํŠธ๋ฆฌ ๊ตฌ์„ฑ const buildTree = (flatDepts: any[]): DeptNode[] => { @@ -320,14 +323,39 @@ export default function CompanyPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -337,6 +365,7 @@ export default function CompanyPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์˜ˆ์š”."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์˜ˆ์š”."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -690,9 +719,31 @@ export default function CompanyPage() {
- - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID" className="h-9" disabled={userEditMode} /> + +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID" className="h-9 flex-1" disabled={userEditMode} /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_9/master-data/department/page.tsx b/frontend/app/(main)/COMPANY_9/master-data/department/page.tsx index 23f70f2b..6eb4c2f2 100644 --- a/frontend/app/(main)/COMPANY_9/master-data/department/page.tsx +++ b/frontend/app/(main)/COMPANY_9/master-data/department/page.tsx @@ -80,6 +80,9 @@ export default function DepartmentPage() { const [userEditMode, setUserEditMode] = useState(false); const [userForm, setUserForm] = useState>({}); const [formErrors, setFormErrors] = useState>({}); + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ ์ƒํƒœ + const [idChecked, setIdChecked] = useState(false); + const [idChecking, setIdChecking] = useState(false); // ์‚ฌ์› ํƒญ (์žฌ์ง์ค‘/ํ‡ด์‚ฌ) const [memberTab, setMemberTab] = useState<"active" | "resigned">("active"); @@ -237,14 +240,39 @@ export default function DepartmentPage() { if (editData) { setUserEditMode(true); setUserForm({ ...editData, user_password: "" }); + setIdChecked(true); } else { setUserEditMode(false); setUserForm({ dept_code: selectedDeptCode || "", user_password: "" }); + setIdChecked(false); } setFormErrors({}); setUserModalOpen(true); }; + // ์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ + const handleCheckUserId = async () => { + const uid = (userForm.user_id || "").trim(); + if (!uid) { toast.error("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”."); return; } + setIdChecking(true); + try { + const res = await apiClient.post("/admin/users/check-duplicate", { userId: uid }); + const isDuplicate = res.data?.data?.isDuplicate; + if (isDuplicate) { + toast.error("์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ธ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(false); + } else { + toast.success("์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฌ์šฉ์ž ID์ž…๋‹ˆ๋‹ค."); + setIdChecked(true); + } + } catch { + toast.error("์ค‘๋ณต ํ™•์ธ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + setIdChecked(false); + } finally { + setIdChecking(false); + } + }; + const handleUserFormChange = (field: string, value: string) => { const formatted = formatField(field, value); setUserForm((prev) => ({ ...prev, [field]: formatted })); @@ -254,6 +282,7 @@ export default function DepartmentPage() { const handleUserSave = async () => { if (!userForm.user_id) { toast.error("์‚ฌ์šฉ์ž ID๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } + if (!userEditMode && !idChecked) { toast.error("์‚ฌ์šฉ์ž ID ์ค‘๋ณต ํ™•์ธ์„ ํ•ด์ฃผ์„ธ์š”."); return; } if (!userForm.user_name) { toast.error("์‚ฌ์šฉ์ž ์ด๋ฆ„์€ ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } if (!userForm.dept_code) { toast.error("๋ถ€์„œ๋Š” ํ•„์ˆ˜์ž…๋‹ˆ๋‹ค."); return; } const errors = validateForm(userForm, ["cell_phone", "email"]); @@ -633,14 +662,34 @@ export default function DepartmentPage() {
์‚ฌ์šฉ์ž ID * + {!userEditMode && idChecked && ( + โœ“ ์‚ฌ์šฉ ๊ฐ€๋Šฅ + )} - setUserForm((p) => ({ ...p, user_id: e.target.value }))} - placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" - className="h-9" - disabled={userEditMode} - /> +
+ { + setUserForm((p) => ({ ...p, user_id: e.target.value })); + if (!userEditMode) setIdChecked(false); + }} + placeholder="์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•ด ์ฃผ์„ธ์š”" + className="h-9 flex-1" + disabled={userEditMode} + /> + {!userEditMode && ( + + )} +
diff --git a/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx b/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx index 33159c85..a2946e89 100644 --- a/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx +++ b/frontend/app/(main)/COMPANY_9/sales/customer/page.tsx @@ -239,7 +239,7 @@ export default function CustomerManagementPage() { })); const res = await apiClient.post(`/table-management/tables/${CUSTOMER_TABLE}/data`, { - page: 1, size: 500, + page: 1, size: 99999, dataFilter: filters.length > 0 ? { enabled: true, filters } : undefined, autoFilter: true, sort: { columnName: "customer_code", order: "desc" }, diff --git a/frontend/lib/api/packaging.ts b/frontend/lib/api/packaging.ts index 81c67d5f..016e7d49 100644 --- a/frontend/lib/api/packaging.ts +++ b/frontend/lib/api/packaging.ts @@ -18,6 +18,7 @@ export interface PkgUnit { remarks: string | null; created_date: string; writer: string | null; + item_number?: string | null; } export interface PkgUnitItem { @@ -48,6 +49,7 @@ export interface LoadingUnit { remarks: string | null; created_date: string; writer: string | null; + item_number?: string | null; } export interface LoadingUnitPkg {