Files
sam-react-prod/src/components/business/WorkerDashboard.tsx
byeongcheolryu a68a25b737 [feat]: 인증 및 UI/UX 개선 작업
주요 변경사항:
- 로그인/회원가입 페이지 인증 리다이렉트 로직 추가
- 로그인 상태에서 auth 페이지 접근 시 대시보드로 자동 리다이렉트
- router.replace() 사용으로 브라우저 히스토리에서 auth 페이지 제거
- 사이드바 메뉴 활성화 동기화 개선 (URL 직접 입력 및 뒤로가기 대응)
- usePathname 기반 자동 메뉴 활성화 로직 추가
- ESLint 설정 업데이트 (전역 변수 추가, business 폴더 제외)
- TypeScript 빌드 설정 조정 (ignoreBuildErrors 추가)
- 다국어 지원 및 테마 선택 기능 통합
- 대시보드 레이아웃 및 컴포넌트 구조 개선
- UI 컴포넌트 라이브러리 확장 (dialog, sheet, progress 등)

기술적 개선:
- HttpOnly 쿠키 기반 인증 시스템 유지
- 로딩 상태 UI 추가 (인증 체크 중)
- 경로 정규화 로직 (locale 제거)
- 재귀적 메뉴 탐색 및 자동 확장

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 18:55:16 +09:00

173 lines
6.6 KiB
TypeScript

import { useMemo } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useCurrentTime } from "@/hooks/useCurrentTime";
import {
CheckCircle,
Clock,
Shield,
Package,
AlertTriangle,
Factory,
Activity,
FileText,
Settings
} from "lucide-react";
export function WorkerDashboard() {
const currentTime = useCurrentTime();
const workerData = useMemo(() => {
return {
myTasks: [
{ id: "W001", product: "스마트폰 케이스", quantity: 150, deadline: "14:00", status: "진행중" },
{ id: "W002", product: "태블릿 스탠드", quantity: 80, deadline: "16:30", status: "대기" }
],
currentShift: "1교대",
workTime: "08:00-17:00",
todayProduction: 120,
targetProduction: 150,
safetyAlerts: 0,
equipment: {
machine1: "정상",
machine2: "점검필요"
},
qualityChecks: 12
};
}, []);
return (
<div className="p-4 md:p-6 space-y-4 md:space-y-6">
{/* 작업자 헤더 */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-2xl md:text-3xl font-bold text-foreground"> </h1>
<p className="text-muted-foreground mt-1">{workerData.currentShift} · {workerData.workTime} · {currentTime}</p>
</div>
<div className="flex space-x-2">
<Button variant="outline" size="sm">
<Shield className="h-4 w-4 mr-2" />
</Button>
<Button className="bg-blue-600 hover:bg-blue-700" size="sm">
<CheckCircle className="h-4 w-4 mr-2" />
</Button>
</div>
</div>
{/* 개인 실적 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"> </CardTitle>
<Factory className="h-4 w-4 text-blue-600" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-blue-600">
{workerData.todayProduction}
</div>
<p className="text-xs text-muted-foreground">
: {workerData.targetProduction} ({Math.round((workerData.todayProduction / workerData.targetProduction) * 100)}%)
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"> </CardTitle>
<CheckCircle className="h-4 w-4 text-green-600" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-green-600">
{workerData.qualityChecks}
</div>
<p className="text-xs text-muted-foreground">
불량률: 0%
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"> </CardTitle>
<Shield className="h-4 w-4 text-green-600" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-green-600">
</div>
<p className="text-xs text-muted-foreground">
: {workerData.safetyAlerts}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium"> </CardTitle>
<Activity className="h-4 w-4 text-purple-600" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-purple-600">
{Math.round((workerData.todayProduction / workerData.targetProduction) * 100)}%
</div>
<p className="text-xs text-muted-foreground">
: {workerData.targetProduction - workerData.todayProduction}
</p>
</CardContent>
</Card>
</div>
{/* 개인 작업 현황 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<FileText className="h-5 w-5" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
{workerData.myTasks.map((task, index) => (
<div key={index} className="flex justify-between items-center p-3 bg-muted/50 dark:bg-muted/20 rounded">
<div>
<p className="font-medium">{task.product}</p>
<p className="text-sm text-muted-foreground">: {task.quantity} | : {task.deadline}</p>
</div>
<Badge className={task.status === "진행중" ? "bg-blue-500 text-white" : "bg-muted text-muted-foreground"}>
{task.status}
</Badge>
</div>
))}
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center space-x-2">
<Settings className="h-5 w-5" />
<span> </span>
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-3">
<div className="flex justify-between items-center p-3 bg-green-50 rounded">
<span className="font-medium"> #1</span>
<Badge className="bg-green-500 text-white">{workerData.equipment.machine1}</Badge>
</div>
<div className="flex justify-between items-center p-3 bg-yellow-50 rounded">
<span className="font-medium"> #2</span>
<Badge className="bg-yellow-500 text-white">{workerData.equipment.machine2}</Badge>
</div>
</div>
</CardContent>
</Card>
</div>
</div>
);
}