feat(WEB): 차량 관리 기능 추가 및 CEO 대시보드 Enhanced 섹션 적용
차량 관리 (신규): - VehicleList/VehicleDetail: 차량 목록/상세 - ForkliftList/ForkliftDetail: 지게차 목록/상세 - VehicleLogList/VehicleLogDetail: 운행일지 목록/상세 - 관련 페이지 라우트 추가 (/vehicle-management/*) CEO 대시보드: - Enhanced 섹션 컴포넌트 적용 (아이콘 + 컬러 테마) - EnhancedStatusBoardSection, EnhancedDailyReportSection, EnhancedMonthlyExpenseSection - TodayIssueSection 개선 IntegratedDetailTemplate: - FieldInput, FieldRenderer 기능 확장 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 지게차 수정 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ForkliftDetail } from '@/components/vehicle-management/ForkliftDetail';
|
||||
import { getForkliftById } from '@/components/vehicle-management/ForkliftList/actions';
|
||||
import type { Forklift } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function ForkliftEditPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<Forklift | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getForkliftById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '지게차를 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <ForkliftDetail mode="edit" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 지게차 상세 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { ForkliftDetail } from '@/components/vehicle-management/ForkliftDetail';
|
||||
import { getForkliftById } from '@/components/vehicle-management/ForkliftList/actions';
|
||||
import type { Forklift } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function ForkliftDetailPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<Forklift | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getForkliftById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '지게차를 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <ForkliftDetail mode="view" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 지게차 등록 페이지
|
||||
*/
|
||||
|
||||
import { ForkliftDetail } from '@/components/vehicle-management/ForkliftDetail';
|
||||
|
||||
export default function ForkliftNewPage() {
|
||||
return <ForkliftDetail mode="create" />;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 지게차 관리 리스트 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ForkliftList } from '@/components/vehicle-management/ForkliftList';
|
||||
import { getForklifts } from '@/components/vehicle-management/ForkliftList/actions';
|
||||
import type { Forklift } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function ForkliftPage() {
|
||||
const [data, setData] = useState<Forklift[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
getForklifts()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
setData(result.data);
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <ForkliftList initialData={data} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량일지 수정 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { VehicleLogDetail } from '@/components/vehicle-management/VehicleLogDetail';
|
||||
import { getVehicleLogById } from '@/components/vehicle-management/VehicleLogList/actions';
|
||||
import type { VehicleLog } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehicleLogEditPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<VehicleLog | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getVehicleLogById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '차량일지를 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleLogDetail mode="edit" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량일지 상세 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { VehicleLogDetail } from '@/components/vehicle-management/VehicleLogDetail';
|
||||
import { getVehicleLogById } from '@/components/vehicle-management/VehicleLogList/actions';
|
||||
import type { VehicleLog } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehicleLogDetailPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<VehicleLog | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getVehicleLogById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '차량일지를 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleLogDetail mode="view" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량일지 등록 페이지
|
||||
*/
|
||||
|
||||
import { VehicleLogDetail } from '@/components/vehicle-management/VehicleLogDetail';
|
||||
|
||||
export default function VehicleLogNewPage() {
|
||||
return <VehicleLogDetail mode="create" />;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량일지/월간사진기록 리스트 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { VehicleLogList } from '@/components/vehicle-management/VehicleLogList';
|
||||
import { getVehicleLogs } from '@/components/vehicle-management/VehicleLogList/actions';
|
||||
import type { VehicleLog } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehicleLogPage() {
|
||||
const [data, setData] = useState<VehicleLog[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
getVehicleLogs()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
setData(result.data);
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleLogList initialData={data} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량 수정 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { VehicleDetail } from '@/components/vehicle-management/VehicleDetail';
|
||||
import { getVehicleById } from '@/components/vehicle-management/VehicleList/actions';
|
||||
import type { Vehicle } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehicleEditPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<Vehicle | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getVehicleById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '차량을 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleDetail mode="edit" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량 상세 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'next/navigation';
|
||||
import { VehicleDetail } from '@/components/vehicle-management/VehicleDetail';
|
||||
import { getVehicleById } from '@/components/vehicle-management/VehicleList/actions';
|
||||
import type { Vehicle } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehicleDetailPage() {
|
||||
const params = useParams();
|
||||
const id = params.id as string;
|
||||
|
||||
const [data, setData] = useState<Vehicle | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) return;
|
||||
|
||||
getVehicleById(id)
|
||||
.then((result) => {
|
||||
if (result.success && result.data) {
|
||||
setData(result.data);
|
||||
} else {
|
||||
setError(result.error || '데이터를 불러올 수 없습니다.');
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, [id]);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-red-500">{error || '차량을 찾을 수 없습니다.'}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleDetail mode="view" initialData={data} id={id} />;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량 등록 페이지
|
||||
*/
|
||||
|
||||
import { VehicleDetail } from '@/components/vehicle-management/VehicleDetail';
|
||||
|
||||
export default function VehicleNewPage() {
|
||||
return <VehicleDetail mode="create" />;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
'use client';
|
||||
|
||||
/**
|
||||
* 차량 관리 리스트 페이지
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { VehicleList } from '@/components/vehicle-management/VehicleList';
|
||||
import { getVehicles } from '@/components/vehicle-management/VehicleList/actions';
|
||||
import type { Vehicle } from '@/components/vehicle-management/types';
|
||||
|
||||
export default function VehiclePage() {
|
||||
const [data, setData] = useState<Vehicle[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
getVehicles()
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
setData(result.data);
|
||||
}
|
||||
})
|
||||
.finally(() => setIsLoading(false));
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[400px]">
|
||||
<div className="text-muted-foreground">로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <VehicleList initialData={data} />;
|
||||
}
|
||||
Reference in New Issue
Block a user