feat: 새로운 구성 요소 및 환경 설정 추가, 기존 코드 개선

This commit is contained in:
yschoi
2025-03-27 14:32:47 +09:00
parent a1171a6d02
commit 2955ff9702
20 changed files with 240 additions and 153 deletions
-59
View File
@@ -1,59 +0,0 @@
'use client'
import { SearchDiv, SelectBox, UiTable } from '@common/business_components';
import { Button, TableHead, TableRow } from '@common/components';
import { useSnStore } from '../../store';
import type { TableUpperProps } from '../../store/oper';
const headMakeColSpan = (upHeadList: TableUpperProps[]) => {
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={3}>{'중축 개축 증축 '}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe'?<TableHead key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
export default function PublicReuseFaciltyInsert() {
const { upHeadList, publicReuseFacilityUper} = useSnStore((state) => state);
const nodeList= headMakeColSpan(upHeadList);
const total = 100;
const headName= ()=>{
return(
<p className="pl-3 text-xl">현황정보</p>
)
}
return ( <>
<div>
<p className="pl-3 text-2xl"> 현황정보 조회</p>
<SearchDiv>
<div className='grid grid-cols-12 w-4/5'>
<SelectBox className='col-span-3' label='시설물' selectArray={[{text:'관로', val:'1'}]} />
<SelectBox className='col-span-3' label='사용여부' selectArray={[{text:'사용', val:'1'}]} />
<SelectBox className='col-span-3' label='경과년도' selectArray={[{text:'10년', val:'1'}]} />
<SelectBox className='col-span-3' label='경사' selectArray={[{text:'', val:'1'}]} />
<SelectBox className='col-span-3' label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} />
<SelectBox className='col-span-3' label='용도' selectArray={[{text:'오수관', val:'1'}]} />
<SelectBox className='col-span-3' label='관종' selectArray={[{text:'PE관관', val:'1'}]} />
<SelectBox className='col-span-3' label='관경' selectArray={[{text:'50', val:'1'}]} />
</div>
<div className='w-1/5 flex items-center justify-end'>
<Button className='mr-10 rounded-lg'>조회</Button>
</div>
</SearchDiv>
</div>
<div className="grid grid-cols-1 gap-4">
<UiTable tableData={publicReuseFacilityUper} headlist={upHeadList} pageSize={20} total={total} headName={headName()}>
{nodeList}
</UiTable>
</div>
</> )
}
+7
View File
@@ -0,0 +1,7 @@
'use client'
import { BaseLayout } from "@common/components";
export function ClientLayout({ children }: { children: React.ReactNode }) {
return <BaseLayout>{children}</BaseLayout>;
}
+29 -10
View File
@@ -1,17 +1,36 @@
import './styles/base.css'
import {StoreProvider} from './store'
import { BaseLayout } from "@common/components";
export default function RootLayout({
import './styles/components.css'
import type { Metadata } from "next";
import { cookies } from "next/headers";
import { StoreProvider } from './store'
import { ClientLayout } from './clientLayout'
export const metadata: Metadata = {
title: "디지털 하수관로",
description: "디지털 하수관로 시스템입니다",
keywords: ["하수관로"],
openGraph: {
title: "디지털 하수관로",
description: "디지털 하수관로 시스템입니다"
}
};
export default async function RootLayout({
children,
}: {
children: React.ReactNode
}) {
}: Readonly<{
children: React.ReactNode;
}>) {
const theme = (await cookies()).get("site-theme-key");
return (
<html lang="en">
<html lang="en" data-theme={theme?.value}>
<body>
<StoreProvider>
<BaseLayout>{children}</BaseLayout>
</StoreProvider>
<StoreProvider>
<ClientLayout>
{children}
</ClientLayout>
</StoreProvider>
</body>
</html>
)
+22 -23
View File
@@ -6,18 +6,19 @@ import { TableHead } from '@common/components';
import { useSnStore } from '../../store';
import type { TableUpperProps } from '../../store/table';
const headMakeColSpan = (upHeadList: TableUpperProps[]) => {
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(<TableHead key={head.id}>{head.title}</TableHead>))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe'?<TableHead key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
const headMakeColSpan = (upHeadList: TableUpperProps[]) => {
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={3}>{'중축 개축 증축 '}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe'?<TableHead key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
export default function PublicReuseFaciltyInsert() {
const { upHeadList, publicReuseFacilityUper} = useSnStore((state) => state);
@@ -33,23 +34,21 @@ import type { TableUpperProps } from '../../store/table';
<p className="pl-3 text-2xl"> 점검 및 진단 조회</p>
<SearchDiv>
<div className='flex'>
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} />
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} />
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} />
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} />
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} />
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} />
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} />
<SelectBox label='관경' selectArray={[{text:'50', val:'1'}]} />
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<SelectBox label='관경' selectArray={[{text:'50', val:'1'}]} selectClass='w-24' className='mr-4' onSelectValue={()=>{}} />
<Button className='mr-4' size='sm'>조회</Button>
</div>
</SearchDiv>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="colsapn-1">
<UiTable publicReuseFacility={publicReuseFacilityUper} headlist={upHeadList} pageSize={20} total={total} headName={headName()}>
</UiTable>
</div>
</div>
</> )
+21 -25
View File
@@ -2,7 +2,7 @@
import { useEffect, useState } from "react";
import { SearchDiv, SelectBox, UiTable } from '@common/business_components';
import { Button } from '@common/components';
import { Button, Input } from '@common/components';
import { useRouter } from "next/navigation";
import { pplinesService } from "@service";
@@ -21,7 +21,9 @@ export default function Pplines() {
const headName = () => {
return(
<p className="pl-3 text-xl">현황정보</p>
<div className="card-header">
<h3 className="text-2xl font-medium text-label">현황정보</h3>
</div>
)
}
@@ -43,32 +45,26 @@ export default function Pplines() {
}, []);
return (
<div>
<p className="pl-3 text-2xl"> 관로시설 조회</p>
<div className="h-auto min-h-full bg-container">
<h2 className="px-[1.8rem] pt-[2rem] text-[2.4rem] font-semibold text-title">
관로시설
</h2>
<SearchDiv>
<div className='grid grid-cols-12 gap-1 w-4/5'>
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
<SelectBox label='관경' selectArray={[{text:'50', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}}/>
</div>
<div className='w-1/5 flex justify-end items-center'>
<Button className='mr-10 rounded-lg' onClick={fetchData}>조회</Button>
</div>
<form className='grid grid-cols-12 gap-1 w-4/5'>
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='관경' selectArray={[{text:'50', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<Button className="h-[3.2rem]" onClick={fetchData}>조회</Button>
</form>
</SearchDiv>
<div className="grid grid-cols-2 gap-4">
<div className="col-span-2">
<UiTable tableData={data.content} headlist={tableHeadlist} pageSize={pageSize} total={total} children={null} headName={headName()}/>
</div>
<div className="card card-border">
<UiTable tableData={data.content} headlist={tableHeadlist} pageSize={pageSize} total={total} children={null} headName={headName()} cellClick={()=>{}} />
</div>
<button onClick={() => router.push("/oper/soil")} className="mr-3 mt-4 bg-red-500 text-white px-4 py-2 rounded">
이동
</button>
</div> );
}
+8 -8
View File
@@ -87,13 +87,13 @@ import { useRouter } from "next/navigation";
<p className="pl-3 text-2xl"> 현황정보 조회</p>
<SearchDiv >
<div className='grid grid-cols-12 gap-1 w-4/5'>
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} className='col-span-3'/>
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} className='col-span-3'/>
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} className='col-span-3'/>
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} className='col-span-3'/>
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} className='col-span-3'/>
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} className='col-span-3'/>
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} className='col-span-3'/>
<SelectBox label='시설물' selectArray={[{text:'관로', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='사용여부' selectArray={[{text:'사용', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='경과년도' selectArray={[{text:'10년', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='경사' selectArray={[{text:'', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='흐름방식' selectArray={[{text:'자연유하', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='용도' selectArray={[{text:'오수관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
<SelectBox label='관종' selectArray={[{text:'PE관관', val:'1'}]} className='col-span-3' onSelectValue={(val: string)=>{}} selectClass='w-24' />
{/* <SelectBox label='관경' selectArray={[{text:'50', val:'1'}]} className='col-span-3'/> */}
</div>
<div className='w-1/5 flex justify-end items-center'>
@@ -103,7 +103,7 @@ import { useRouter } from "next/navigation";
{/* </div> */}
<div className="grid grid-cols-2 gap-4">
<div className="col-span-2">
<UiTable tableData={publicReuseFacilityUper} headlist={upHeadList} pageSize={20} total={total} headName={headName()}/>
</div>
</div>
+16 -7
View File
@@ -1,10 +1,19 @@
'use client'
import Link from "next/link";
import { Button } from "@common/components/ui";
const App = ({ children }: Readonly<{ children: React.ReactNode }>) => {
export default function Home() {
return (
<>
{children}
</>
<div className="grid grid-cols-12 gap-2">
<div className="col-span-2">
<Link href={"/oper/pplines"}><Button>관로시설</Button></Link>
</div>
<div className="col-span-2">
<Link href={"/oper/diag"}><Button>점검 및 진단</Button></Link>
</div>
<div className="col-span-2">
<Link href={"/oper/soil"}><Button>중점관리 우수토실</Button></Link>
</div>
</div>
);
};
export default App;
}
+1 -1
View File
@@ -4,7 +4,7 @@ import axios from 'axios'
/**
* 하수관로 API URL
*/
const API_URL = process.env.NEXT_PUBLIC_API_URL
const API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL
const PPLINES_URL = `${API_URL}/v1/pplines`
const PAGE_NO = 1
const PAGE_SIZE = 20
+1 -1
View File
@@ -18,7 +18,7 @@ export interface StoreProviderProps {
export const StoreProvider = ({
children,
}: StoreProviderProps) => {
const storeRef = useRef<StoreApi>(null)
const storeRef = useRef<StoreApi | null>(null)
if (!storeRef.current) {
storeRef.current = StoreZus()
+3 -1
View File
@@ -1,6 +1,8 @@
export type TableUpperProps = {
id: string,
title: string
title: string,
upSequnce?: number,
upName?: string
}
type PublicReuseFacility = {
sido: string,
+1 -2
View File
@@ -1,8 +1,7 @@
@import "@common/assets/styles/base.css";
@import "@common/assets/styles/diveicon.css";
@tailwind base;
@tailwind utilities;
html,
body {
+25
View File
@@ -0,0 +1,25 @@
@tailwind components;
@layer components {
.card {
@apply relative m-[0.6rem] box-border min-h-[2rem] rounded-[0.5rem] p-[0.6rem] text-[1.3rem] text-[#666];
}
.card-border {
@apply border border-[#dbe2e6] bg-[rgba(255,255,255,0.6)] shadow-blur;
}
.card-header {
@apply -mx-[0.6rem] flex items-center justify-between border-b border-b-[#e1e7eb] p-4;
}
.card-footer {
@apply -mx-[0.6rem] mt-2 flex items-center justify-center border-t border-t-[#e1e7eb] py-4;
}
[data-theme="black"] {
.card-border {
@apply border-[#3f4042] bg-[rgba(255,255,255,0.065)];
}
.card-header {
@apply border-b-[#3f4042];
}
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
+16
View File
@@ -0,0 +1,16 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_PORT: string;
readonly VITE_PROXY_HOST: string;
readonly VITE_SERVER_API_URL: string;
readonly VITE_SITE_ID: string;
readonly VITE_CLAIM_NAME: string;
readonly VITE_ACCESS_TOKEN: string;
readonly VITE_REFRESH_TOKEN: string;
readonly VITE_AUTH_USER_ID: string;
}
interface ImportMeta {
readonly env: ImportMetaEnv;
}
+38 -8
View File
@@ -1,14 +1,44 @@
import type { NextConfig } from "next";
import webpack from "webpack";
const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: "/sewerage-service/api/:path*", // 프론트엔드에서 호출할 API URL
destination: "http://localhost:8000/sewerage-service/api/:path*", // 백엔드의 실제 API URL
},
];
}
typescript: {
ignoreBuildErrors: true, // 이 줄 추가
},
eslint: {
ignoreDuringBuilds: true,
},
webpack: (config) => {
config.plugins.push(
new webpack.DefinePlugin({
"import.meta.env.VITE_PORT": JSON.stringify(
process.env.NEXT_PUBLIC_PORT
),
"import.meta.env.VITE_PROXY_HOST": JSON.stringify(
process.env.NEXT_PUBLIC_PROXY_HOST
),
"import.meta.env.VITE_SERVER_API_URL": JSON.stringify(
process.env.NEXT_PUBLIC_SERVER_API_URL
),
"import.meta.env.VITE_SITE_ID": JSON.stringify(process.env.SITE_ID),
"import.meta.env.VITE_CLAIM_NAME": JSON.stringify(
process.env.NEXT_PUBLIC_CLAIM_NAME
),
"import.meta.env.VITE_ACCESS_TOKEN": JSON.stringify(
process.env.NEXT_PUBLIC_ACCESS_TOKEN
),
"import.meta.env.VITE_REFRESH_TOKEN": JSON.stringify(
process.env.NEXT_PUBLIC_REFRESH_TOKEN
),
"import.meta.env.VITE_AUTH_USER_ID": JSON.stringify(
process.env.NEXT_PUBLIC_AUTH_USER_ID
),
"process.env.SITE_ID": JSON.stringify(process.env.NEXT_PUBLIC_SITE_ID),
})
);
return config;
},
};
export default nextConfig;
+19 -2
View File
@@ -3,7 +3,7 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbopack",
"dev": "next dev -p 3002",
"build": "next build",
"start": "next start",
"lint": "next lint"
@@ -12,18 +12,35 @@
"@common/assets": "workspace:*",
"@common/business_components": "workspace:*",
"@common/components": "workspace:*",
"@frontend-opensource/use-react-hooks": "^1.1.4",
"@hookform/resolvers": "^4.1.3",
"axios": "^1.8.3",
"@radix-ui/react-label": "^2.1.2",
"@radix-ui/react-slot": "^1.1.2",
"@tanstack/react-query": "^5.67.1",
"@tanstack/react-table": "^8.21.2",
"axios": "^1.8.1",
"chart.js": "^4.4.8",
"dayjs": "^1.11.13",
"js-cookie": "^3.0.5",
"next": "15.1.7",
"react-chartjs-2": "^5.3.0",
"react-hook-form": "^7.54.2",
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.2",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@faker-js/faker": "^9.5.0",
"@types/js-cookie": "^3.0.6",
"@types/node": "^22.13.1",
"@types/webpack": "^5.28.5",
"eslint": "^9",
"eslint-config-next": "15.1.7",
"postcss": "^8",
"prettier": "^3.4.2",
"prettier-plugin-tailwindcss": "^0.6.11",
"steiger": "^0.5.5",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
+4
View File
@@ -25,8 +25,12 @@
"baseUrl": "app",
"paths": {
"@/*": ["./*"],
"@common/components/*": ["../../../packages/common/components/src/*"],
"@common/components": ["../../../packages/common/components/src"],
"@common/business_components/*": ["../../../packages/common/business_components/src/*"],
"@common/business_components": ["../../../packages/common/business_components/src"],
"@common/network/*": ["../../../packages/common/network/*"],
"@common/network": ["../../../packages/common/network/"],
"@store": ["./store"],
"@service": ["./service"]
}
@@ -1,9 +1,9 @@
function SearchDiv ({children}:{ children: React.ReactNode; }) {
return (
<div className="flex border border-black m-4 rounded-lg min-h-[80px]">
{children}
</div>
)
<div className="card card-border ">
{children}
</div>
);
}
export { SearchDiv };
@@ -22,6 +22,7 @@ export function UiTable({ tableData, headlist, pageSize, total, children, headNa
return (
<>
{headName ? headName : <></>}
<div className="card !m-[1.2rem] h-[49rem] overflow-auto !p-0">
<Table>
<TableHeader>
{children ? <>{children}</> : <TableRow>
@@ -42,6 +43,7 @@ export function UiTable({ tableData, headlist, pageSize, total, children, headNa
</TableBody>
</Table>
{total <= 0 ? <></> : <UiPageNations total={total} cur={cur} pageSize={pageSize} />}
</div>
</>
);
}
@@ -7,7 +7,7 @@ import { Footer } from "./footer";
import "./common.css";
import type { QueryClient } from "@tanstack/react-query";
export function BaseLayout({
export function VBaseLayout({
children,
queryClient,
}: {