refactor(WEB): WorkerScreen 코드 정리 및 최적화

- 중복 로직 제거 및 구조 개선 (-103줄)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
유병철
2026-02-06 16:15:48 +09:00
parent c2ed71540f
commit 666eb6bcc6

View File

@@ -232,14 +232,37 @@ interface SidebarOrder {
quantity: number;
shutterCount: number;
priority: 'urgent' | 'priority' | 'normal';
subType?: 'slat' | 'jointbar' | 'bending' | 'wip';
}
const MOCK_SIDEBAR_ORDERS: SidebarOrder[] = [
{ id: 'order-1', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'urgent' },
{ id: 'order-2', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'priority' },
{ id: 'order-3', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'normal' },
{ id: 'order-4', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'normal' },
];
// 스크린: subType 없음 / 슬랫: slat|jointbar / 절곡: bending|wip
const MOCK_SIDEBAR_ORDERS: Record<ProcessTab, SidebarOrder[]> = {
screen: [
{ id: 'order-s1', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'urgent' },
{ id: 'order-s2', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'priority' },
{ id: 'order-s3', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'normal' },
{ id: 'order-s4', siteName: '현장명', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'normal' },
],
slat: [
{ id: 'order-l1', siteName: '현장명A', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'urgent', subType: 'slat' },
{ id: 'order-l2', siteName: '현장명B', date: '2024-09-24', quantity: 3, shutterCount: 2, priority: 'priority', subType: 'jointbar' },
{ id: 'order-l3', siteName: '현장명C', date: '2024-09-24', quantity: 5, shutterCount: 4, priority: 'normal', subType: 'slat' },
{ id: 'order-l4', siteName: '현장명D', date: '2024-09-24', quantity: 4, shutterCount: 3, priority: 'normal', subType: 'jointbar' },
],
bending: [
{ id: 'order-b1', siteName: '현장명A', date: '2024-09-24', quantity: 7, shutterCount: 5, priority: 'urgent', subType: 'bending' },
{ id: 'order-b2', siteName: '현장명B', date: '2024-09-24', quantity: 3, shutterCount: 2, priority: 'priority', subType: 'wip' },
{ id: 'order-b3', siteName: '현장명C', date: '2024-09-24', quantity: 5, shutterCount: 4, priority: 'normal', subType: 'bending' },
{ id: 'order-b4', siteName: '현장명D', date: '2024-09-24', quantity: 4, shutterCount: 3, priority: 'normal', subType: 'wip' },
],
};
const SUB_TYPE_TAGS: Record<string, { label: string; className: string }> = {
slat: { label: '슬랫', className: 'bg-blue-100 text-blue-700' },
jointbar: { label: '조인트바', className: 'bg-purple-100 text-purple-700' },
bending: { label: '절곡', className: 'bg-amber-100 text-amber-700' },
wip: { label: '재공품', className: 'bg-orange-100 text-orange-700' },
};
const PRIORITY_GROUPS = [
{ key: 'urgent' as const, label: '긴급', color: 'text-red-600' },
@@ -811,13 +834,11 @@ export default function WorkerScreen() {
<div className="mt-2 p-3 border rounded-lg bg-white max-h-[300px] overflow-y-auto">
<SidebarContent
tab={tab}
slatSubMode={slatSubMode}
setSlatSubMode={setSlatSubMode}
bendingSubMode={bendingSubMode}
setBendingSubMode={setBendingSubMode}
selectedOrderId={selectedSidebarOrderId}
onSelectOrder={(id) => {
onSelectOrder={(id, subType) => {
setSelectedSidebarOrderId(id);
if (subType === 'slat' || subType === 'jointbar') setSlatSubMode(subType === 'jointbar' ? 'jointbar' : 'normal');
if (subType === 'bending' || subType === 'wip') setBendingSubMode(subType === 'wip' ? 'wip' : 'normal');
setIsSidebarOpen(false);
}}
/>
@@ -833,12 +854,12 @@ export default function WorkerScreen() {
<CardContent className="p-4">
<SidebarContent
tab={tab}
slatSubMode={slatSubMode}
setSlatSubMode={setSlatSubMode}
bendingSubMode={bendingSubMode}
setBendingSubMode={setBendingSubMode}
selectedOrderId={selectedSidebarOrderId}
onSelectOrder={setSelectedSidebarOrderId}
onSelectOrder={(id, subType) => {
setSelectedSidebarOrderId(id);
if (subType === 'slat' || subType === 'jointbar') setSlatSubMode(subType === 'jointbar' ? 'jointbar' : 'normal');
if (subType === 'bending' || subType === 'wip') setBendingSubMode(subType === 'wip' ? 'wip' : 'normal');
}}
/>
</CardContent>
</Card>
@@ -1072,110 +1093,58 @@ export default function WorkerScreen() {
// ===== 사이드바 컨텐츠 =====
interface SidebarContentProps {
tab: ProcessTab;
slatSubMode: 'normal' | 'jointbar';
setSlatSubMode: (mode: 'normal' | 'jointbar') => void;
bendingSubMode: 'normal' | 'wip';
setBendingSubMode: (mode: 'normal' | 'wip') => void;
selectedOrderId: string;
onSelectOrder: (id: string) => void;
onSelectOrder: (id: string, subType?: SidebarOrder['subType']) => void;
}
function SidebarContent({
tab,
slatSubMode,
setSlatSubMode,
bendingSubMode,
setBendingSubMode,
selectedOrderId,
onSelectOrder,
}: SidebarContentProps) {
const orders = MOCK_SIDEBAR_ORDERS[tab];
return (
<div className="space-y-3">
<h3 className="text-sm font-semibold text-gray-900"> </h3>
{/* 서브 탭: 슬랫 */}
{tab === 'slat' && (
<div className="flex items-center gap-1 p-1 bg-gray-100 rounded-lg">
<button
type="button"
onClick={() => setSlatSubMode('normal')}
className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
slatSubMode === 'normal'
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-500 hover:text-gray-700'
}`}
>
</button>
<button
type="button"
onClick={() => setSlatSubMode('jointbar')}
className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
slatSubMode === 'jointbar'
? 'bg-blue-500 text-white shadow-sm'
: 'text-gray-500 hover:text-gray-700'
}`}
>
</button>
</div>
)}
{/* 서브 탭: 절곡 */}
{tab === 'bending' && (
<div className="flex items-center gap-1 p-1 bg-gray-100 rounded-lg">
<button
type="button"
onClick={() => setBendingSubMode('normal')}
className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
bendingSubMode === 'normal'
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-500 hover:text-gray-700'
}`}
>
</button>
<button
type="button"
onClick={() => setBendingSubMode('wip')}
className={`flex-1 px-3 py-1.5 text-xs font-medium rounded-md transition-colors ${
bendingSubMode === 'wip'
? 'bg-orange-500 text-white shadow-sm'
: 'text-gray-500 hover:text-gray-700'
}`}
>
</button>
</div>
)}
{/* 우선순위별 작업지시 카드 */}
{/* 우선순위별 작업지시 카드 + 태그 */}
{PRIORITY_GROUPS.map((group) => {
const orders = MOCK_SIDEBAR_ORDERS.filter((o) => o.priority === group.key);
if (orders.length === 0) return null;
const groupOrders = orders.filter((o) => o.priority === group.key);
if (groupOrders.length === 0) return null;
return (
<div key={group.key}>
<p className={`text-xs font-semibold mb-1.5 ${group.color}`}>{group.label}</p>
<div className="space-y-1.5">
{orders.map((order) => (
<button
key={order.id}
type="button"
onClick={() => onSelectOrder(order.id)}
className={cn(
'w-full text-left p-2.5 rounded-lg border transition-colors text-xs',
selectedOrderId === order.id
? 'border-primary bg-primary/5 ring-1 ring-primary/20'
: 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'
)}
>
<p className="font-medium text-gray-900">{order.siteName}</p>
<div className="flex items-center justify-between mt-1 text-gray-500">
<span>{order.date}</span>
<span>{order.quantity}/{order.shutterCount}</span>
</div>
</button>
))}
{groupOrders.map((order) => {
const tag = order.subType ? SUB_TYPE_TAGS[order.subType] : null;
return (
<button
key={order.id}
type="button"
onClick={() => onSelectOrder(order.id, order.subType)}
className={cn(
'w-full text-left p-2.5 rounded-lg border transition-colors text-xs',
selectedOrderId === order.id
? 'border-primary bg-primary/5 ring-1 ring-primary/20'
: 'border-gray-200 hover:border-gray-300 hover:bg-gray-50'
)}
>
<div className="flex items-center justify-between">
<p className="font-medium text-gray-900">{order.siteName}</p>
{tag && (
<span className={`px-1.5 py-0.5 rounded text-[10px] font-semibold ${tag.className}`}>
{tag.label}
</span>
)}
</div>
<div className="flex items-center justify-between mt-1 text-gray-500">
<span>{order.date}</span>
<span>{order.quantity}/{order.shutterCount}</span>
</div>
</button>
);
})}
</div>
</div>
);