주요 변경사항: - 로그인/회원가입 페이지 인증 리다이렉트 로직 추가 - 로그인 상태에서 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>
173 lines
6.6 KiB
TypeScript
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>
|
|
);
|
|
}
|