summaryrefslogtreecommitdiff
path: root/lib/evaluation-target-list/table
diff options
context:
space:
mode:
Diffstat (limited to 'lib/evaluation-target-list/table')
-rw-r--r--lib/evaluation-target-list/table/evaluation-target-table.tsx109
-rw-r--r--lib/evaluation-target-list/table/evaluation-targets-columns.tsx4
-rw-r--r--lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx4
3 files changed, 97 insertions, 20 deletions
diff --git a/lib/evaluation-target-list/table/evaluation-target-table.tsx b/lib/evaluation-target-list/table/evaluation-target-table.tsx
index 87be3589..b140df0e 100644
--- a/lib/evaluation-target-list/table/evaluation-target-table.tsx
+++ b/lib/evaluation-target-list/table/evaluation-target-table.tsx
@@ -7,7 +7,7 @@
import * as React from "react";
import { useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";
-import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
+import { HelpCircle, PanelLeftClose, PanelLeftOpen } from "lucide-react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
@@ -28,6 +28,74 @@ import { EvaluationTargetsTableToolbarActions } from "./evaluation-targets-toolb
import { EvaluationTargetFilterSheet } from "./evaluation-targets-filter-sheet";
import { EvaluationTargetWithDepartments } from "@/db/schema";
import { EditEvaluationTargetSheet } from "./update-evaluation-target";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+
+/* -------------------------------------------------------------------------- */
+/* Process Guide Popover */
+/* -------------------------------------------------------------------------- */
+function ProcessGuidePopover() {
+ return (
+ <Popover>
+ <PopoverTrigger asChild>
+ <Button variant="ghost" size="icon" className="h-6 w-6">
+ <HelpCircle className="h-4 w-4 text-muted-foreground" />
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent className="w-96" align="start">
+ <div className="space-y-3">
+ <div className="space-y-1">
+ <h4 className="font-medium">평가 대상 확정 프로세스</h4>
+ <p className="text-sm text-muted-foreground">
+ 발주실적을 기반으로 평가 대상을 확정하는 절차입니다.
+ </p>
+ </div>
+ <div className="space-y-3 text-sm">
+ <div className="flex gap-3">
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600">
+ 1
+ </div>
+ <div>
+ <p className="font-medium">발주실적 기반 자동 추출</p>
+ <p className="text-muted-foreground">전년도 10월 ~ 해당년도 9월 발주실적에서 업체 목록을 자동으로 생성합니다.</p>
+ </div>
+ </div>
+ <div className="flex gap-3">
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600">
+ 2
+ </div>
+ <div>
+ <p className="font-medium">담당자 지정</p>
+ <p className="text-muted-foreground">각 평가 대상별로 5개 부서(발주/조달/품질/설계/CS)의 담당자를 지정합니다.</p>
+ </div>
+ </div>
+ <div className="flex gap-3">
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600">
+ 3
+ </div>
+ <div>
+ <p className="font-medium">검토 및 의견 수렴</p>
+ <p className="text-muted-foreground">모든 담당자가 평가 대상 적합성을 검토하고 의견을 제출합니다.</p>
+ </div>
+ </div>
+ <div className="flex gap-3">
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600">
+ 4
+ </div>
+ <div>
+ <p className="font-medium">최종 확정</p>
+ <p className="text-muted-foreground">모든 담당자 의견이 일치하면 평가 대상으로 최종 확정됩니다.</p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </PopoverContent>
+ </Popover>
+ )
+}
/* -------------------------------------------------------------------------- */
/* Stats Card */
@@ -130,7 +198,7 @@ function EvaluationTargetsStats({ evaluationYear }: { evaluationYear: number })
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">확정</CardTitle>
- <Badge variant="default">완료</Badge>
+ <Badge variant="success">완료</Badge>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold text-green-600">{confirmed.toLocaleString()}</div>
@@ -204,10 +272,17 @@ export function EvaluationTargetsTable({ promises, evaluationYear, className }:
const tableData = promiseData;
/* ---------------------- 검색 파라미터 안전 처리 ---------------------- */
- const getSearchParam = React.useCallback((key: string, defaultValue?: string): string => {
- return searchParams?.get(key) ?? defaultValue ?? "";
- }, [searchParams]);
-
+ const searchString = React.useMemo(
+ () => searchParams.toString(), // query가 바뀔 때만 새로 계산
+ [searchParams]
+ );
+
+ const getSearchParam = React.useCallback(
+ (key: string, def = "") =>
+ new URLSearchParams(searchString).get(key) ?? def,
+ [searchString]
+ );
+
// 제네릭 함수는 useCallback 밖에서 정의
const parseSearchParamHelper = React.useCallback((key: string, defaultValue: any): any => {
try {
@@ -226,7 +301,7 @@ const parseSearchParam = <T,>(key: string, defaultValue: T): T => {
const initialSettings = React.useMemo(() => ({
page: parseInt(getSearchParam("page", "1")),
perPage: parseInt(getSearchParam("perPage", "10")),
- sort: parseSearchParam("sort", [{ id: "createdAt", desc: true }]),
+ sort: getSearchParam('sort') ? JSON.parse(getSearchParam('sort')!) : [{ id: "createdAt", desc: true }],
filters: parseSearchParam("filters", []),
joinOperator: (getSearchParam("joinOperator") as "and" | "or") || "and",
basicFilters: parseSearchParam("basicFilters", []),
@@ -237,7 +312,7 @@ const parseSearchParam = <T,>(key: string, defaultValue: T): T => {
pinnedColumns: { left: [], right: ["actions"] },
groupBy: [],
expandedRows: [],
- }), [getSearchParam, parseSearchParamHelper]);
+ }), [getSearchParam]);
/* --------------------- 프리셋 훅 ------------------------------ */
const {
@@ -267,9 +342,6 @@ const parseSearchParam = <T,>(key: string, defaultValue: T): T => {
// { accessorKey: "division", header: "구분" }
// ];
-window.addEventListener('beforeunload', () => {
- console.trace('[beforeunload] 문서가 통째로 사라지려 합니다!');
-});
/* 기본 필터 */
const filterFields: DataTableFilterField<EvaluationTargetWithDepartments>[] = [
@@ -297,11 +369,16 @@ window.addEventListener('beforeunload', () => {
/* current settings */
const currentSettings = React.useMemo(() => getCurrentSettings(), [getCurrentSettings]);
- const initialState = React.useMemo(() => ({
- sorting: initialSettings.sort.filter((s: any) => columns.some((c: any) => ("accessorKey" in c ? c.accessorKey : c.id) === s.id)),
- columnVisibility: currentSettings.columnVisibility,
- columnPinning: currentSettings.pinnedColumns,
- }), [columns, currentSettings, initialSettings.sort]);
+ const initialState = React.useMemo(() => {
+ return {
+ sorting: initialSettings.sort.filter(sortItem => {
+ const columnExists = columns.some(col => col.accessorKey === sortItem.id)
+ return columnExists
+ }) as any,
+ columnVisibility: currentSettings.columnVisibility,
+ columnPinning: currentSettings.pinnedColumns,
+ }
+ }, [currentSettings, initialSettings.sort, columns])
/* ----------------------- useDataTable ------------------------ */
const { table } = useDataTable({
diff --git a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
index e2163cad..b6631f14 100644
--- a/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
+++ b/lib/evaluation-target-list/table/evaluation-targets-columns.tsx
@@ -42,7 +42,7 @@ const getConsensusBadge = (consensusStatus: boolean | null) => {
return <Badge variant="outline">검토 중</Badge>;
}
if (consensusStatus === true) {
- return <Badge variant="default" className="bg-green-600">의견 일치</Badge>;
+ return <Badge variant="success">의견 일치</Badge>;
}
return <Badge variant="destructive">의견 불일치</Badge>;
};
@@ -383,7 +383,7 @@ export function getEvaluationTargetsColumns({ setRowAction }: GetColumnsProps):
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="확정일" />,
cell: ({ row }) => {
const confirmedAt = row.getValue<Date>("confirmedAt");
- return <span className="text-sm">{formatDate(confirmedAt, "KR")}</span>;
+ return <span className="text-sm">{ confirmedAt ? formatDate(confirmedAt, "KR") :'-'}</span>;
},
size: 100,
},
diff --git a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx
index 7ea2e0ec..82b7c97c 100644
--- a/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx
+++ b/lib/evaluation-target-list/table/evaluation-targets-toolbar-actions.tsx
@@ -160,9 +160,9 @@ export function EvaluationTargetsTableToolbarActions({
{/* 확정 버튼 */}
{selectedStats.canConfirm && (
<Button
- variant="default"
+ variant="success"
size="sm"
- className="gap-2 bg-green-600 hover:bg-green-700"
+ className="gap-2"
onClick={() => setConfirmDialogOpen(true)}
disabled={isLoading}
>