diff --git a/db_fix_data.js b/db_fix_data.js new file mode 100644 index 0000000..92fb2b4 --- /dev/null +++ b/db_fix_data.js @@ -0,0 +1,49 @@ +import mysql from 'mysql2/promise'; +import dotenv from 'dotenv'; + +dotenv.config(); + +const { DB_HOST, DB_USER, DB_PASS, DB_NAME, DB_PORT } = process.env; + +async function migrateData() { + const connection = await mysql.createConnection({ + host: DB_HOST, + user: DB_USER, + password: DB_PASS, + database: DB_NAME, + port: parseInt(DB_PORT || '3306') + }); + + console.log('๐Ÿ”„ ๊ธฐ์กด ๋ฐ์ดํ„ฐ ๋ณด์ • ์‹œ์ž‘ (์ƒ์„ธ์œ ํ˜• = ์œ ํ˜•)...'); + + const tables = ['pc_assets', 'server_assets', 'storage_assets', 'equip_assets', 'mobile_assets']; + + for (const table of tables) { + // 1. ์œ ํ˜•(type)์ด ๋น„์–ด์žˆ๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ๊ฐ’ ์ฑ„์šฐ๊ธฐ (๋ณด์ • ์ „ ๋‹จ๊ณ„) + let defaultType = '๊ธฐํƒ€'; + if (table === 'server_assets') defaultType = '์„œ๋ฒ„'; + else if (table === 'pc_assets') defaultType = '๊ฐœ์ธPC'; + else if (table === 'storage_assets') defaultType = '์Šคํ† ๋ฆฌ์ง€'; + else if (table === 'equip_assets') defaultType = '์ „์‚ฐ๋น„ํ’ˆ'; + else if (table === 'mobile_assets') defaultType = '๋ชจ๋ฐ”์ผ๊ธฐ๊ธฐ'; + + await connection.query(`UPDATE ${table} SET type = ? WHERE type IS NULL OR type = ''`, [defaultType]); + + // 2. ๊ฐœ์ธPC๊ฐ€ ์•„๋‹Œ ๋ฐ์ดํ„ฐ๋“ค์— ๋Œ€ํ•ด ์ƒ์„ธ์œ ํ˜• = ์œ ํ˜• ์—…๋ฐ์ดํŠธ + const [result] = await connection.query(` + UPDATE ${table} + SET detail_purpose = type + WHERE type NOT IN ('๊ฐœ์ธPC', 'PC') + `); + + console.log(`โœ… ${table}: ${result.affectedRows}๊ฐœ ๋ฐ์ดํ„ฐ ๋ณด์ • ์™„๋ฃŒ`); + } + + console.log('โœจ ๋ชจ๋“  ๊ธฐ์กด ๋ฐ์ดํ„ฐ ๋ณด์ •์ด ์™„๋ฃŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'); + await connection.end(); +} + +migrateData().catch(err => { + console.error('โŒ ๋ฐ์ดํ„ฐ ๋ณด์ • ์‹คํŒจ:', err); + process.exit(1); +}); diff --git a/db_init.js b/db_init.js index 0aa41e8..bc9daa1 100644 --- a/db_init.js +++ b/db_init.js @@ -32,7 +32,7 @@ async function initDB() { id VARCHAR(50) PRIMARY KEY, corp VARCHAR(100) COMMENT '๊ตฌ๋งค๋ฒ•์ธ', asset_code VARCHAR(100) COMMENT '์ž์‚ฐ๋ฒˆํ˜ธ', - purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์ผ์ž', + purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์—ฐ์›”', type VARCHAR(50) COMMENT '์œ ํ˜•', detail_purpose VARCHAR(50) COMMENT '์ƒ์„ธ์šฉ๋„', purpose VARCHAR(255) COMMENT '์šฉ๋„', @@ -57,6 +57,8 @@ async function initDB() { monitoring VARCHAR(100), price VARCHAR(100) COMMENT '๊ธˆ์•ก', remarks TEXT, + storage_location VARCHAR(255) COMMENT '๋ณด๊ด€์œ„์น˜', + status VARCHAR(50) COMMENT 'ํ˜„์žฌ์ƒํƒœ', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='${comment}'; `; @@ -77,7 +79,7 @@ async function initDB() { license_type VARCHAR(100) COMMENT '๋ผ์ด์„ ์Šค ์œ ํ˜•', quantity INT COMMENT '์ˆ˜๋Ÿ‰', price VARCHAR(100) COMMENT '๊ธˆ์•ก', - purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์ผ', + purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์—ฐ์›”', expiry_date VARCHAR(50) COMMENT '๋งŒ๋ฃŒ์ผ', vendor VARCHAR(255) COMMENT '๋‚ฉํ’ˆ์—…์ฒด', remarks TEXT COMMENT '๋น„๊ณ ', @@ -95,7 +97,7 @@ async function initDB() { license_key VARCHAR(255) COMMENT '๋ผ์ด์„ ์Šค ํ‚ค', quantity INT COMMENT '์ˆ˜๋Ÿ‰', price VARCHAR(100) COMMENT '๊ธˆ์•ก', - purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์ผ', + purchase_date VARCHAR(50) COMMENT '๊ตฌ๋งค์—ฐ์›”', vendor VARCHAR(255) COMMENT '๋‚ฉํ’ˆ์—…์ฒด', remarks TEXT COMMENT '๋น„๊ณ ', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP diff --git a/server.js b/server.js index 22cd962..c5d0b95 100644 --- a/server.js +++ b/server.js @@ -90,22 +90,48 @@ const hardwareInsertSQL = (table) => ` `; const getHardwareValues = (a) => [ - a.id, a.๋ฒ•์ธ||'', a.์ž์‚ฐ์ฝ”๋“œ||'', a.๊ตฌ๋งค์ผ||'', a.type||'', a.์ƒ์„ธ์šฉ๋„||'', a.์šฉ๋„||'', a.์ƒ์„ธ||'', + a.id, a.๋ฒ•์ธ||'', a.์ž์‚ฐ์ฝ”๋“œ||'', a.๊ตฌ๋งค์—ฐ์›”||'', a.type||'', a.์ƒ์„ธ์šฉ๋„||'', a.์šฉ๋„||'', a.์ƒ์„ธ||'', a.ํ˜„์‚ฌ์šฉ์กฐ์ง||'', a.์ด์ „์‚ฌ์šฉ์กฐ์ง||'', a.์œ„์น˜||'', a.๋‹ด๋‹น์ž_์ •||'', a.๋‹ด๋‹น์ž_๋ถ€||'', a.IP์ฃผ์†Œ||'', a.์›๊ฒฉ์ ‘์†||'', a.์„œ๋ฒ„ID||'', a.์„œ๋ฒ„PW||'', a.๋ชจ๋ธ๋ช…||'', a.OS||'', a.CPU||'', a.RAM||'', a.GPU||'', a.SSD1||'', a.SSD2||'', a.HDD1||'', a.๋ชจ๋‹ˆํ„ฐ๋ง||'', a.๊ธˆ์•ก||'', a.๋น„๊ณ ||'', a.๋ณด๊ด€์œ„์น˜||'', a.ํ˜„์žฌ์ƒํƒœ||'' ]; -const mapHardware = (r, defaultType) => ({ - id: r.id, ๋ฒ•์ธ: r.corp, ์ž์‚ฐ์ฝ”๋“œ: r.asset_code, ๊ตฌ๋งค์ผ: r.purchase_date, type: r.type || defaultType, - ์ƒ์„ธ์šฉ๋„: r.detail_purpose, ์šฉ๋„: r.purpose, ์ƒ์„ธ: r.details, ํ˜„์‚ฌ์šฉ์กฐ์ง: r.current_org, - ์ด์ „์‚ฌ์šฉ์กฐ์ง: r.prev_org, ์œ„์น˜: r.location, ๋‹ด๋‹น์ž_์ •: r.manager_main, ๋‹ด๋‹น์ž_๋ถ€: r.manager_sub, - IP์ฃผ์†Œ: r.ip_address, ์›๊ฒฉ์ ‘์†: r.remote_tool, ์„œ๋ฒ„ID: r.server_id, ์„œ๋ฒ„PW: r.server_pw, - ๋ชจ๋ธ๋ช…: r.model_name, OS: r.os, CPU: r.cpu, RAM: r.ram, GPU: r.gpu, SSD1: r.storage1, - SSD2: r.storage2, HDD1: r.storage3, ๋ชจ๋‹ˆํ„ฐ๋ง: r.monitoring, ๊ธˆ์•ก: r.price, ๋น„๊ณ : r.remarks, - ๋ณด๊ด€์œ„์น˜: r.storage_location, ํ˜„์žฌ์ƒํƒœ: r.status -}); +const mapHardware = (r, defaultType) => { + const type = r.type || defaultType; + return { + id: r.id, + ๋ฒ•์ธ: r.corp, + ์ž์‚ฐ์ฝ”๋“œ: r.asset_code, + ๊ตฌ๋งค์—ฐ์›”: r.purchase_date, + type: type, + ์ƒ์„ธ์šฉ๋„: (type !== '๊ฐœ์ธPC' && !r.detail_purpose) ? type : r.detail_purpose, + ์šฉ๋„: r.purpose, + ์ƒ์„ธ: r.details, + ํ˜„์‚ฌ์šฉ์กฐ์ง: r.current_org, + ์ด์ „์‚ฌ์šฉ์กฐ์ง: r.prev_org, + ์œ„์น˜: r.location, + ๋‹ด๋‹น์ž_์ •: r.manager_main, + ๋‹ด๋‹น์ž_๋ถ€: r.manager_sub, + IP์ฃผ์†Œ: r.ip_address, + ์›๊ฒฉ์ ‘์†: r.remote_tool, + ์„œ๋ฒ„ID: r.server_id, + ์„œ๋ฒ„PW: r.server_pw, + ๋ชจ๋ธ๋ช…: r.model_name, + OS: r.os, + CPU: r.cpu, + RAM: r.ram, + GPU: r.gpu, + SSD1: r.storage1, + SSD2: r.storage2, + HDD1: r.storage3, + ๋ชจ๋‹ˆํ„ฐ๋ง: r.monitoring, + ๊ธˆ์•ก: r.price, + ๋น„๊ณ : r.remarks, + ๋ณด๊ด€์œ„์น˜: r.storage_location, + ํ˜„์žฌ์ƒํƒœ: r.status + }; +}; // --- API ๋ผ์šฐํŠธ ์ •์˜ --- @@ -323,6 +349,34 @@ app.post('/api/sw-users/batch', async (req, res) => { } catch (err) { res.status(500).json({ error: err.message }); } }); +// ์ž์‚ฐ๋ฒˆํ˜ธ ์ž๋™ ์ƒ์„ฑ API +app.get('/api/generate-asset-code', async (req, res) => { + const { prefix } = req.query; + if (!prefix) return res.status(400).json({ error: 'Prefix is required' }); + + try { + const tables = ['pc_assets', 'server_assets', 'storage_assets', 'equip_assets', 'mobile_assets']; + let maxNum = 0; + + for (const table of tables) { + const [rows] = await pool.query( + `SELECT asset_code FROM ${table} WHERE asset_code LIKE ?`, + [`${prefix}%`] + ); + rows.forEach(r => { + const numPart = r.asset_code.replace(prefix, ''); + const num = parseInt(numPart); + if (!isNaN(num) && num > maxNum) maxNum = num; + }); + } + + const nextNum = (maxNum + 1).toString().padStart(3, '0'); + res.json({ nextCode: `${prefix}${nextNum}` }); + } catch (err) { + res.status(500).json({ error: err.message }); + } +}); + // ์ดˆ๊ธฐํ™” ๋ฐ ์„œ๋ฒ„ ๊ธฐ๋™ ensureTables().then(() => { app.listen(PORT, () => { diff --git a/src/components/Modal/DashboardDetailModal.ts b/src/components/Modal/DashboardDetailModal.ts index 4ea6f96..7c5fe94 100644 --- a/src/components/Modal/DashboardDetailModal.ts +++ b/src/components/Modal/DashboardDetailModal.ts @@ -49,7 +49,7 @@ export function openDashboardDetail(title: string, list: HardwareAsset[]) { if (!thead) return; titleEl.textContent = title; - thead.innerHTML = `No์œ ํ˜•์ž์‚ฐ์ฝ”๋“œ๋ช…์นญ/๋ชจ๋ธ์œ„์น˜๋‹ด๋‹น/์‚ฌ์šฉ์ž๊ตฌ๋งค์ผ๊ธˆ์•ก`; + thead.innerHTML = `No์œ ํ˜•์ž์‚ฐ์ฝ”๋“œ๋ช…์นญ/๋ชจ๋ธ์œ„์น˜๋‹ด๋‹น/์‚ฌ์šฉ์ž๊ตฌ๋งค์—ฐ์›”๊ธˆ์•ก`; tbody.innerHTML = ''; if (list.length === 0) { tbody.innerHTML = `ํ•ด๋‹น ์กฐ๊ฑด์˜ ์ž์‚ฐ์ด ์—†์Šต๋‹ˆ๋‹ค.`; diff --git a/src/components/Modal/HWModal.ts b/src/components/Modal/HWModal.ts index 02a7bc7..f0d53f1 100644 --- a/src/components/Modal/HWModal.ts +++ b/src/components/Modal/HWModal.ts @@ -1,406 +1,395 @@ -import { createIcons, Paperclip, History, Plus, X } from 'lucide'; -import { HardwareAsset, HardwareLog } from '../../core/state'; import { state, saveHardwareAsset, deleteHardwareAsset } from '../../core/state'; -import { setEditLock } from './ModalUtils'; +import { HardwareAsset, HardwareLog } from '../../core/excelHandler'; +import { closeModals } from './BaseModal'; +import { createIcons, History, Plus, X, Save, Edit2, RotateCcw, Paperclip } from 'lucide'; +import { CORP_LIST, ORG_LIST, HW_TYPE_LIST, LOCATION_DATA, TYPE_PREFIX_MAP } from './SharedData'; +import { + generateOptionsHTML, + setFieldValue, + getFieldValue, + parseAndSetLocation, + bindLocationEvents, + getCombinedLocation, + setEditLock, + createModalFrameHTML, + autoFillForm, + autoExtractForm +} from './ModalUtils'; let currentAsset: HardwareAsset | null = null; let isEditMode = false; -// ๊ณตํ†ต ์˜ต์…˜ ์ƒ์„ฑ ํ•จ์ˆ˜ -const generateOptionsHTML = (options: string[]) => - options.map(opt => ``).join(''); +const STATUS_LIST = ['์‚ฌ์šฉ์ค‘', '๋ณด๊ด€์ค‘', '์ˆ˜๋ฆฌ์ค‘', 'ํ๊ธฐ์˜ˆ์ •', '๊ธฐํƒ€']; -const ORG_LIST = ["๊ฒฝ์˜์ง€์›ํŒ€", "์ „๋žต๊ธฐํšํŒ€", "IT์šด์˜ํŒ€", "๊ณต์ •๊ธฐ์ˆ ํŒ€", "ํ’ˆ์งˆ๊ด€๋ฆฌํŒ€"]; -const HW_TYPE_LIST = ["์„œ๋ฒ„", "๋ฐ์Šคํฌํƒ‘", "๋…ธํŠธ๋ถ", "์›Œํฌ์Šคํ…Œ์ด์…˜", "ํƒœ๋ธ”๋ฆฟ", "๊ธฐํƒ€"]; -const STATUS_LIST = ["์‚ฌ์šฉ์ค‘", "์œ ํœด", "์ˆ˜๋ฆฌ์ค‘", "ํ๊ธฐ์˜ˆ์ •", "๊ธฐํƒ€"]; -const LOCATION_DATA: Record = { - "๋ณธ์‚ฌ": ["1์ธต ์ „์‚ฐ์‹ค", "2์ธต ์‚ฌ๋ฌด์‹ค", "3์ธต ํšŒ์˜์‹ค"], - "IDC": ["A๊ตฌ์—ญ ๋ž™ 01", "A๊ตฌ์—ญ ๋ž™ 02", "B๊ตฌ์—ญ ๋ž™ 05"], - "๊ณต์žฅ": ["์ƒ์‚ฐ๋ผ์ธ 1", "์ œ์–ด์‹ค", "์ฐฝ๊ณ "] +// ํ•„๋“œ ID โ†” ๋ฐ์ดํ„ฐ Key ๋งคํ•‘ (์„œ๋ฒ„ ์ „์šฉ ํ•„๋“œ ํฌํ•จ) +const HW_FIELD_MAP: Record = { + '์œ ํ˜•': 'type', + '๋ฒ•์ธ': '๋ฒ•์ธ', + '์ž์‚ฐ์ฝ”๋“œ': '์ž์‚ฐ์ฝ”๋“œ', + 'ํ˜„์‚ฌ์šฉ์กฐ์ง': '์‹ค์‚ฌ์šฉ์กฐ์ง', + '์ด์ „์‚ฌ์šฉ์กฐ์ง': '์ด์ „์‚ฌ์šฉ์กฐ์ง', + '์ƒ์„ธ์šฉ๋„': '์„œ๋ฒ„์šฉ๋„', // ์„œ๋ฒ„ ์ „์šฉ + '๋ชจ๋ธ๋ช…': '๋ชจ๋ธ๋ช…', + '๋ช…์นญ': '๋ช…์นญ', + '๋ณด๊ด€์œ„์น˜': '๋ณด๊ด€์œ„์น˜', + 'ํ˜„์žฌ์ƒํƒœ': '์ƒํƒœ', + 'IP์ฃผ์†Œ': 'IP์ฃผ์†Œ', + 'IP2': 'IP2', // ์„œ๋ฒ„ ์ „์šฉ + '์›๊ฒฉ์ ‘์†': '์›๊ฒฉ๋ฐฉ๋ฒ•', // ์„œ๋ฒ„ ์ „์šฉ + '์„œ๋ฒ„ID': '์„œ๋ฒ„ID', // ์„œ๋ฒ„ ์ „์šฉ + '์„œ๋ฒ„PW': '์„œ๋ฒ„PW', // ์„œ๋ฒ„ ์ „์šฉ + '๋ชจ๋‹ˆํ„ฐ๋ง': '๋ชจ๋‹ˆํ„ฐ๋ง', // ์„œ๋ฒ„ ์ „์šฉ + 'OS': 'OS', + 'CPU': 'CPU', + 'RAM': 'RAM', + 'SSD1': 'SSD1', + 'SSD2': 'SSD2', + 'HW์‚ฌ์–‘': 'HW์‚ฌ์–‘', + '๋‹ด๋‹น์ž_์ •': '๊ด€๋ฆฌ์กฐ์ง', + '๊ตฌ๋งค์ผ': '๋„์ž…์ผ', + '๊ธˆ์•ก': '๊ตฌ๋งค๊ฐ€', + '๋น„๊ณ ': '๋น„๊ณ ', + '์‚ฌ์šฉ์ž': '์‚ฌ์šฉ์ž' }; -const HW_MODAL_HTML = ` -