From 648d62745fb12aeffcd20f59ce1443b8126e8e90 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Jul 2026 21:12:33 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20institutions-code-view=20(=EC=9D=B4?= =?UTF-8?q?=EC=8A=88=20#22=20=EC=9E=90=EB=8F=99=20=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit REST 계약(GET /facilities, /facilities/{id}) 기반 기관코드 목록/상세 화면(/institutions)을 추가한다. 기관코드↔실명 매핑 정보원 확보 전까지 (backend#16) 기관명·지역은 표시하지 않고 기관코드·기관유형·재고 시급도 요약만 노출한다(잠정). Co-Authored-By: Claude --- app/components/Nav.tsx | 1 + app/institutions/[id]/page.tsx | 108 ++++++++++++++++++++++++++++ app/institutions/page.tsx | 127 +++++++++++++++++++++++++++++++++ 3 files changed, 236 insertions(+) create mode 100644 app/institutions/[id]/page.tsx create mode 100644 app/institutions/page.tsx diff --git a/app/components/Nav.tsx b/app/components/Nav.tsx index 39d28d6..444d747 100644 --- a/app/components/Nav.tsx +++ b/app/components/Nav.tsx @@ -9,6 +9,7 @@ import { useAuth, roleHome } from "../lib/auth-context"; const CENTRAL_LINKS = [ { href: "/", label: "예측" }, { href: "/inventory", label: "재고·발주" }, + { href: "/institutions", label: "기관코드" }, { href: "/data", label: "데이터" }, ]; diff --git a/app/institutions/[id]/page.tsx b/app/institutions/[id]/page.tsx new file mode 100644 index 0000000..7278735 --- /dev/null +++ b/app/institutions/[id]/page.tsx @@ -0,0 +1,108 @@ +"use client"; +import { useParams } from "next/navigation"; +import { useApi } from "../../lib/api"; +import { num } from "../../lib/format"; +import { + Card, Kpi, RiskBadge, StatusBadge, Th, Td, State, + SkeletonStatGrid, SkeletonTable, EmptyState, PageTitle, Link, +} from "../../components/ui"; +import RequireRole from "../../components/RequireRole"; + +function InstitutionDetail({ id }: { id: string }) { + const fac = useApi(`/facilities/${encodeURIComponent(id)}`); + const inst = fac.data?.institution; + const summary = fac.data?.summary; + const inventory: any[] = fac.data?.inventory ?? []; + + return ( +
+ + ← 목록으로 + + } + /> + + {fac.loading && } + {fac.error && } + {!fac.loading && !fac.error && !inst && } + + {inst && ( + <> +
+ + + 0 ? "danger" : "default"} + /> + 0 ? "warn" : "default"} + /> +
+ + + {inventory.length === 0 && } + {inventory.length > 0 && ( +
+ + + + + + + + + + + + + + {inventory.map((r, i) => ( + + + + + + + + + + ))} + +
품목현재고가용ROP발주권고위험상태
+ {r.standardName} + {r.standardCode} + {num(r.onHand)}{num(r.available)}{num(r.ROP)}{num(r.orderRecommendation)} + {r.supplyRiskLevel && r.supplyRiskLevel !== "NORMAL" ? ( + + ) : ( + 정상 + )} + + +
+
+ )} +
+ + )} +
+ ); +} + +export default function InstitutionDetailPage() { + const params = useParams<{ id: string }>(); + const id = Array.isArray(params.id) ? params.id[0] : params.id; + return ( + + + + ); +} diff --git a/app/institutions/page.tsx b/app/institutions/page.tsx new file mode 100644 index 0000000..b69eede --- /dev/null +++ b/app/institutions/page.tsx @@ -0,0 +1,127 @@ +"use client"; +import { useMemo, useState } from "react"; +import { useApi } from "../lib/api"; +import { num } from "../lib/format"; +import { + Card, Toolbar, Field, Select, TextInput, Th, Td, State, + SkeletonTable, EmptyState, PageTitle, Link, +} from "../components/ui"; +import RequireRole from "../components/RequireRole"; + +const CATEGORY_OPTS: [string, string][] = [ + ["", "전체"], + ["보건소", "보건소"], + ["보건지소", "보건지소"], + ["보건진료소", "보건진료소"], +]; + +function InstitutionTable() { + const [category, setCategory] = useState(""); + const [q, setQ] = useState(""); + const path = + "/facilities?limit=500" + (category ? `&category=${encodeURIComponent(category)}` : ""); + const fac = useApi(path); + const rawItems: any[] = fac.data?.items ?? []; + + const rows = useMemo(() => { + if (!q) return rawItems; + return rawItems.filter((r: any) => String(r.id ?? "").toLowerCase().includes(q.toLowerCase())); + }, [rawItems, q]); + + return ( +
+
+ 기관코드↔실제 기관 매핑 정보원 확보 전까지(backend#16) 기관명·지역은 표시하지 않고{" "} + 기관코드 기준으로만 목록/상세를 제공합니다(잠정). +
+ + + + + + setQ(e.target.value)} placeholder="inst_0001" className="w-48" /> + + + + + {fac.loading && ( +
+ +
+ )} + {fac.error && ( +
+ +
+ )} + {fac.data && rows.length === 0 && ( + + )} + {rows.length > 0 && ( +
+ + + + + + + + + + + + + {rows.map((r: any) => ( + + + + + + + + + ))} + +
기관코드기관유형추적 품목긴급부족재주문점 미달발주 필요
+ + {r.id} + + {r.category}{num(r.summary?.trackedItems)} + {Number(r.summary?.critical ?? 0) > 0 ? ( + {num(r.summary.critical)} + ) : ( + num(r.summary?.critical) + )} + {num(r.summary?.belowRop)}{num(r.summary?.orderNeeded)}
+
+ )} + {fac.data?.truncated && ( +
+ 전체 {num(fac.data.totalElements)}건 중 {num(fac.data.returned)}건 표시(상한 도달). 기관유형 필터로 좁혀보세요. +
+ )} +
+
+ ); +} + +export default function InstitutionsPage() { + return ( + + + + + ); +}