6af863199f
- fito-nextjs 기반으로 재구성 - 로그인: MOMO 로고 + 모모유통 + 유통관리 ERP, 하단에 본사/지사 주소 표시 - 사이드바 상단: MOMO 아이콘 + 모모유통 + 유통관리 ERP - 파비콘: /src/app/icon.svg (MOMO 그린 배지) - layout.tsx title: 모모유통 | 유통관리 ERP - DB: 183.99.177.40:5432/distribution (fito 스키마 import 완료) - Traefik: Host(momo.junggomoa.com), 컨테이너 momo-erp
1.8 KiB
1.8 KiB
globs
| globs |
|---|
| src/app/(main)/**/*.tsx, src/app/(auth)/**/*.tsx, src/app/admin-panel/**/*.tsx |
페이지 코딩 규칙
기본 구조
모든 페이지는 "use client" 클라이언트 컴포넌트:
"use client";
import { useState, useCallback } from "react";
import { DataGrid, GridColumn } from "@/components/grid/data-grid";
import { SearchForm, SearchField } from "@/components/layout/search-form";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { CodeSelect } from "@/components/ui/code-select";
import Swal from "sweetalert2";
페이지 레이아웃 순서
- 제목:
<h2 className="text-lg font-bold text-gray-800 mb-4">{제목}</h2> - 검색 폼:
<SearchForm>+<SearchField> - 버튼 영역: 조회/등록/삭제
- 데이터 그리드:
<DataGrid>+<Pagination>
데이터 조회
const [data, setData] = useState<Record<string, unknown>[]>([]);
const fetchData = useCallback(async () => {
const res = await fetch("/api/{domain}/{resource}", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ year, category_cd }),
});
if (res.ok) {
const json = await res.json();
setData(json.RESULTLIST || []);
}
}, [year, category_cd]);
팝업
const left = (window.screen.width - 1200) / 2;
const top = (window.screen.height - 550) / 2;
window.open(url, "formPopup", `width=1200,height=550,left=${left},top=${top}`);
삭제 확인
if (selectedRows.length === 0) {
Swal.fire("알림", "삭제할 항목을 선택하세요.", "warning");
return;
}
DataGrid 높이
페이지별 calc(100vh - 350px) ~ calc(100vh - 400px) 사용.
년도 필터 (자주 사용)
Array.from({ length: 5 }, (_, i) => new Date().getFullYear() - i)