fix: [order] 수주관리 UI 개선 - 카드 재구성, stats 상태 추가, STOCK 제품 필터링

- 수주관리 카드 재구성 (수주/생산/출하)
- stats에 inProduction/produced 매핑 추가
- 수주관리 목록에서 재고생산(STOCK) 제품 필터링
This commit is contained in:
2026-03-18 15:28:48 +09:00
parent 3ec6fdd71b
commit 4e967540e9
2 changed files with 51 additions and 36 deletions

View File

@@ -4,7 +4,7 @@
* 수주관리 - IntegratedListTemplateV2 적용
*
* 수주 관리 페이지
* - 상단 통계 카드: 이번 달 수주, 분할 대기, 생산지시 대기, 출하 대기
* - 상단 통계 카드: N월 수주(기간 수주), 수주, 생산, 출하
* - 상태 필터: 셀렉트박스 (전체, 수주등록, N자수정, 수주확정, 생산지시완료)
* - 날짜 범위 필터: 달력
* - 완전한 반응형 지원
@@ -41,7 +41,7 @@ import {
TableCell,
} from "@/components/ui/table";
import { Checkbox } from "@/components/ui/checkbox";
import { formatAmount, formatAmountManwon } from "@/lib/utils/amount";
import { formatAmount } from "@/lib/utils/amount";
import { ListMobileCard, InfoField } from "@/components/organisms/MobileCard";
import { ConfirmDialog, DeleteConfirmDialog } from "@/components/ui/confirm-dialog";
import {
@@ -131,9 +131,16 @@ function OrderListContent() {
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 20;
// 날짜 범위 필터 상태
const [startDate, setStartDate] = useState<string>("");
const [endDate, setEndDate] = useState<string>("");
// 날짜 범위 필터 상태 (이번달 초기값)
const [startDate, setStartDate] = useState<string>(() => {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-01`;
});
const [endDate, setEndDate] = useState<string>(() => {
const d = new Date();
const last = new Date(d.getFullYear(), d.getMonth() + 1, 0);
return `${last.getFullYear()}-${String(last.getMonth() + 1).padStart(2, '0')}-${String(last.getDate()).padStart(2, '0')}`;
});
// 필터 상태
const [filterValues, setFilterValues] = useState<Record<string, string | string[]>>({
@@ -181,7 +188,7 @@ function OrderListContent() {
try {
setIsLoading(true);
const [ordersResult, statsResult] = await Promise.all([
getOrders(),
getOrders({ order_type: 'ORDER' }),
getOrderStats(),
]);
@@ -276,53 +283,57 @@ function OrderListContent() {
setMobileDisplayCount(20);
}, [searchTerm, filterValues]);
// 통계 계산 (API stats 우선 사용, 없으면 로컬 계산)
const now = new Date();
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
// 통계 계산 — 날짜 범위 내 데이터 기준
const periodLabel = useMemo(() => {
if (!startDate || !endDate) return '전체 수주';
const sm = startDate.slice(0, 7); // YYYY-MM
const em = endDate.slice(0, 7);
if (sm === em) {
return `${parseInt(startDate.slice(5, 7))}월 수주`;
}
return '기간 수주';
}, [startDate, endDate]);
// 이번 달 수주 금액 (수주확정 이후 건만 - 취소, 수주등록 제외)
const thisMonthOrders = orders.filter(
(o) =>
new Date(o.orderDate) >= startOfMonth &&
o.status !== "cancelled" &&
o.status !== "order_registered"
);
const thisMonthAmount = apiStats?.thisMonthAmount ?? thisMonthOrders.reduce((sum, o) => sum + (Number(o.amount) || 0), 0);
// 날짜 범위 내 수주 건수 (취소 제외)
const periodOrderCount = orders.filter((o) => o.status !== "cancelled").length;
// 분할 대기 (예시: 수주확정 상태)
const splitPendingCount = apiStats?.splitPending ?? orders.filter((o) => o.status === "order_confirmed").length;
// 생산지시 대기 (수주확정 상태 중 생산지시 안된 것)
const productionPendingCount = apiStats?.productionPending ?? orders.filter(
(o) => o.status === "order_confirmed" || o.status === "order_registered"
// 수주: 생산에 넘어가지 않은 건 (DRAFT + CONFIRMED)
const orderCount = orders.filter(
(o) => o.status === "draft" || o.status === "order_registered" || o.status === "order_confirmed"
).length;
// 출하 대기 (작업완료 상태)
const shipPendingCount = apiStats?.shipPending ?? orders.filter((o) => o.status === "work_completed").length;
// 생산: 생산지시대기 + 생산중 (IN_PROGRESS + IN_PRODUCTION)
const productionCount = orders.filter(
(o) => o.status === "in_progress" || o.status === "in_production"
).length;
// 출하: 출하대기 ~ 출고중 (PRODUCED + SHIPPING)
const shipCount = orders.filter(
(o) => o.status === "produced" || o.status === "shipping"
).length;
const stats = [
{
label: "이번 달 수주",
value: formatAmountManwon(thisMonthAmount),
label: periodLabel,
value: `${periodOrderCount}`,
icon: DollarSign,
iconColor: "text-blue-600",
},
{
label: "분할 대기",
value: `${splitPendingCount}`,
icon: SplitSquareVertical,
label: "수주",
value: `${orderCount}`,
icon: ClipboardList,
iconColor: "text-orange-600",
},
{
label: "생산지시 대기",
value: `${productionPendingCount}`,
icon: ClipboardList,
label: "생산",
value: `${productionCount}`,
icon: SplitSquareVertical,
iconColor: "text-green-600",
},
{
label: "출하 대기",
value: `${shipPendingCount}`,
label: "출하",
value: `${shipCount}`,
icon: Truck,
iconColor: "text-purple-600",
},

View File

@@ -379,6 +379,8 @@ export interface OrderStats {
draft: number;
confirmed: number;
inProgress: number;
inProduction: number;
produced: number;
completed: number;
cancelled: number;
totalAmount: number;
@@ -958,6 +960,8 @@ export async function getOrderStats(): Promise<{
draft: result.data.draft,
confirmed: result.data.confirmed,
inProgress: result.data.in_progress,
inProduction: result.data.in_production || 0,
produced: result.data.produced || 0,
completed: result.data.completed,
cancelled: result.data.cancelled,
totalAmount: result.data.total_amount,