Update cost-pdf.html: Implemented team-specific distribution ratios for rental, power, and general expenses.
This commit is contained in:
180
cost-pdf.html
180
cost-pdf.html
@@ -151,17 +151,22 @@
|
|||||||
|
|
||||||
const POOL_A_PROJECTS = ['총무 [26-관리-03]', '부서 공통 [26-관리-06]'];
|
const POOL_A_PROJECTS = ['총무 [26-관리-03]', '부서 공통 [26-관리-06]'];
|
||||||
const POOL_B_PROJECTS = ['관리[26-공통-01]', '생산[26-공통-02]', '인사 [26-관리-02]'];
|
const POOL_B_PROJECTS = ['관리[26-공통-01]', '생산[26-공통-02]', '인사 [26-관리-02]'];
|
||||||
const TEAM_RATIOS = { '일반경비': { '철근': 0.45, '제작': 0.30, '공무': 0.25 } };
|
const TEAM_RATIOS = {
|
||||||
|
'지급임차료': { '철근': 0.13, '제작': 0.52, '공무': 0.35 },
|
||||||
|
'전력비': { '철근': 0.60, '제작': 0.30, '공무': 0.10 },
|
||||||
|
// 지급임차료/전력비 외 계정 + 풀 인건비는 일반경비로 본다.
|
||||||
|
'일반경비': { '철근': 0.45, '제작': 0.30, '공무': 0.25 }
|
||||||
|
};
|
||||||
const NON_MANAGED_FORMS = ['가족사지원', '공통', '기타', '시설관리', '연구개발', '현장자재', '현장지원', '공통(거더)', '공통(데크,가로보)', '품질'];
|
const NON_MANAGED_FORMS = ['가족사지원', '공통', '기타', '시설관리', '연구개발', '현장자재', '현장지원', '공통(거더)', '공통(데크,가로보)', '품질'];
|
||||||
const FORM_ALLOC_C_RULES = {
|
const FORM_ALLOC_C_RULES = {
|
||||||
'가족사지원': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'가족사지원': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'공통': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'공통': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'기타': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'기타': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'시설관리': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'시설관리': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'연구개발': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'연구개발': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'현장자재': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'현장자재': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'현장지원': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
'현장지원': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '거푸집', '가로보', '가설벤트'],
|
||||||
'공통(거더)': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'DR.거더', 'DR.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더'],
|
'공통(거더)': ['노출거더', '분절거더', '철도교', 'DPC.거더', 'RFI.거더', 'RFI.거더(v2.0)', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', 'SP.거더'],
|
||||||
'공통(데크,가로보)': ['강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '가로보'],
|
'공통(데크,가로보)': ['강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '가로보'],
|
||||||
'품질': ['노출거더', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '가로보']
|
'품질': ['노출거더', 'Inverted-T', 'Post-Tension 거더', 'Pre Beam', '강교 Deck', '캔틸레버Deck', 'Full-DepthDeck', 'PSCDeck', 'RCDeck', '가로보']
|
||||||
};
|
};
|
||||||
@@ -276,13 +281,19 @@
|
|||||||
const t = String(teamRaw || '').trim();
|
const t = String(teamRaw || '').trim();
|
||||||
if (!t) return '공통';
|
if (!t) return '공통';
|
||||||
if (t.includes('관리')) return '관리팀';
|
if (t.includes('관리')) return '관리팀';
|
||||||
if (t.includes('공무')) return '공무';
|
if (t.includes('공무')) return '공무팀';
|
||||||
if (t.includes('제작')) return '제작';
|
if (t.includes('제작')) return '제작팀';
|
||||||
if (t.includes('철근')) return '철근';
|
if (t.includes('철근')) return '철근팀';
|
||||||
if (t.includes('공통')) return '공통';
|
if (t.includes('공통')) return '공통';
|
||||||
if (t.includes('일용')) return '공통';
|
if (t.includes('일용')) return '공통';
|
||||||
return '공통';
|
return '공통';
|
||||||
};
|
};
|
||||||
|
const formatTeamLabel = (teamName = '') => {
|
||||||
|
const t = String(teamName || '').trim();
|
||||||
|
if (!t) return '공통팀';
|
||||||
|
if (t.endsWith('팀')) return t;
|
||||||
|
return `${t}팀`;
|
||||||
|
};
|
||||||
|
|
||||||
const mapExpenseRows = (data) => data.flatMap((r, i) => {
|
const mapExpenseRows = (data) => data.flatMap((r, i) => {
|
||||||
const account = String(r['계정'] || r['소계정명'] || '미분류').trim();
|
const account = String(r['계정'] || r['소계정명'] || '미분류').trim();
|
||||||
@@ -395,9 +406,9 @@
|
|||||||
const n = String(name || '').trim();
|
const n = String(name || '').trim();
|
||||||
const r = String(regularType || '').trim();
|
const r = String(regularType || '').trim();
|
||||||
if (r.includes('일용')) return '공통';
|
if (r.includes('일용')) return '공통';
|
||||||
if (teamMfg.has(n)) return '제작';
|
if (teamMfg.has(n)) return '제작팀';
|
||||||
if (teamAdmin.has(n)) return '공무';
|
if (teamAdmin.has(n)) return '공무팀';
|
||||||
return '철근';
|
return '철근팀';
|
||||||
};
|
};
|
||||||
const toNum = (v) => {
|
const toNum = (v) => {
|
||||||
const n = parseFloat(String(v || '').replace(/[^0-9.-]/g, ''));
|
const n = parseFloat(String(v || '').replace(/[^0-9.-]/g, ''));
|
||||||
@@ -463,9 +474,9 @@
|
|||||||
rate: w.rate,
|
rate: w.rate,
|
||||||
team: String(w.regularType || '').includes('일용')
|
team: String(w.regularType || '').includes('일용')
|
||||||
? '공통'
|
? '공통'
|
||||||
: teamMfg.has(w.name) ? '제작'
|
: teamMfg.has(w.name) ? '제작팀'
|
||||||
: teamAdmin.has(w.name) ? '공무'
|
: teamAdmin.has(w.name) ? '공무팀'
|
||||||
: '철근'
|
: '철근팀'
|
||||||
}));
|
}));
|
||||||
setFactoryWorkers(fallbackWorkers);
|
setFactoryWorkers(fallbackWorkers);
|
||||||
applyFactoryDefaults(fallbackWorkers);
|
applyFactoryDefaults(fallbackWorkers);
|
||||||
@@ -721,25 +732,114 @@
|
|||||||
Object.entries(p.byTeam).forEach(([tm, val]) => {
|
Object.entries(p.byTeam).forEach(([tm, val]) => {
|
||||||
if (!tmMap[tm]) tmMap[tm] = { name: tm, direct: 0, allocA: 0, allocB: 0, final: 0, hours: 0, breakdown: {}, allocTrace: [] };
|
if (!tmMap[tm]) tmMap[tm] = { name: tm, direct: 0, allocA: 0, allocB: 0, final: 0, hours: 0, breakdown: {}, allocTrace: [] };
|
||||||
tmMap[tm].direct += val;
|
tmMap[tm].direct += val;
|
||||||
const ratio = p.direct > 0 ? val / p.direct : 0;
|
tmMap[tm].hours += p.direct > 0 ? (p.hours * (val / p.direct)) : 0;
|
||||||
const shareHours = p.hours * ratio;
|
|
||||||
const shareAllocA = p.allocA * ratio;
|
|
||||||
const shareAllocB = p.allocB * ratio;
|
|
||||||
tmMap[tm].allocA += shareAllocA;
|
|
||||||
tmMap[tm].allocB += shareAllocB;
|
|
||||||
tmMap[tm].hours += shareHours;
|
|
||||||
tmMap[tm].breakdown[p.name] = (tmMap[tm].breakdown[p.name] || 0) + val;
|
tmMap[tm].breakdown[p.name] = (tmMap[tm].breakdown[p.name] || 0) + val;
|
||||||
tmMap[tm].allocTrace.push({
|
|
||||||
projectName: p.name,
|
|
||||||
directShare: val,
|
|
||||||
ratio,
|
|
||||||
projectHours: p.hours,
|
|
||||||
shareHours,
|
|
||||||
allocA: shareAllocA,
|
|
||||||
allocB: shareAllocB
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const ensureTeam = (name) => {
|
||||||
|
if (!tmMap[name]) tmMap[name] = { name, direct: 0, allocA: 0, allocB: 0, final: 0, hours: 0, breakdown: {}, allocTrace: [] };
|
||||||
|
return tmMap[name];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 팀별 하이브리드 배분:
|
||||||
|
// 1) 공통 성격 풀은 팀 공수 비례
|
||||||
|
// 2) 관리 성격 풀은 계정별 고정비율
|
||||||
|
const commonPool = { A: 0, B: 0 };
|
||||||
|
const bucketA = { '지급임차료': 0, '전력비': 0, '일반경비': 0 };
|
||||||
|
const bucketB = { '지급임차료': 0, '전력비': 0, '일반경비': 0 };
|
||||||
|
const classifyAccount = (account) => {
|
||||||
|
const raw = String(account || '');
|
||||||
|
const compact = raw.replace(/\s+/g, '').toLowerCase();
|
||||||
|
const digits = compact.replace(/[^0-9]/g, '');
|
||||||
|
if (digits === '819' || digits === '81901') return '지급임차료';
|
||||||
|
if (compact.includes('전력비') || compact.includes('전기료') || compact.includes('815')) return '전력비';
|
||||||
|
return '일반경비';
|
||||||
|
};
|
||||||
|
const isCommonPoolProject = (projectName) => String(projectName || '').includes('공통');
|
||||||
|
|
||||||
|
fEx.forEach(e => {
|
||||||
|
const bucket = classifyAccount(e.account);
|
||||||
|
if (isA(e.projectName)) {
|
||||||
|
if (isCommonPoolProject(e.projectName)) commonPool.A += e.amount;
|
||||||
|
else bucketA[bucket] += e.amount;
|
||||||
|
} else if (isB(e.projectName)) {
|
||||||
|
if (isCommonPoolProject(e.projectName)) commonPool.B += e.amount;
|
||||||
|
else bucketB[bucket] += e.amount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
laborWithCost.forEach(l => {
|
||||||
|
if (isA(l.projectName)) {
|
||||||
|
if (isCommonPoolProject(l.projectName)) commonPool.A += l.cost;
|
||||||
|
else bucketA['일반경비'] += l.cost;
|
||||||
|
} else if (isB(l.projectName)) {
|
||||||
|
if (isCommonPoolProject(l.projectName)) commonPool.B += l.cost;
|
||||||
|
else bucketB['일반경비'] += l.cost;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 토글 OFF 상태일 때 분배 제외
|
||||||
|
if (!allocPoolA) commonPool.A = 0;
|
||||||
|
if (!allocPoolB) commonPool.B = 0;
|
||||||
|
if (!allocPoolA) Object.keys(bucketA).forEach(k => { bucketA[k] = 0; });
|
||||||
|
if (!allocPoolB) Object.keys(bucketB).forEach(k => { bucketB[k] = 0; });
|
||||||
|
|
||||||
|
const teamHoursMap = {};
|
||||||
|
let totalTeamHours = 0;
|
||||||
|
laborWithCost.forEach(l => {
|
||||||
|
if (isA(l.projectName) || isB(l.projectName)) return;
|
||||||
|
const t = normalizeTeamName(l.team);
|
||||||
|
if (t === '관리팀') return;
|
||||||
|
teamHoursMap[t] = (teamHoursMap[t] || 0) + (Number(l.hours) || 0);
|
||||||
|
totalTeamHours += (Number(l.hours) || 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
const distributeCommonByHours = (poolName, amount, allocKey) => {
|
||||||
|
if (!amount || totalTeamHours <= 0) return;
|
||||||
|
Object.entries(teamHoursMap).forEach(([team, hrs]) => {
|
||||||
|
const ratio = hrs / totalTeamHours;
|
||||||
|
const share = amount * ratio;
|
||||||
|
const row = ensureTeam(team);
|
||||||
|
row[allocKey] += share;
|
||||||
|
row.allocTrace.push({
|
||||||
|
projectName: `${poolName}/공통(공수)`,
|
||||||
|
directShare: amount,
|
||||||
|
ratio,
|
||||||
|
projectHours: totalTeamHours,
|
||||||
|
shareHours: hrs,
|
||||||
|
allocA: allocKey === 'allocA' ? share : 0,
|
||||||
|
allocB: allocKey === 'allocB' ? share : 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const distributeManagedByRatio = (poolName, bucketMap, allocKey) => {
|
||||||
|
Object.entries(bucketMap).forEach(([bucket, amount]) => {
|
||||||
|
if (!amount) return;
|
||||||
|
const ratios = TEAM_RATIOS[bucket] || TEAM_RATIOS['일반경비'];
|
||||||
|
Object.entries(ratios).forEach(([team, ratio]) => {
|
||||||
|
const share = amount * ratio;
|
||||||
|
const row = ensureTeam(team);
|
||||||
|
row[allocKey] += share;
|
||||||
|
row.allocTrace.push({
|
||||||
|
projectName: `${poolName}/${bucket}`,
|
||||||
|
directShare: amount,
|
||||||
|
ratio,
|
||||||
|
projectHours: 0,
|
||||||
|
shareHours: 0,
|
||||||
|
allocA: allocKey === 'allocA' ? share : 0,
|
||||||
|
allocB: allocKey === 'allocB' ? share : 0
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
distributeCommonByHours('POOL A', commonPool.A, 'allocA');
|
||||||
|
distributeCommonByHours('POOL B', commonPool.B, 'allocB');
|
||||||
|
distributeManagedByRatio('POOL A', bucketA, 'allocA');
|
||||||
|
distributeManagedByRatio('POOL B', bucketB, 'allocB');
|
||||||
|
|
||||||
return Object.values(tmMap).map(t => ({
|
return Object.values(tmMap).map(t => ({
|
||||||
...t,
|
...t,
|
||||||
final: t.direct + t.allocA + t.allocB,
|
final: t.direct + t.allocA + t.allocB,
|
||||||
@@ -779,14 +879,14 @@
|
|||||||
if (dailyWorkers.has(name) || String(name).includes('일용')) {
|
if (dailyWorkers.has(name) || String(name).includes('일용')) {
|
||||||
map[name] = '공통';
|
map[name] = '공통';
|
||||||
} else {
|
} else {
|
||||||
map[name] = teamMfg.has(name) ? '제작' : teamAdmin.has(name) ? '공무' : '철근';
|
map[name] = teamMfg.has(name) ? '제작팀' : teamAdmin.has(name) ? '공무팀' : '철근팀';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return map;
|
return map;
|
||||||
}, [factoryWorkers, wageSettings, laborRows]);
|
}, [factoryWorkers, wageSettings, laborRows]);
|
||||||
|
|
||||||
const factoryTeamCounts = useMemo(() => {
|
const factoryTeamCounts = useMemo(() => {
|
||||||
const base = { '철근': 0, '제작': 0, '공무': 0, '공통': 0, '관리팀': 0 };
|
const base = { '철근팀': 0, '제작팀': 0, '공무팀': 0, '공통': 0, '관리팀': 0 };
|
||||||
Object.values(workerTeamMap || {}).forEach(team => {
|
Object.values(workerTeamMap || {}).forEach(team => {
|
||||||
if (base[team] !== undefined) base[team] += 1;
|
if (base[team] !== undefined) base[team] += 1;
|
||||||
});
|
});
|
||||||
@@ -985,7 +1085,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{results.all.team.map(tm => (
|
{results.all.team.map(tm => (
|
||||||
<tr key={tm.name}>
|
<tr key={tm.name}>
|
||||||
<td className="font-bold">{tm.name} ({utils.formatHr(tm.hours)} HR)</td>
|
<td className="font-bold">{formatTeamLabel(tm.name)} ({utils.formatHr(tm.hours)} HR)</td>
|
||||||
<td className="text-right">{Math.round(tm.direct).toLocaleString()}</td>
|
<td className="text-right">{Math.round(tm.direct).toLocaleString()}</td>
|
||||||
{allocPoolA && <td className="text-right text-blue-700">{Math.round(tm.allocA).toLocaleString()}</td>}
|
{allocPoolA && <td className="text-right text-blue-700">{Math.round(tm.allocA).toLocaleString()}</td>}
|
||||||
{allocPoolB && <td className="text-right text-orange-700">{Math.round(tm.allocB).toLocaleString()}</td>}
|
{allocPoolB && <td className="text-right text-orange-700">{Math.round(tm.allocB).toLocaleString()}</td>}
|
||||||
@@ -1035,7 +1135,7 @@
|
|||||||
].map((c, i) => (
|
].map((c, i) => (
|
||||||
<div key={i} className="bg-white p-5 rounded-2xl border border-slate-200 shadow-sm transition-all hover:bg-slate-50/50">
|
<div key={i} className="bg-white p-5 rounded-2xl border border-slate-200 shadow-sm transition-all hover:bg-slate-50/50">
|
||||||
<p className="text-xs font-bold text-slate-400 uppercase tracking-widest mb-1">{c.label}</p>
|
<p className="text-xs font-bold text-slate-400 uppercase tracking-widest mb-1">{c.label}</p>
|
||||||
<p className={`text-xl font-extrabold ${c.color} tracking-tighter leathering-none`}>
|
<p className={`text-xl font-extrabold ${c.color} tracking-tighter leading-none`}>
|
||||||
{c.unit ? (c.unit === 'HR' ? utils.formatHr(c.val) : c.val.toLocaleString()) : utils.formatWon(c.val)}
|
{c.unit ? (c.unit === 'HR' ? utils.formatHr(c.val) : c.val.toLocaleString()) : utils.formatWon(c.val)}
|
||||||
{c.unit && <span className="text-sm font-bold ml-1 opacity-50">{c.unit}</span>}
|
{c.unit && <span className="text-sm font-bold ml-1 opacity-50">{c.unit}</span>}
|
||||||
</p>
|
</p>
|
||||||
@@ -1089,7 +1189,7 @@
|
|||||||
<div className="bg-violet-50 border border-violet-100 rounded-xl p-4 space-y-2">
|
<div className="bg-violet-50 border border-violet-100 rounded-xl p-4 space-y-2">
|
||||||
<div className="font-black text-violet-800">형식배분 (C)</div>
|
<div className="font-black text-violet-800">형식배분 (C)</div>
|
||||||
<div className="text-slate-700">원가관리 비대상 형식 원가를 관리대상 형식으로 규칙에 따라 재배분합니다.</div>
|
<div className="text-slate-700">원가관리 비대상 형식 원가를 관리대상 형식으로 규칙에 따라 재배분합니다.</div>
|
||||||
<div className="text-slate-500">적용 범위: 형식별 탭 전용 (교량별/팀별 미적용)</div>
|
<div className="text-blue-700 font-black">적용 범위: 형식별 탭 전용 (교량별/팀별 미적용)</div>
|
||||||
<div className="text-slate-500">비대상 형식은 형식별 리스트에서 숨김 처리됩니다.</div>
|
<div className="text-slate-500">비대상 형식은 형식별 리스트에서 숨김 처리됩니다.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1116,7 +1216,7 @@
|
|||||||
<tr key={item.name} className={`transition-all group ${isInvalid ? 'bg-red-50 hover:bg-red-100/30' : 'hover:bg-blue-50/20'}`}>
|
<tr key={item.name} className={`transition-all group ${isInvalid ? 'bg-red-50 hover:bg-red-100/30' : 'hover:bg-blue-50/20'}`}>
|
||||||
<td className="px-8 py-4 cursor-pointer" onClick={() => setSelectedDetail(item)}>
|
<td className="px-8 py-4 cursor-pointer" onClick={() => setSelectedDetail(item)}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<span className={`font-bold block transition-colors text-sm leading-tight ${isInvalid ? 'text-red-700' : 'text-slate-800 group-hover:text-blue-600'}`}>{item.name}</span>
|
<span className={`font-bold block transition-colors text-sm leading-tight ${isInvalid ? 'text-red-700' : 'text-slate-800 group-hover:text-blue-600'}`}>{viewMode === 'team' ? formatTeamLabel(item.name) : item.name}</span>
|
||||||
<span className="text-xs text-slate-400 font-bold mt-1 inline-flex items-center gap-1.5 bg-slate-100 px-1.5 py-0.5 rounded-md w-max">
|
<span className="text-xs text-slate-400 font-bold mt-1 inline-flex items-center gap-1.5 bg-slate-100 px-1.5 py-0.5 rounded-md w-max">
|
||||||
<Icon name="clock" size={10} /> {utils.formatHr(item.hours || 0)} HR
|
<Icon name="clock" size={10} /> {utils.formatHr(item.hours || 0)} HR
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user