Merge branch 'iipbs/dm/dev'
@@ -18,8 +18,7 @@ NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr
|
||||
NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={x}&tilerow={y}
|
||||
NEXT_PUBLIC_GATEWAY_URL=http://con.mind-one.co.kr:11083 # 개발 서버 API
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||
# NEXT_PUBLIC_GATEWAY_URL=http://localhost:8883 # 개발 서버 API
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
|
||||
export type AnchorSection = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type BusinessSubNavMenu = {
|
||||
key: string;
|
||||
label: string;
|
||||
href?: string;
|
||||
current?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
sections?: AnchorSection[];
|
||||
};
|
||||
|
||||
type BusinessSubNavProps = {
|
||||
menus?: BusinessSubNavMenu[];
|
||||
offsetTop?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function BusinessSubNav({
|
||||
menus = [],
|
||||
offsetTop = 120,
|
||||
className = "",
|
||||
}: BusinessSubNavProps) {
|
||||
const [activeId, setActiveId] = useState("");
|
||||
const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
|
||||
|
||||
const allSections = useMemo(
|
||||
() => menus.flatMap((menu) => menu.sections ?? []),
|
||||
[menus]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const initialOpenState: Record<string, boolean> = {};
|
||||
|
||||
menus.forEach((menu) => {
|
||||
initialOpenState[menu.key] = menu.defaultOpen ?? menu.current ?? false;
|
||||
});
|
||||
|
||||
setOpenMenus(initialOpenState);
|
||||
}, [menus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!allSections.length) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const visible = entries
|
||||
.filter((entry) => entry.isIntersecting)
|
||||
.sort((a, b) => b.intersectionRatio - a.intersectionRatio);
|
||||
|
||||
if (visible[0]) {
|
||||
setActiveId(visible[0].target.id);
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: `-${offsetTop}px 0px -55% 0px`,
|
||||
threshold: [0.2, 0.35, 0.5, 0.7],
|
||||
}
|
||||
);
|
||||
|
||||
const targets = allSections
|
||||
.map((section) => document.getElementById(section.id))
|
||||
.filter(Boolean) as HTMLElement[];
|
||||
|
||||
targets.forEach((el) => observer.observe(el));
|
||||
|
||||
const hash = window.location.hash.replace("#", "");
|
||||
const initialActive =
|
||||
allSections.find((section) => section.id === hash)?.id ??
|
||||
allSections[0]?.id ??
|
||||
"";
|
||||
|
||||
setActiveId(initialActive);
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [allSections, offsetTop]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeId) return;
|
||||
|
||||
const parentMenu = menus.find((menu) =>
|
||||
(menu.sections ?? []).some((section) => section.id === activeId)
|
||||
);
|
||||
|
||||
if (!parentMenu) return;
|
||||
|
||||
setOpenMenus(() => {
|
||||
const nextOpenState: Record<string, boolean> = {};
|
||||
|
||||
menus.forEach((menu) => {
|
||||
nextOpenState[menu.key] = menu.key === parentMenu.key;
|
||||
});
|
||||
|
||||
return nextOpenState;
|
||||
});
|
||||
}, [activeId, menus]);
|
||||
|
||||
const handleMove = (id: string) => {
|
||||
setActiveId(id);
|
||||
|
||||
const target = document.getElementById(id);
|
||||
if (!target) return;
|
||||
|
||||
window.history.replaceState(null, "", `#${id}`);
|
||||
|
||||
const top = target.getBoundingClientRect().top + window.scrollY - offsetTop;
|
||||
|
||||
window.scrollTo({
|
||||
top,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
const toggleMenu = (key: string) => {
|
||||
setOpenMenus((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
if (!menus.length) return null;
|
||||
|
||||
return (
|
||||
<aside className={`fixed right-2 top-[110px] z-50 w-[260px] ${className}`}>
|
||||
<div className="overflow-hidden bg-white">
|
||||
{menus.map((menu) => {
|
||||
const isOpen = openMenus[menu.key] ?? false;
|
||||
const hasChildren = (menu.sections?.length ?? 0) > 0;
|
||||
const isCurrentGroup = (menu.sections ?? []).some(
|
||||
(section) => section.id === activeId
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={menu.key}
|
||||
>
|
||||
<div className="flex items-center justify-between px-5 py-2">
|
||||
{menu.href ? (
|
||||
<Link
|
||||
href={menu.href}
|
||||
className={[
|
||||
"text-[16px] font-medium tracking-[-0.02em]",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
>
|
||||
{menu.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (hasChildren) toggleMenu(menu.key);
|
||||
}}
|
||||
className={[
|
||||
"text-left text-[16px] font-medium tracking-[-0.02em]",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
>
|
||||
{menu.label}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{hasChildren && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleMenu(menu.key)}
|
||||
className={[
|
||||
"ml-2 shrink-0",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
aria-label={`${menu.label} 드롭다운 토글`}
|
||||
>
|
||||
{isOpen ? (
|
||||
<ChevronUp className="h-5 w-5" />
|
||||
) : (
|
||||
<ChevronDown className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasChildren && isOpen && (
|
||||
<div className="relative px-5 pb-5">
|
||||
<div className="absolute left-[31px] top-[6px] bottom-[14px] w-[2px] bg-[#CDD5DB]" />
|
||||
|
||||
<ul className="space-y-4 pl-2">
|
||||
{menu.sections!.map((section) => {
|
||||
const active = activeId === section.id;
|
||||
|
||||
return (
|
||||
<li key={section.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMove(section.id)}
|
||||
className="flex items-start gap-3 text-left"
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
"relative z-[1] mt-[6px] -ml-[2px] h-[11px] w-[11px] shrink-0 rounded-full border",
|
||||
active
|
||||
? "border-sky-500 bg-sky-500"
|
||||
: "border-[#BFC7CF] bg-white",
|
||||
].join(" ")}
|
||||
/>
|
||||
<span
|
||||
className={[
|
||||
"break-keep text-[14px] leading-[1.45] transition-colors",
|
||||
active
|
||||
? "text-[#4A4A4A]"
|
||||
: "text-[#D1D7DE] hover:text-[#7B8794]",
|
||||
].join(" ")}
|
||||
>
|
||||
{section.label}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { useRouter } from "next/navigation";
|
||||
const NAV_ITEMS = [
|
||||
{ href: "/reuse/intro/law", label: "물 재이용 소개" },
|
||||
{ href: "/reuse/cases/rain", label: "물 재이용 현황" },
|
||||
{ href: "/reuse/business/rain/suwon", label: "물 재이용 사례" },
|
||||
{ href: "/reuse/business/rain", label: "물 재이용 사례" },
|
||||
{ href: "/reuse/apply/place", label: "수요신청" },
|
||||
];
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
// apps/dm/app/(landing)/reuse/apply/modal/SimilarCaseModal.tsx (유사사례 모달)
|
||||
/**
|
||||
* 유사사례 Modal
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useState, useId } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
type Criterion = "" | "supplyAmt" | "treatment" | "usePurpose";
|
||||
type Criterion = "" | "supplyAmt" | "treatment" | "usePurpose"; // 유사기준
|
||||
|
||||
export type SimilarCaseRow = {
|
||||
caseId: string;
|
||||
@@ -90,7 +92,7 @@ export default function SimilarCaseModal({
|
||||
// 모달 열릴 때 초기화
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setCriterion("supplyAmt");
|
||||
setCriterion("supplyAmt"); // 요구수량(㎥/d)
|
||||
onSearch("supplyAmt");
|
||||
setSelectedCaseId("");
|
||||
}, [open]);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function GlobalCaseSection() {
|
||||
const title = "해외 재이용 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-global-overview" className="scroll-mt-[120px]">
|
||||
<SectionTitle>사례 개요</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/overview_01.png",
|
||||
alt: "해외 재이용 사례 개요 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-global-taiwan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>대만</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/taiwan_01.png",
|
||||
alt: "대만 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-global-australia" className="scroll-mt-[120px]">
|
||||
<SectionTitle>호주</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/australia_01.png",
|
||||
alt: "호주 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-global-singapore" className="scroll-mt-[120px]">
|
||||
<SectionTitle>싱가포르</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/singapore_01.png",
|
||||
alt: "싱가포르 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-global-eu" className="scroll-mt-[120px]">
|
||||
<SectionTitle>EU</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/eu_01.png",
|
||||
alt: "EU 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
"use client";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="inline-block h-[14px] w-[24px] rounded-full bg-gradient-to-r from-sky-300 to-lime-300" />
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type SupplyRow = {
|
||||
facility: string;
|
||||
usage: string;
|
||||
amount: string;
|
||||
};
|
||||
|
||||
function SupplyTable({ rows }: { rows: SupplyRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-2 text-[18px] font-medium text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#40B5A5] px-4 py-2 text-[18px] font-medium text-white">
|
||||
이용용도
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#73BF4B] px-4 py-2 text-[18px] font-medium text-white">
|
||||
공급량(㎥)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.usage}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.amount}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
totalCost,
|
||||
treatment,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
totalCost: string;
|
||||
treatment: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>총사업비 : {totalCost}</li>
|
||||
<li>처리공법 : {treatment}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GreywaterExampleSection() {
|
||||
const title = "중수 재이용 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-greywater-ansan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 안산시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="안산시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="81억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "경기테크노파크", usage: "청소·화장실용수", amount: "40㎥/일" },
|
||||
{ facility: "올림픽수영장", usage: "청소·화장실/세척·살수용수", amount: "35㎥/일" },
|
||||
{ facility: "와스타디움", usage: "청소·화장실/조경용수", amount: "90㎥/일" },
|
||||
{ facility: "호수실내수영장", usage: "청소·화장실/조경/세척·살수용수", amount: "50㎥/일" },
|
||||
{ facility: "한양대학교 에리카캠퍼스", usage: "청소·화장실용수", amount: "370㎥/일" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
<section id="example-greywater-yongin" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 용인시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="용인시 중수도 설치사업"
|
||||
purpose="물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용"
|
||||
totalCost="40억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "여성회관", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "수지아르피아", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "지상작전사령부", usage: "청소·화장실/조경/세척·살수용수", amount: "53㎥/일" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
<section id="example-greywater-pyeongtaek" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 평택시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="평택시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="21억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "평택대학교",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "50㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function RainExampleSection() {
|
||||
const title = "빗물 재이용 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<section id="example-rain-suwon" className="mt-10 scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 수원시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/rain/rain_01.png",
|
||||
alt: "수원시 사례 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/rain/rain_02.png",
|
||||
alt: "수원시 사례 2",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-rain-jeju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>제주특별자치도</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/rain/jeju_01.png",
|
||||
alt: "제주 사례 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/rain/jeju_02.png",
|
||||
alt: "제주 사례 2",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function SewageExampleSection() {
|
||||
const title = "하수처리수 재이용 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<section id="example-sewage-bolyeong" className="scroll-mt-[120px]">
|
||||
<SectionTitle>충청남도 보령시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/bolyeong_01.png",
|
||||
alt: "보령시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
충남 보령 하수재이용
|
||||
<br />: 보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해
|
||||
보령하수처리시설의 하수처리수 재이용 사업 추진
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 방식</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
2017년 한국환경공단과 사업대행 위 · 수탁 협약 체결
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
처리공정 : 전처리 UF + 후처리 RO
|
||||
<br />
|
||||
시설용량 : 1만m³/일
|
||||
<br />
|
||||
관로 : 8.7km
|
||||
<br />
|
||||
사업비 : 315억원
|
||||
<br />
|
||||
위치: 충청남도 보령시 대청방조제로 322 일원
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권
|
||||
가뭄 시 지역사회 기여 차원에서 기관간 MOU를 바탕으로 추진
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-sewage-cheongju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>청주시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/cheongju_01.png",
|
||||
alt: "청주시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">청주시 하수 재이용 시설</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
청주시 하수처리수 재이용 민간투자사업(BTO)
|
||||
<br />
|
||||
공급량 : 35,000㎥/일
|
||||
<br />
|
||||
공급관로 : 8.3㎞
|
||||
<br />
|
||||
사업비 : 622억원
|
||||
<br />
|
||||
수요처 : SK하이닉스 등 일반산업단지
|
||||
<br />
|
||||
지역 : 충청북도 청주시 흥덕구 가락리 135-9번지 일원
|
||||
<br />
|
||||
적용기술 : 분리막 UF, RO
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-sewage-guri" className="scroll-mt-[120px]">
|
||||
<SectionTitle>구리시 도로자동청소시스템</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_01.png",
|
||||
alt: "구리시 도로자동청소시스템 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_02.png",
|
||||
alt: "구리시 도로자동청소시스템 2",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
구리시 도로자동청소시스템 설치사업
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여
|
||||
미세먼지 저감 및 폭염 취약성 완화
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-sewage-hwanggu" className="scroll-mt-[120px]">
|
||||
<SectionTitle>수원시 황구지천</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_01.png",
|
||||
alt: "수원시 황구지천 하수 재이용 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_02.png",
|
||||
alt: "수원시 황구지천 하수 재이용 2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_03.png",
|
||||
alt: "수원시 황구지천 하수 재이용 3",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
수원시 황구지천 하수처리수 재이용사업
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-sewage-paju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>파주시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_01.png",
|
||||
alt: "파주시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
경기도 파주시 하수재이용
|
||||
<br />
|
||||
운정·금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주 LCD
|
||||
산업단지에 공업용수 공급
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="example-sewage-yeosu" className="scroll-mt-[120px]">
|
||||
<SectionTitle>여수시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_01.png",
|
||||
alt: "여수시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
여수시 하수처리수 재이용 민간 투자사업(BTO)
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,71 @@
|
||||
// apps/dm/app/(landing)/reuse/business/layout.tsx
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Lnb from "../_components/Lnb";
|
||||
import { LNB_BUSINESS } from "../_constants/lnb";
|
||||
import { useEffect } from "react";
|
||||
// import { usePathname } from "next/navigation";
|
||||
// import Lnb from "../_components/Lnb";
|
||||
// import BusinessSubNav from "../_components/BusinessSubNav";
|
||||
import {
|
||||
// fetchAnonymousMenu,
|
||||
// findActiveTopMenu,
|
||||
// type MenuItem,
|
||||
} from "@/app/store/common";
|
||||
|
||||
export default function BusinessLayout({ children }: { children: ReactNode }) {
|
||||
// const pathname = usePathname();
|
||||
// const [menus, setMenus] = useState<MenuItem[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
// const loaded = await fetchAnonymousMenu();
|
||||
if (!isMounted) return;
|
||||
// setMenus(loaded);
|
||||
} catch {
|
||||
if (!isMounted) return;
|
||||
// setMenus([]);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// const currentTopMenu = useMemo(
|
||||
// () => findActiveTopMenu(menus, pathname),
|
||||
// [menus, pathname],
|
||||
// );
|
||||
|
||||
// const businessMenus = useMemo(
|
||||
// () => currentTopMenu?.children ?? [],
|
||||
// [currentTopMenu],
|
||||
// );
|
||||
|
||||
return (
|
||||
<div className="flex gap-0">
|
||||
<Lnb title="사례" items={LNB_BUSINESS} />
|
||||
<main className="flex-1 px-[100px] pt-[80px]">
|
||||
{children}
|
||||
</main>
|
||||
<div className="bg-white min-w-[1440px]">
|
||||
|
||||
{/* 좌측 LNB */}
|
||||
{/* <Lnb /> */}
|
||||
|
||||
{/* 컨텐츠 영역 */}
|
||||
<div className="pl-[260px] pt-[83px]">
|
||||
<div className="mx-auto flex max-w-[1660px] gap-16 px-[60px] py-[60px]">
|
||||
|
||||
{/* 본문 */}
|
||||
<main className="min-w-0 flex-1 max-w-[980px]">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* 우측 서브 메뉴 */}
|
||||
<div className="w-[240px] shrink-0">
|
||||
{/* <BusinessSubNav menus={businessMenus} /> */}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
|
||||
import RainExampleSection from "./_components/RainExampleSection";
|
||||
import GreywaterExampleSection from "./_components/GreywaterExampleSection";
|
||||
import SewageExampleSection from "./_components/SewageExampleSection";
|
||||
import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "example-rain",
|
||||
label: "빗물 재이용 사례",
|
||||
sections: [
|
||||
{ id: "example-rain-suwon", label: "경기도 수원시" },
|
||||
{ id: "example-rain-jeju", label: "제주특별자치도" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-greywater",
|
||||
label: "중수 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-greywater-ansan", label: "경기도 안산시" },
|
||||
{ id: "example-greywater-yongin", label: "경기도 용인시" },
|
||||
{ id: "example-greywater-pyeongtaek", label: "경기도 평택시" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-sewage",
|
||||
label: "하수처리수 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-sewage-bolyeong", label: "충청남도 보령시" },
|
||||
{ id: "example-sewage-cheongju", label: "청주시" },
|
||||
{ id: "example-sewage-guri", label: "구리시 도로자동청소시스템" },
|
||||
{ id: "example-sewage-hwanggu", label: "수원시 황구지천" },
|
||||
{ id: "example-sewage-paju", label: "파주시" },
|
||||
{ id: "example-sewage-yeosu", label: "여수시" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-global",
|
||||
label: "해외 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-global-overview", label: "사례 개요" },
|
||||
{ id: "example-global-taiwan", label: "대만" },
|
||||
{ id: "example-global-australia", label: "호주" },
|
||||
{ id: "example-global-singapore", label: "싱가포르" },
|
||||
{ id: "example-global-eu", label: "EU" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function ExamplePage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 사례" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
<div className="max-w-[1300px] pb-24">
|
||||
<RainExampleSection />
|
||||
<Hr className="my-16" />
|
||||
<GreywaterExampleSection />
|
||||
<Hr className="my-16" />
|
||||
<SewageExampleSection />
|
||||
<Hr className="my-16" />
|
||||
<GlobalCaseSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,461 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「2023 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
export default function GreywaterStatusSection() {
|
||||
const title = "중수도(현황)";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
{/* 1) 전체 중수도 설치 및 이용 현황 */}
|
||||
<section
|
||||
id="status-greywater-overall"
|
||||
className="mt-10 scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>전체 중수도 설치 및 이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "576", "633", "682", "740", "819", "842"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "1,494.2", "1,514.4", "1,524.9", "989.1", "1,104.3", "1,015.4"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "39.6", "40.0", "51.1", "47.1", "48.9", "50.7"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "2,223.4", "1,165.2", "6,713.0", "4,681.7", "3,918.2", "898.4"],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "146", "154", "156", "170", "166", "169"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "674.9", "682.4", "736.1", "421.1", "434.9", "465.5"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "20.0", "20.1", "31.2", "22.8", "21.2", "24.7"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "1,819.4", "712.8", "6,479.9", "4,416.8", "3,663.1", "616.1"],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 비대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "430", "479", "526", "570", "653", "673"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "819.3", "832.0", "788.8", "568.0", "669.4", "549.9"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "19.6", "19.5", "19.9", "24.3", "27.7", "26.0"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "404.0", "452.4", "233.1", "264.9", "255.1", "282.3"],
|
||||
],
|
||||
},
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
isFirstCol ? "!font-bold !text-neutral-900" : ""
|
||||
} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 공장 및 발전시설 내 중수도 설치 현황 */}
|
||||
<section
|
||||
id="status-greywater-factory"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>공장 및 발전시설 내 중수도 설치 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "125", "127", "139", "146", "151", "176"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "141.3", "141.0", "146.7", "189.0", "190.3", "166.5"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "33,712", "29,300", "33,110", "22,284", "21,593", "21,349"],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "59", "63", "78", "78", "82", "86"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "108.6", "109.7", "118.1", "159.5", "142.6", "119.4"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "24,847", "24,021", "28,055", "17,959", "12,241", "12,296"],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 비대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "66", "64", "61", "68", "69", "90"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "32.7", "31.4", "28.7", "29.5", "47.7", "47.1"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "8,864.9", "5,278.8", "5,054.7", "4,324.6", "9,353.3", "9,052.4"],
|
||||
],
|
||||
},
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
isFirstCol ? "!font-bold !text-neutral-900" : ""
|
||||
} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 중수도 건축물별 이용량 현황 */}
|
||||
<section
|
||||
id="status-greywater-building"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>중수도 건축물별 이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["공동주택", "804.0", "245.8", "255.1", "282.3", "255.1", "282.3"],
|
||||
["판매시설", "1,447.5", "477.1", "802.1", "713.0", "972.3", "742.4"],
|
||||
["숙박시설", "458.5", "0.4", "177.0", "102.0", "123.3", "140.9"],
|
||||
["업무시설", "20.7", "0.3", "14.3", "44.9", "31.5", "37.4"],
|
||||
["의료시설", "97.7", "7.2", "63.7", "55.7", "85.1", "70.4"],
|
||||
["교육연구시설", "536.6", "1.5", "1,558.4", "772.2", "315.1", "66.7"],
|
||||
["운수·물류시설", "759.2", "34.2", "459.8", "259.8", "252.8", "410.4"],
|
||||
["기타", "453.1", "443.3", "3,126.8", "2,451.5", "1,880.8", "199.7"],
|
||||
["합계", "35,936.3", "30,466.6", "37,487.2", "25,480.6", "24,345.8", "22,247.6"],
|
||||
].map((row, idx) => (
|
||||
<tr
|
||||
key={idx}
|
||||
className={idx === 8 ? "bg-white font-bold" : "bg-white"}
|
||||
>
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 중수도 용도별 이용량 현황 */}
|
||||
<section
|
||||
id="status-greywater-usage"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>중수도 용도별 이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["공업용수", "33,082", "28,719", "32,312", "21,707", "20,991", "20,470"],
|
||||
["세척·살수용수", "860.2", "146.5", "133.8", "100.7", "95.1", "149.9"],
|
||||
["조경용수", "403.7", "99.3", "3,297.0", "2,611.1", "1,827.0", "1,116.7"],
|
||||
["청소용수", "623.9", "764.2", "1,430.6", "879.7", "1,146.2", "286.0"],
|
||||
["수세식화장실용수", "530.0", "646.6", "183.5", "161.2", "216.6", "147.6"],
|
||||
["냉·난방용수", "150.1", "56.2", "57.5", "6.4", "0.9", "0.5"],
|
||||
["친수용수", "120.8", "18.5", "29.3", "8.2", "4.1", "2.8"],
|
||||
["기타", "165.6", "16.1", "43.2", "5.7", "64.0", "73.9"],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
// 공통 섹션 타이틀 컴포넌트
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「2023 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
export default function RainStatusSection() {
|
||||
const title = "빗물이용시설(현황)";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
{/* 1) 빗물이용시설의 건축물별 빗물이용량 현황 */}
|
||||
<section id="status-rain-building" className="mt-10 scroll-mt-[120px]">
|
||||
<SectionTitle>빗물이용시설의 건축물별 빗물이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
style={{ width: "160px" }}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
빗물이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#01ACE5] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2018
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#17AFC7] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2019
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#2CB2A9] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2020
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#42B58B] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2021
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#57B86D] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2022
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#81C784] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["골프장", "6,771.8", "6,417.4", "8,371.6", "10,215.4", "11,360", "10,191.6"],
|
||||
["공공시설", "364", "325.2", "201.9", "244.6", "209.9", "299.6"],
|
||||
["공동주택", "291", "840.5", "804.2", "2,393.0", "2,386", "2,428.4"],
|
||||
["대규모점포", "169.9", "214.4", "58.4", "427.2", "371", "293.3"],
|
||||
["체육시설", "52.6", "44.7", "48.6", "75.3", "71.8", "77.6"],
|
||||
["학교", "33", "25.5", "22", "155.8", "165.4", "32.6"],
|
||||
["기타", "311.6", "901.2", "344.5", "537.8", "564", "621.2"],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 빗물이용시설의 용도별 분포 (2023년 기준) */}
|
||||
<section
|
||||
id="status-rain-purpose-distribution"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>빗물이용시설의 용도별 분포 (2023년 기준)</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
style={{ width: "160px" }}
|
||||
>
|
||||
용도
|
||||
</th>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
개소수
|
||||
<br />
|
||||
(개소)
|
||||
</th>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
저류용량
|
||||
<br />
|
||||
(천m³)
|
||||
</th>
|
||||
<th
|
||||
colSpan={8}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
빗물이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#01ACE5] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
총사용량
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#10AED0] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
공공시설
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#20B0BA] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
공동주택
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#2FB2A5] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
대규모점포
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#3FB58F] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
체육시설
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#4EB77A] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
학교
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#5EB964] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
골프장
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#6DBB4F] py-2 text-[13px] text-white`}
|
||||
style={{ width: "84px" }}
|
||||
>
|
||||
기타
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["청소·환경정화용수", "195", "32.7", "108.8", "24.9", "5.8", "2.6", "4.4", "2.9", "-", "68.1"],
|
||||
["세척·살수용수", "12", "1.4", "37", "35", "-", "-", "1.9", "-", "-", "0.2"],
|
||||
["조경용수", "3,165", "7,440.8", "13,554.1", "168.1", "2,421.9", "290.7", "66.2", "29", "10,044", "534.3"],
|
||||
["냉·난방수", "8", "1.6", "0.1", "-", "-", "-", "-", "0.1", "-", "-"],
|
||||
["공업용수", "6", "14.9", "53.2", "52.8", "-", "-", "-", "-", "-", "0.4"],
|
||||
["농업용수", "6.0", "0.5", "0.6", "-", "-", "-", "-", "-", "-", "0.6"],
|
||||
["기타", "48", "306.1", "155.6", "2.8", "-", "-", "0.6", "0.5", "147.5", "4.2"],
|
||||
["미기재", "103", "22.9", "34.8", "16.0", "0.6", "-", "4.6", "0.2", "-", "13.5"],
|
||||
["합계", "3,543", "7,820.8", "13,944.7", "299.6", "2,428.4", "293.3", "77.6", "32.6", "10,191.6", "621.2"],
|
||||
].map((row, idx) => (
|
||||
<tr
|
||||
key={idx}
|
||||
className={idx === 8 ? "bg-white font-bold" : "bg-white"}
|
||||
>
|
||||
{row.map((val, i) => {
|
||||
const isHighlightedCol = [1, 2].includes(i);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 빗물이용시설의 용도별 빗물이용량 현황 */}
|
||||
<section
|
||||
id="status-rain-purpose-usage"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>빗물이용시설의 용도별 빗물이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
style={{ width: "160px" }}
|
||||
>
|
||||
용도
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
빗물이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#01ACE5] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2018
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#14AFCD] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2019
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#28B2B4] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2020
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#3CB59A] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2021
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#50B880] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2022
|
||||
</th>
|
||||
<th
|
||||
className={`${tableStyles.th} bg-[#6DBB4F] text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["청소·화장실용수", "126.5", "134.1", "96.5", "153.9", "140.5", "108.8"],
|
||||
["세척·살수용수", "14.8", "14.6", "9.5", "49.8", "49.7", "37.0"],
|
||||
["조경용수", "7,629.8", "8,496.2", "9,415.4", "13,360.4", "14,466.3", "13,554.1"],
|
||||
["친수용수", "1.0", "0.1", "0.1", "0.1", "0.1", "0.1"],
|
||||
["공업용수", "152.0", "77.0", "77.0", "75.4", "0.4", "53.2"],
|
||||
["농업용수", "4.4", "4.3", "4.3", "4.3", "0.6", "0.6"],
|
||||
["기타", "37.7", "42.6", "230.7", "394.3", "463.2", "155.6"],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,705 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「2023 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
export default function SewageStatusSection() {
|
||||
const title = "하수처리수 재이용시설(현황)";
|
||||
|
||||
return (
|
||||
<div className="relative bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
{/* 1) 연도별 하수처리수 재이용 현황 */}
|
||||
<section id="status-treated-year" className="mt-10 scroll-mt-[120px]">
|
||||
<SectionTitle>연도별 하수처리수 재이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "180px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
[
|
||||
"개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"671",
|
||||
"681",
|
||||
"698",
|
||||
"704",
|
||||
"714",
|
||||
"717",
|
||||
],
|
||||
[
|
||||
"하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"7,163.7",
|
||||
"7,140.4",
|
||||
"7,375.6",
|
||||
"7,335.8",
|
||||
"7,378.9",
|
||||
"7,537.2",
|
||||
],
|
||||
[
|
||||
"재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"1,112.7",
|
||||
"1,148.7",
|
||||
"1,148.7",
|
||||
"1,127.0",
|
||||
"1,136.8",
|
||||
"1,137.5",
|
||||
],
|
||||
[
|
||||
"재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>",
|
||||
"17.4",
|
||||
"16.5",
|
||||
"15.5",
|
||||
"14.1",
|
||||
"14.3",
|
||||
"14.4",
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
[
|
||||
"개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"68",
|
||||
"82",
|
||||
"94",
|
||||
"97",
|
||||
"101",
|
||||
"118",
|
||||
],
|
||||
[
|
||||
"하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"761.2",
|
||||
"839.0",
|
||||
"935.3",
|
||||
"1,158.7",
|
||||
"1,220.6",
|
||||
"1,473.6",
|
||||
],
|
||||
[
|
||||
"재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"162.2",
|
||||
"190.6",
|
||||
"218.5",
|
||||
"244.6",
|
||||
"247.2",
|
||||
"324.9",
|
||||
],
|
||||
[
|
||||
"재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>",
|
||||
"24.7",
|
||||
"25.4",
|
||||
"27.8",
|
||||
"25.7",
|
||||
"24.2",
|
||||
"24.1",
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
[
|
||||
"개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"603",
|
||||
"599",
|
||||
"604",
|
||||
"607",
|
||||
"613",
|
||||
"599",
|
||||
],
|
||||
[
|
||||
"하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"6,402.5",
|
||||
"6,301.4",
|
||||
"6,440.3",
|
||||
"6,177.1",
|
||||
"6,158.3",
|
||||
"6,063.5",
|
||||
],
|
||||
[
|
||||
"재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
"950.6",
|
||||
"958.1",
|
||||
"926.2",
|
||||
"882.4",
|
||||
"889.5",
|
||||
"812.7",
|
||||
],
|
||||
[
|
||||
"재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>",
|
||||
"16.5",
|
||||
"15.3",
|
||||
"13.6",
|
||||
"12.2",
|
||||
"12.6",
|
||||
"12.5",
|
||||
],
|
||||
],
|
||||
},
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
isFirstCol ? "!font-bold !text-neutral-900" : ""
|
||||
} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 용도별 하수처리수 재이용 현황 */}
|
||||
<section id="status-treated-usage" className="scroll-mt-[120px]">
|
||||
<SectionTitle>용도별 하수처리수 재이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "180px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} ${tableStyles.headerBg}`}>
|
||||
구분
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{ label: "총 재이용량", type: "total", values: ["1,112.7", "1,148.7", "1,144.7", "1,127.0", "1,136.8", "1,137.5"] },
|
||||
{ label: "장내 재이용", type: "category", values: ["520.6", "519.8", "513.9", "520.3", "502.2", "490.0"] },
|
||||
{ label: "세척수", type: "red-item", values: ["177.9", "174.4", "169.4", "180.9", "196.8", "191.2"] },
|
||||
{ label: "냉각수", type: "red-item", values: ["45.5", "46.8", "50.4", "40.6", "38.3", "41.7"] },
|
||||
{ label: "청소수", type: "red-item", values: ["17.6", "21.2", "21.2", "21.7", "20.8", "20.5"] },
|
||||
{ label: "조경용수", type: "red-item", values: ["0.1", "0.1", "-", "-", "-", "-"] },
|
||||
{ label: "회생용수", type: "red-item", values: ["15.8", "16.7", "17.8", "15.7", "14.4", "14.0"] },
|
||||
{ label: "장내급수도", type: "red-item", values: ["2.4", "-", "-", "-", "-", "-"] },
|
||||
{ label: "장내기타용수", type: "red-item", values: ["261.3", "260.6", "255.0", "261.1", "231.8", "222.6"] },
|
||||
{ label: "장외 재이용", type: "category", values: ["592.1", "628.9", "630.8", "606.7", "634.6", "647.5"] },
|
||||
{ label: "청소·화장실용수", type: "blue-item", values: ["0.6", "0.4", "0.4", "0.4", "0.4", "0.4"] },
|
||||
{ label: "세척·살수용수", type: "blue-item", values: ["5.6", "9.9", "2.9", "2.1", "3.0", "3.6"] },
|
||||
{ label: "조경용수", type: "blue-item", values: ["10.3", "16.4", "5.9", "4.5", "8.9", "9.5"] },
|
||||
{ label: "친수용수", type: "blue-item", values: ["7.8", "3.1", "3.1", "3.0", "10.9", "10.5"] },
|
||||
{ label: "하천유지용수", type: "blue-item", values: ["480.5", "482.5", "496.2", "442.7", "471.7", "484.0"] },
|
||||
{ label: "농업용수", type: "blue-item", values: ["12.2", "11.9", "13.4", "10.7", "10.6", "11.4"] },
|
||||
{ label: "지하수 충전", type: "blue-item", values: ["0.0", "0.0", "0.0", "13.4", "0.0", "0.0"] },
|
||||
{ label: "공업용수", type: "blue-item", values: ["75.2", "104.6", "108.9", "129.9", "129.0", "128.1"] },
|
||||
].map((row, idx) => {
|
||||
const isSubItem = row.type === "red-item" || row.type === "blue-item";
|
||||
const isLastInGroup = idx === 8 || idx === 17;
|
||||
|
||||
return (
|
||||
<tr key={idx} className="bg-white">
|
||||
<td className={`${tableStyles.td} relative`}>
|
||||
{isSubItem ? (
|
||||
<>
|
||||
<div
|
||||
className={`absolute left-[1px] w-[16px] bg-white z-10 top-[-2px] ${
|
||||
isLastInGroup ? "bottom-[0px]" : "bottom-[-2px]"
|
||||
}`}
|
||||
/>
|
||||
<div
|
||||
className={`absolute left-[16px] w-[4px] z-20 ${
|
||||
row.type === "red-item" ? "bg-[#FF8B8B]" : "bg-[#4ECBFF]"
|
||||
} top-[-2px] ${
|
||||
isLastInGroup ? "bottom-[0px]" : "bottom-[-2px]"
|
||||
}`}
|
||||
/>
|
||||
<span className="text-[#4f4f4f] font-bold block w-full text-center relative z-30 translate-x-[8px]">
|
||||
{row.label}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="font-bold text-neutral-900 block w-full text-center">
|
||||
{row.label}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
|
||||
{row.values.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
[0, 2, 4].includes(i) ? "bg-[#EAF5F5]" : ""
|
||||
}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 하수처리시설 설치 및 이용 현황 */}
|
||||
<section id="status-treated-facility" className="scroll-mt-[120px]">
|
||||
<SectionTitle>하수처리시설 설치 및 이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>
|
||||
2018
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>
|
||||
2019
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>
|
||||
2020
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>
|
||||
2021
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>
|
||||
2022
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>
|
||||
2023
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
[
|
||||
"시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"671",
|
||||
"681",
|
||||
"698",
|
||||
"704",
|
||||
"714",
|
||||
"717",
|
||||
],
|
||||
[
|
||||
"시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"25,846",
|
||||
"26,067",
|
||||
"26,244",
|
||||
"26,492",
|
||||
"26,586",
|
||||
"26,766",
|
||||
],
|
||||
[
|
||||
"처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"1,112.7",
|
||||
"1,148.7",
|
||||
"1,144.7",
|
||||
"1,127.0",
|
||||
"1,136.8",
|
||||
"1,137.5",
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
[
|
||||
"시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"68",
|
||||
"82",
|
||||
"94",
|
||||
"97",
|
||||
"101",
|
||||
"118",
|
||||
],
|
||||
[
|
||||
"시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"3,088.9",
|
||||
"3,359.2",
|
||||
"3,612.6",
|
||||
"4,412.2",
|
||||
"4,692.3",
|
||||
"5,503.8",
|
||||
],
|
||||
[
|
||||
"처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"162.2",
|
||||
"190.6",
|
||||
"218.5",
|
||||
"244.6",
|
||||
"247.2",
|
||||
"324.9",
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
[
|
||||
"시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
"603",
|
||||
"599",
|
||||
"604",
|
||||
"607",
|
||||
"613",
|
||||
"599",
|
||||
],
|
||||
[
|
||||
"시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"22,757",
|
||||
"22,708",
|
||||
"22,631",
|
||||
"22,080",
|
||||
"21,894",
|
||||
"21,262",
|
||||
],
|
||||
[
|
||||
"처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
"950.6",
|
||||
"958.1",
|
||||
"926.2",
|
||||
"882.4",
|
||||
"889.5",
|
||||
"812.7",
|
||||
],
|
||||
],
|
||||
},
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
isFirstCol ? "!font-bold !text-neutral-900" : ""
|
||||
} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 시도별 하수처리수 재이용 현황 */}
|
||||
<section
|
||||
id="status-treated-region"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>시도별 하수처리수 재이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<div className="mb-2 text-right text-[13px] text-neutral-600">
|
||||
(단위: 백만m³/일)
|
||||
</div>
|
||||
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
시도
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
연간
|
||||
<br />
|
||||
하수
|
||||
<br />
|
||||
처리량
|
||||
</th>
|
||||
<th
|
||||
colSpan={7}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
재이용현황
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
재이용률
|
||||
<br />
|
||||
(%)
|
||||
</th>
|
||||
<th
|
||||
rowSpan={3}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
비고
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
계
|
||||
</th>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
장내
|
||||
</th>
|
||||
<th
|
||||
colSpan={5}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-1`}
|
||||
>
|
||||
장외
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white py-2 text-[16px]`}>
|
||||
소계
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#1FB1C2] text-white py-2 text-[13px]`}>
|
||||
하천유지용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3EB59F] text-white py-2 text-[16px]`}>
|
||||
공업용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#5DB97C] text-white py-2 text-[16px]`}>
|
||||
농업용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white py-2 text-[16px]`}>
|
||||
기타용수
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["합계", "7,537.2", "1,137.5", "490.0", "647.5", "484.0", "128.1", "11.4", "24.0", "15.1%", ""],
|
||||
["서울특별시", "1,567.0", "114.9", "59.3", "55.6", "55.1", "-", "-", "0.5", "7.3%", ""],
|
||||
["부산광역시", "491.5", "146.2", "120.7", "25.5", "25.3", "-", "-", "0.2", "29.7%", ""],
|
||||
["대구광역시", "432.7", "102.4", "38.2", "64.2", "64.1", "-", "-", "0.1", "23.7%", ""],
|
||||
["인천광역시", "302.3", "57.7", "11.6", "46.1", "29.2", "12.1", "0.5", "4.3", "19.1%", ""],
|
||||
["광주광역시", "211.1", "28.7", "6.5", "22.3", "22.2", "-", "-", "0.1", "13.6%", ""],
|
||||
["대전광역시", "244.6", "11.9", "11.9", "0.68", "0.0", "-", "-", "0.1", "4.9%", ""],
|
||||
["울산광역시", "235.1", "36.1", "6.1", "29.9", "29.7", "0.2", "-", "-", "15.3%", ""],
|
||||
["세종특별자치시", "38.5", "2.5", "2.0", "0.5", "0.4", "-", "0.2", "-", "6.6%", ""],
|
||||
["경기도", "1,926.8", "313.3", "122.2", "191.0", "153.0", "33.3", "0.4", "4.3", "16.3%", ""],
|
||||
["강원도", "230.4", "19.6", "15.4", "4.2", "2.1", "-", "-", "2.1", "8.5%", ""],
|
||||
["충청북도", "190.9", "15.6", "6.3", "9.2", "7.4", "0.6", "-", "1.2", "8.2%", ""],
|
||||
["충청남도", "241.5", "41.4", "9.8", "31.5", "17.7", "8.6", "4.5", "0.8", "17.1%", ""],
|
||||
["전라북도", "292.4", "42.7", "7.5", "35.1", "33.5", "0.2", "1.0", "0.5", "14.6%", ""],
|
||||
["전라남도", "254.0", "23.6", "12.3", "11.3", "8.7", "0.0", "1.0", "1.6", "9.3%", ""],
|
||||
["경상북도", "378.4", "129.1", "33.7", "95.3", "20.8", "72.5", "0.8", "1.3", "34.1%", ""],
|
||||
["경상남도", "411.3", "50.1", "25.0", "25.0", "14.7", "0.5", "3.0", "6.9", "12.2%", ""],
|
||||
["제주특별자치도", "88.6", "1.7", "1.5", "0.12", "-", "-", "0.1", "-", "1.9%", ""],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${
|
||||
i === 0 ? "!font-bold !text-neutral-900" : ""
|
||||
} ${[1, 3, 9].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// DM 물 재이용 현황 페이지
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import RainStatusSection from "./_components/RainStatusSection";
|
||||
import GreywaterStatusSection from "./_components/GreywaterStatusSection";
|
||||
import SewageStatusSection from "./_components/SewageStatusSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "status-rain",
|
||||
label: "빗물이용시설(현황)",
|
||||
sections: [
|
||||
{
|
||||
id: "status-rain-building",
|
||||
label: "빗물이용시설의 건축물별 빗물이용량 현황",
|
||||
},
|
||||
{
|
||||
id: "status-rain-purpose-distribution",
|
||||
label: "빗물이용시설의 용도별 분포 (2023년 기준)",
|
||||
},
|
||||
{
|
||||
id: "status-rain-purpose-usage",
|
||||
label: "빗물이용시설의 용도별 빗물이용량 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "status-greywater",
|
||||
label: "중수도(현황)",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{
|
||||
id: "status-greywater-overall",
|
||||
label: "전체 중수도 설치 및 이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-factory",
|
||||
label: "공장 및 발전시설 내 중수도 설치 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-building",
|
||||
label: "중수도 건축물별 이용량 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-usage",
|
||||
label: "중수도 용도별 이용량 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "status-sewage",
|
||||
label: "하수처리수 재이용시설(현황)",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{
|
||||
id: "status-treated-year",
|
||||
label: "연도별 하수처리수 재이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-usage",
|
||||
label: "용도별 하수처리수 재이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-facility",
|
||||
label: "하수처리시설 설치 및 이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-region",
|
||||
label: "시도별 하수처리수 재이용 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function StatusPage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 현황"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 현황", href: "/status" },
|
||||
]}
|
||||
/>
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
<div className="max-w-[1300px] pb-24">
|
||||
<RainStatusSection />
|
||||
<GreywaterStatusSection />
|
||||
<SewageStatusSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import lawPlan from "@/public/images/landing/intro/law_timeline.png";
|
||||
import planPerformance from "@/public/images/landing/intro/performance_left.png";
|
||||
import domesticChange from "@/public/images/landing/intro/domestic_textbox.png";
|
||||
import policyHistory from "@/public/images/landing/intro/policy_timeline.png";
|
||||
import lawRelation from "@/public/images/landing/intro/rel_note.png";
|
||||
import domesticLinkage from "@/public/images/landing/intro/linkage.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LawIntroSection() {
|
||||
const title = "물 재이용 법";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="law-plan"
|
||||
title="법률 제·개정 및 기본계획 수립"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={lawPlan} alt="법률 제·개정 및 기본계획 수립" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="plan-performance"
|
||||
title="제2차 계획 대비 실적 현황"
|
||||
>
|
||||
<ImgCard src={planPerformance} alt="제2차 계획 대비 실적 현황" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell id="domestic-change" title="우리나라의 수자원 현황 및 물이용 여건">
|
||||
<ImgCard src={domesticChange} alt="우리나라의 수자원 현황 및 물이용 여건" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="policy-history"
|
||||
title="물 재이용 정책 변화 추이"
|
||||
>
|
||||
<ImgCard src={policyHistory} alt="물 재이용 정책 변화 추이" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="law-relation"
|
||||
title="타법령과의 관계 검토"
|
||||
>
|
||||
<ImgCard src={lawRelation} alt="타법령과의 관계 검토" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="domestic-linkage"
|
||||
title="주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요"
|
||||
>
|
||||
<ImgCard src={domesticLinkage} alt="주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import rainSummary from "@/public/images/landing/intro/rainreuse_intro.png";
|
||||
import rainDuty from "@/public/images/landing/intro/rainreuse_mandatory.png";
|
||||
import rainDesign from "@/public/images/landing/intro/rainwater_design.png";
|
||||
import rainStandard from "@/public/images/landing/intro/rainwater_standards.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RainIntroSection() {
|
||||
const title = "빗물 이용시설";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell id="rain-intro" title="빗물 재이용 소개" className="mt-10">
|
||||
<ImgCard src={rainSummary} alt="빗물 재이용 소개" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="rain-mandatory"
|
||||
title="빗물이용시설 설치 관리 법적 의무 대상"
|
||||
>
|
||||
<ImgCard src={rainDuty} alt="빗물이용시설 설치 관리 법적 의무 대상" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell id="rain-design" title="빗물이용시설 설계 방안">
|
||||
<ImgCard src={rainDesign} alt="빗물이용시설 설계 방안" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="rain-standards"
|
||||
title="빗물이용시설 시설기준 및 관리기준"
|
||||
>
|
||||
<ImgCard src={rainStandard} alt="빗물이용시설 시설기준 및 관리기준" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import greywaterIntro from "@/public/images/landing/intro/greywater_intro.png";
|
||||
import greywaterMandatory from "@/public/images/landing/intro/greywater_mandatory.png";
|
||||
import greywaterStandard from "@/public/images/landing/intro/greywater_standard.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ReuseWaterIntroSection() {
|
||||
const title = "중수도";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="greywater-intro"
|
||||
title="중수 재이용 소개"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={greywaterIntro} alt="중수 재이용 소개" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="greywater-mandatory"
|
||||
title="중수도 설치 의무 대상"
|
||||
>
|
||||
<ImgCard src={greywaterMandatory} alt="중수도 설치 의무 대상" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="greywater-standard"
|
||||
title="중수도 시설기준 및 관리기준"
|
||||
>
|
||||
<ImgCard src={greywaterStandard} alt="중수도 시설기준 및 관리기준" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import treatedwaterIntro from "@/public/images/landing/intro/treatedwater_intro.png";
|
||||
import treatedwaterMandatory from "@/public/images/landing/intro/treatedwater_mandatory.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TreatedIntroSection() {
|
||||
const title = "하수처리수";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="treatedwater-intro"
|
||||
title="하수처리수 소개"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={treatedwaterIntro} alt="하수처리수" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="treatedwater-mandatory"
|
||||
title="하수처리수 재이용시설 의무 대상"
|
||||
>
|
||||
<ImgCard
|
||||
src={treatedwaterMandatory}
|
||||
alt="하수처리수 재이용시설 의무 대상"
|
||||
/>
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// DM 물 재이용 소개 페이지
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import LawIntroSection from "./_components/LawIntroSection";
|
||||
import RainIntroSection from "./_components/RainIntroSection";
|
||||
import ReuseWaterIntroSection from "./_components/ReuseWaterIntroSection";
|
||||
import TreatedIntroSection from "./_components/TreatedIntroSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "intro-law",
|
||||
label: "물 재이용 법",
|
||||
sections: [
|
||||
{ id: "law-plan", label: "법률 제·개정 및 기본계획 수립" },
|
||||
{ id: "plan-performance", label: "제2차 계획 대비 실적 현황" },
|
||||
{ id: "domestic-change", label: "우리나라의 수자원 현황 및 물이용 여건" },
|
||||
{ id: "policy-history", label: "물 재이용 정책 변화 추이" },
|
||||
{ id: "law-relation", label: "타법령과의 관계 검토" },
|
||||
{ id: "domestic-linkage", label: "주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-rain",
|
||||
label: "빗물 이용시설",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "rain-intro", label: "빗물 재이용 소개" },
|
||||
{
|
||||
id: "rain-mandatory",
|
||||
label: "빗물이용시설 설치 관리 법적 의무 대상",
|
||||
},
|
||||
{ id: "rain-design", label: "빗물이용시설 설계 방안" },
|
||||
{
|
||||
id: "rain-standards",
|
||||
label: "빗물이용시설 시설기준 및 관리기준",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-greywater",
|
||||
label: "중수도",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "greywater-intro", label: "중수 재이용 소개" },
|
||||
{ id: "greywater-mandatory", label: "중수도 설치 의무 대상" },
|
||||
{ id: "greywater-standard", label: "중수도 시설기준 및 관리기준" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-treated",
|
||||
label: "하수처리수",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "treatedwater-intro", label: "하수처리수 소개" },
|
||||
{
|
||||
id: "treatedwater-mandatory",
|
||||
label: "하수처리수 재이용시설 의무 대상",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function IntroPage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 소개"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro" },
|
||||
]}
|
||||
/>
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
{/* 본문 */}
|
||||
<div className="max-w-[1300px] pb-24">
|
||||
<LawIntroSection />
|
||||
<RainIntroSection />
|
||||
<ReuseWaterIntroSection />
|
||||
<TreatedIntroSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import "@/app/commonOrigin.css";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { mapService } from "../service";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
|
||||
@@ -22,32 +22,36 @@ export default function SelectedFcltList({
|
||||
|
||||
const TableContent = (
|
||||
<>
|
||||
<thead className="bg-gray-100 text-gray-600 text-[11px] font-medium border-b border-cyan-800">
|
||||
<thead className="bg-gray-100 text-gray-700 text-[13px]">
|
||||
<tr>
|
||||
<th className="px-2 py-1.5 text-left w-1/3">시설명</th>
|
||||
<th className="px-2 py-1.5 text-right whitespace-nowrap">
|
||||
처리량(㎥/일)
|
||||
<th className="px-3 py-2 text-left border-b">시설명</th>
|
||||
<th className="px-3 py-2 text-right border-b">
|
||||
하수처리량
|
||||
<br />
|
||||
(㎥/일)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-right whitespace-nowrap">
|
||||
재이용량(㎥/일)
|
||||
<th className="px-3 py-2 text-right border-b">
|
||||
재이용량
|
||||
<br />
|
||||
(㎥/일)
|
||||
</th>
|
||||
<th className="px-2 py-1.5 text-center w-10">삭제</th>
|
||||
<th className="px-3 py-2 border-b">삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((item) => (
|
||||
<tr key={item.pbnstRuseFcltId} className="border-b border-gray-100 last:border-none hover:bg-gray-50/50">
|
||||
<td className="px-2 py-1.5 text-[11px] font-medium text-gray-800 break-keep">{item.ruseUtztnFcltNm}</td>
|
||||
<td className="px-2 py-1.5 text-right text-[11px] text-gray-600">
|
||||
<tr key={item.pbnstRuseFcltId} className="border-b last:border-none">
|
||||
<td className="px-3 py-2 text-[12px]">{item.ruseUtztnFcltNm}</td>
|
||||
<td className="px-3 py-2 text-right text-[12px]">
|
||||
{Number(item.splyExtraCpct || 0).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-right text-[11px] text-gray-600">
|
||||
<td className="px-3 py-2 text-right text-[12px]">
|
||||
{Number(item.ruseCpct || 0).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-2 py-1.5 text-center">
|
||||
<td className="px-3 py-2 text-center">
|
||||
<button
|
||||
onClick={() => handleDelete(item.pbnstRuseFcltId)}
|
||||
className="text-gray-400 hover:text-red-500 text-[11px] font-bold transition-colors"
|
||||
className="text-red-500 hover:text-red-700 text-sm font-bold"
|
||||
aria-label="삭제"
|
||||
title="삭제"
|
||||
>
|
||||
@@ -61,12 +65,12 @@ export default function SelectedFcltList({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="fixed right-20 top-[3.5rem] w-[22rem] max-h-[80vh] overflow-hidden bg-white/95 backdrop-blur-sm shadow-[0_4px_12px_rgba(0,0,0,0.1)] rounded-lg border border-gray-200 z-50 flex flex-col">
|
||||
<h3 className="bg-cyan-700 text-white text-[13px] font-semibold p-2">
|
||||
<div className="fixed right-40 top-[8rem] w-[40rem] max-h-[130rem] overflow-auto bg-white shadow-lg rounded p-4 border border-gray-300 z-50">
|
||||
<h3 className="bg-cyan-700 text-white text-xl font-bold mb-2 p-2">
|
||||
선택된 시설
|
||||
</h3>
|
||||
<div className="overflow-y-auto w-full">
|
||||
<table className="w-full text-sm text-gray-800 border-t border-gray-200 table-fixed">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm text-gray-800 border border-gray-200">
|
||||
{TableContent}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
/**
|
||||
* 수요처 등록/수정
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo, useRef, useState, type ReactNode, type RefObject } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import PostcodeModal from "@/components/PostcodeModal";
|
||||
import type { AddrItem } from "@/app/store/sdmMng/postcode-service";
|
||||
@@ -15,6 +19,7 @@ type SessionUser = {
|
||||
dmdCoNm?: string;
|
||||
zip?: string;
|
||||
daddr?: string;
|
||||
daddr2?: string;
|
||||
telno?: string;
|
||||
ctpvCd?: string;
|
||||
sggCd?: string;
|
||||
@@ -153,7 +158,12 @@ function SelectBox({
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function DemandPlaceRegisterPage() {
|
||||
const router = useRouter();
|
||||
const { Common, initializeCommonData } = useDmStore((state) => state);
|
||||
const { selectSidoData, selectSigunData } = Common;
|
||||
|
||||
@@ -166,7 +176,7 @@ export default function DemandPlaceRegisterPage() {
|
||||
const [company, setCompany] = useState("");
|
||||
const [bizNo, setBizNo] = useState("");
|
||||
const [workerNo, setWorkerNo] = useState("");
|
||||
const [noWorkerNo, setNoWorkerNo] = useState(false);
|
||||
const [noWorkerNo, setNoWorkerNo] = useState(true); // 종사업자번호 해당없음 default 선택으로 설정
|
||||
|
||||
const [zip, setZip] = useState("");
|
||||
const [addr1, setAddr1] = useState("");
|
||||
@@ -176,6 +186,9 @@ export default function DemandPlaceRegisterPage() {
|
||||
const detailAddrRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const [tel, setTel] = useState("");
|
||||
const [currentPw, setCurrentPw] = useState("");
|
||||
const [isCurrentPwVerified, setIsCurrentPwVerified] = useState(false);
|
||||
const [isCheckingCurrentPw, setIsCheckingCurrentPw] = useState(false);
|
||||
const [pw, setPw] = useState("");
|
||||
const [pw2, setPw2] = useState("");
|
||||
|
||||
@@ -214,7 +227,7 @@ export default function DemandPlaceRegisterPage() {
|
||||
setCompany(String(user.dmdCoNm ?? ""));
|
||||
setZip(String(user.zip ?? ""));
|
||||
setAddr1(String(user.daddr ?? ""));
|
||||
setAddr2("");
|
||||
setAddr2(String(user.daddr2 ?? ""));
|
||||
setTel(formatKoreanPhoneNumber(String(user.telno ?? "")));
|
||||
setSido(String(user.ctpvCd ?? ""));
|
||||
setSigungu(String(user.sggCd ?? ""));
|
||||
@@ -240,6 +253,11 @@ export default function DemandPlaceRegisterPage() {
|
||||
return () => clearTimeout(timer);
|
||||
}, [addr1, focusDetailAddrOnClose, isPostcodeModalOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editMode) return;
|
||||
setIsCurrentPwVerified(false);
|
||||
}, [bizNo, workerNo, noWorkerNo, editMode]);
|
||||
|
||||
const sidoOptions = useMemo<Option[]>(() => {
|
||||
const mapped = selectSidoData
|
||||
.filter((item) => item.val !== "0")
|
||||
@@ -277,6 +295,57 @@ export default function DemandPlaceRegisterPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const checkCurrentPassword = async () => {
|
||||
const bizDigits = bizNo.replace(/\D/g, "").slice(0, 10);
|
||||
const workerDigits = workerNo.replace(/\D/g, "").slice(0, 4);
|
||||
const password = currentPw.trim();
|
||||
|
||||
if (!bizDigits) return alert("사업자등록번호를 입력해 주세요.");
|
||||
if (!/^\d{10}$/.test(bizDigits)) {
|
||||
return alert("사업자등록번호는 10자리 숫자만 입력 가능합니다.");
|
||||
}
|
||||
if (!noWorkerNo) {
|
||||
if (!workerDigits) return alert("종사업자등록번호를 입력해 주세요.");
|
||||
if (!/^\d{4}$/.test(workerDigits)) {
|
||||
return alert("종사업자등록번호는 4자리 숫자만 입력 가능합니다.");
|
||||
}
|
||||
}
|
||||
if (!password) return alert("기존 비밀번호를 입력해 주세요.");
|
||||
|
||||
const fullDmdInfoId = bizDigits + (noWorkerNo ? "0000" : workerDigits);
|
||||
const payload = {
|
||||
dmdInfoId: fullDmdInfoId,
|
||||
dmdAplyPswd: password,
|
||||
};
|
||||
|
||||
try {
|
||||
setIsCheckingCurrentPw(true);
|
||||
const response = await postFetch("/reuseFc/request/checkPassword", payload);
|
||||
const rawResult = await response.json().catch(() => null);
|
||||
const checkedCount =
|
||||
typeof rawResult === "number"
|
||||
? rawResult
|
||||
: typeof rawResult?.data === "number"
|
||||
? rawResult.data
|
||||
: Number(rawResult ?? 0);
|
||||
|
||||
if (checkedCount >= 1) {
|
||||
setIsCurrentPwVerified(true);
|
||||
alert("기존 비밀번호가 확인되었습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
setIsCurrentPwVerified(false);
|
||||
alert("기존 비밀번호가 일치하지 않습니다.");
|
||||
} catch (error) {
|
||||
setIsCurrentPwVerified(false);
|
||||
console.error("Failed to check current password", error);
|
||||
alert("기존 비밀번호 확인에 실패하였습니다.");
|
||||
} finally {
|
||||
setIsCheckingCurrentPw(false);
|
||||
}
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
const bizDigits = bizNo.replace(/\D/g, "").slice(0, 10);
|
||||
const workerDigits = workerNo.replace(/\D/g, "").slice(0, 4);
|
||||
@@ -301,9 +370,11 @@ export default function DemandPlaceRegisterPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const mergedAddress = [addr1.trim(), addr2.trim()].filter(Boolean).join(" ");
|
||||
if (!mergedAddress) return alert("사업자주소를 입력해 주세요.");
|
||||
if (mergedAddress.length > 40) return alert("주소는 40자 이내로 입력해주세요.");
|
||||
const baseAddress = addr1.trim();
|
||||
const detailAddress = addr2.trim();
|
||||
if (!baseAddress && !detailAddress) return alert("사업자주소를 입력해 주세요.");
|
||||
if (baseAddress.length > 40) return alert("주소는 40자 이내로 입력해주세요.");
|
||||
if (detailAddress.length > 40) return alert("상세 주소는 40자 이내로 입력해주세요.");
|
||||
|
||||
if (!telDigits) return alert("전화번호를 입력해 주세요.");
|
||||
if (!/^\d{8,11}$/.test(telDigits)) {
|
||||
@@ -317,8 +388,15 @@ export default function DemandPlaceRegisterPage() {
|
||||
}
|
||||
if (!passwordConfirm) return alert("비밀번호 확인을 입력해 주세요.");
|
||||
if (password !== passwordConfirm) return alert("비밀번호가 일치하지 않습니다.");
|
||||
} else if ((password || passwordConfirm) && password !== passwordConfirm) {
|
||||
return alert("비밀번호가 일치하지 않습니다.");
|
||||
} else { //수정
|
||||
if (password || passwordConfirm) {
|
||||
if (!password) return alert("비밀번호를 입력해 주세요.");
|
||||
if (password.length < 8 || password.length > 16) {
|
||||
return alert("비밀번호는 8자 이상 16자 이내로 입력해주세요.");
|
||||
}
|
||||
if (!passwordConfirm) return alert("비밀번호 확인을 입력해 주세요.");
|
||||
if (password !== passwordConfirm) return alert("비밀번호가 일치하지 않습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
const fullDmdInfoId = bizDigits + (noWorkerNo ? "0000" : workerDigits);
|
||||
@@ -326,6 +404,8 @@ export default function DemandPlaceRegisterPage() {
|
||||
if (!editMode) {
|
||||
const isDuplicate = await checkDuplicate(fullDmdInfoId);
|
||||
if (isDuplicate) return alert("이미 등록된 사업자등록번호입니다.");
|
||||
} else if (!isCurrentPwVerified) {
|
||||
return alert("기존 비밀번호 확인 후 수정해 주세요.");
|
||||
}
|
||||
|
||||
const myUUID = getCookie(TOKEN_ID_COOKIE) || FALLBACK_UUID;
|
||||
@@ -337,7 +417,8 @@ export default function DemandPlaceRegisterPage() {
|
||||
dmdInfoId: fullDmdInfoId,
|
||||
bzmnNo: bizDigits,
|
||||
zip: zip.trim(),
|
||||
daddr: mergedAddress,
|
||||
daddr: baseAddress,
|
||||
daddr2: detailAddress,
|
||||
telno: telDigits,
|
||||
rgtr: (sessionUser?.rgtr as string) || myUUID,
|
||||
};
|
||||
@@ -363,11 +444,24 @@ export default function DemandPlaceRegisterPage() {
|
||||
alert("수요처 수정 완료!");
|
||||
} else {
|
||||
await postFetch("/reuseFc/request", payload);
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
const nextSessionUser = {
|
||||
...(sessionUser ?? {}),
|
||||
...payload,
|
||||
};
|
||||
delete nextSessionUser.dmdAplyPswd;
|
||||
sessionStorage.setItem("user", JSON.stringify(nextSessionUser));
|
||||
}
|
||||
|
||||
alert("수요처 등록 완료!");
|
||||
router.push("/apply/dmdinfo"); // 수요정보등록
|
||||
}
|
||||
|
||||
setPw("");
|
||||
setPw2("");
|
||||
setCurrentPw("");
|
||||
setIsCurrentPwVerified(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to submit demand place", error);
|
||||
alert(editMode ? "수요처 수정에 실패하였습니다." : "수요처 등록에 실패하였습니다.");
|
||||
@@ -401,177 +495,234 @@ export default function DemandPlaceRegisterPage() {
|
||||
const pageTitle = editMode ? "수요처수정" : "수요처등록";
|
||||
|
||||
return (
|
||||
<main className="bg-white">
|
||||
<SubPageHeader
|
||||
title={pageTitle}
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "수요신청", href: "/reuse/apply" },
|
||||
{ label: pageTitle },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
<FormRow label="시도" required>
|
||||
<SelectBox
|
||||
value={sido}
|
||||
onChange={(v) => {
|
||||
setSido(v);
|
||||
setSigungu("");
|
||||
}}
|
||||
options={sidoOptions}
|
||||
/>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="시군구" required>
|
||||
<select
|
||||
value={sigungu}
|
||||
onChange={(e) => setSigungu(e.target.value)}
|
||||
className="h-11 w-[220px] rounded border border-neutral-300 bg-white px-3 text-sm text-neutral-800 focus:border-neutral-400 focus:outline-none"
|
||||
>
|
||||
{sigunguOptions.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{o.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="회사명" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={company}
|
||||
onChange={setCompany}
|
||||
placeholder="회사명을 입력해주세요."
|
||||
<main className="min-w-0 flex-1">
|
||||
<div className="relative">
|
||||
{/* 서브 타이틀 + 브레드크럼 */}
|
||||
<SubPageHeader
|
||||
title={pageTitle}
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "수요신청", href: "/apply/dmdco" },
|
||||
{ label: pageTitle },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
{/* 컨텐츠 영역 */}
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
{/* 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{pageTitle}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{/* 구분선 */}
|
||||
<Hr className="mt-[40px]" />
|
||||
|
||||
<FormRow label="사업자등록번호" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={formatBusinessNumber(bizNo)}
|
||||
onChange={(v) => setBizNo(v.replace(/\D/g, "").slice(0, 10))}
|
||||
placeholder="사업자 등록번호를 입력해주세요."
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
{/* 필수값 안내 */}
|
||||
<p className="my-4 text-[16px] text-neutral-600 font-medium">
|
||||
<span className="text-red-500">*</span> 는 필수 값입니다.
|
||||
</p>
|
||||
|
||||
<Divider />
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
<FormRow label="시도" required>
|
||||
<SelectBox
|
||||
value={sido}
|
||||
onChange={(v) => {
|
||||
setSido(v);
|
||||
setSigungu("");
|
||||
}}
|
||||
options={sidoOptions}
|
||||
/>
|
||||
</FormRow>
|
||||
|
||||
<FormRow label="종사자등록번호">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<div className="w-[420px] max-w-full">
|
||||
<TextInput
|
||||
value={workerNo}
|
||||
onChange={(v) => setWorkerNo(v.replace(/\D/g, "").slice(0, 4))}
|
||||
placeholder="종사자등록번호를 입력해주세요."
|
||||
disabled={noWorkerNo}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
<FormRow label="시군구" required>
|
||||
<select
|
||||
value={sigungu}
|
||||
onChange={(e) => setSigungu(e.target.value)}
|
||||
className="h-11 w-[220px] rounded border border-neutral-300 bg-white px-3 text-sm text-neutral-800 focus:border-neutral-400 focus:outline-none"
|
||||
>
|
||||
{sigunguOptions.map((o) => (
|
||||
<option key={o.value} value={o.value}>
|
||||
{o.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="회사명" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={company}
|
||||
onChange={setCompany}
|
||||
placeholder="회사명을 입력해주세요."
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="사업자등록번호" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={formatBusinessNumber(bizNo)}
|
||||
onChange={(v) => setBizNo(v.replace(/\D/g, "").slice(0, 10))}
|
||||
placeholder="사업자 등록번호를 입력해주세요."
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="종사자등록번호">
|
||||
<div className="flex flex-wrap items-center gap-4">
|
||||
<div className="w-[420px] max-w-full">
|
||||
<TextInput
|
||||
value={workerNo}
|
||||
onChange={(v) => setWorkerNo(v.replace(/\D/g, "").slice(0, 4))}
|
||||
placeholder="종사자등록번호를 입력해주세요."
|
||||
disabled={noWorkerNo}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="flex items-center gap-2 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={noWorkerNo}
|
||||
onChange={(e) => {
|
||||
setNoWorkerNo(e.target.checked);
|
||||
if (e.target.checked) setWorkerNo("");
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
해당없음
|
||||
</label>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="사업자주소" required>
|
||||
<div className="max-w-[420px] space-y-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="w-[180px]">
|
||||
<TextInput value={zip} onChange={setZip} placeholder="우편번호" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsPostcodeModalOpen(true)}
|
||||
className="h-11 rounded bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
>
|
||||
우편번호 검색
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<TextInput value={addr1} onChange={setAddr1} placeholder="주소" />
|
||||
<TextInput
|
||||
value={addr2}
|
||||
onChange={setAddr2}
|
||||
placeholder="상세 주소를 입력하세요."
|
||||
inputRef={detailAddrRef}
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="전화번호" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={tel}
|
||||
onChange={(v) => setTel(formatKoreanPhoneNumber(v))}
|
||||
placeholder="전화번호를 입력하세요."
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
{editMode ? (
|
||||
<>
|
||||
<FormRow label="기존 비밀번호 확인" required>
|
||||
<div className="max-w-[520px]">
|
||||
<div className="flex gap-2">
|
||||
<div className="flex-1">
|
||||
<TextInput
|
||||
value={currentPw}
|
||||
onChange={(value) => {
|
||||
setCurrentPw(value);
|
||||
setIsCurrentPwVerified(false);
|
||||
}}
|
||||
placeholder="기존 비밀번호를 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={checkCurrentPassword}
|
||||
disabled={isCheckingCurrentPw}
|
||||
className="h-11 shrink-0 rounded bg-[#0B4EA2] px-4 text-base text-white hover:opacity-90 disabled:bg-neutral-400"
|
||||
>
|
||||
{isCheckingCurrentPw ? "확인 중..." : "확인"}
|
||||
</button>
|
||||
</div>
|
||||
<p className={`mt-2 text-sm ${isCurrentPwVerified ? "text-emerald-600" : "text-neutral-500"}`}>
|
||||
{isCurrentPwVerified ? "기존 비밀번호 확인이 완료되었습니다." : "수정 전 기존 비밀번호 확인이 필요합니다."}
|
||||
</p>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<FormRow label="비밀번호" required={!editMode}>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={pw}
|
||||
onChange={setPw}
|
||||
placeholder="비밀번호를 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="비밀번호 확인" required={!editMode}>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={pw2}
|
||||
onChange={setPw2}
|
||||
placeholder="비밀번호를 한번 더 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center py-12">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSubmit}
|
||||
className="h-11 rounded-md bg-[#0B4EA2] px-10 text-lg text-white hover:opacity-90"
|
||||
>
|
||||
{editMode ? "수요처 수정" : "수요처 등록"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<PostcodeModal
|
||||
open={isPostcodeModalOpen}
|
||||
onClose={() => setIsPostcodeModalOpen(false)}
|
||||
onSelect={handleSelectAddress}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<label className="flex items-center gap-2 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={noWorkerNo}
|
||||
onChange={(e) => {
|
||||
setNoWorkerNo(e.target.checked);
|
||||
if (e.target.checked) setWorkerNo("");
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
해당없음
|
||||
</label>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="사업자주소" required>
|
||||
<div className="max-w-[420px] space-y-2">
|
||||
<div className="flex gap-2">
|
||||
<div className="w-[180px]">
|
||||
<TextInput value={zip} onChange={setZip} placeholder="우편번호" />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsPostcodeModalOpen(true)}
|
||||
className="h-11 rounded bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
>
|
||||
우편번호 검색
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<TextInput value={addr1} onChange={setAddr1} placeholder="주소" />
|
||||
<TextInput
|
||||
value={addr2}
|
||||
onChange={setAddr2}
|
||||
placeholder="상세 주소를 입력하세요."
|
||||
inputRef={detailAddrRef}
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="전화번호" required>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={tel}
|
||||
onChange={(v) => setTel(formatKoreanPhoneNumber(v))}
|
||||
placeholder="전화번호를 입력하세요."
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="비밀번호" required={!editMode}>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={pw}
|
||||
onChange={setPw}
|
||||
placeholder="비밀번호를 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="비밀번호 확인" required={!editMode}>
|
||||
<div className="max-w-[420px]">
|
||||
<TextInput
|
||||
value={pw2}
|
||||
onChange={setPw2}
|
||||
placeholder="비밀번호를 한번 더 입력하세요."
|
||||
type="password"
|
||||
/>
|
||||
</div>
|
||||
</FormRow>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center py-12">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onSubmit}
|
||||
className="h-11 rounded-md bg-[#0B4EA2] px-10 text-lg text-white hover:opacity-90"
|
||||
>
|
||||
{editMode ? "수요처 수정" : "수요처 등록"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<PostcodeModal
|
||||
open={isPostcodeModalOpen}
|
||||
onClose={() => setIsPostcodeModalOpen(false)}
|
||||
onSelect={handleSelectAddress}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// app/(with-layout)/apply/register/page.tsx
|
||||
/**
|
||||
* 수요정보등록(수정) 화면
|
||||
*
|
||||
@@ -37,9 +36,9 @@ export default function DemandRegisterPage() {
|
||||
|
||||
// 기본정보
|
||||
const [reqDate, setReqDate] = useState<string>(today);
|
||||
const [reqQty, setReqQty] = useState<string>("");
|
||||
const [usePurpose, setUsePurpose] = useState<string>("");
|
||||
const [hopePrice, setHopePrice] = useState<string>("");
|
||||
const [reqQty, setReqQty] = useState<string>(""); // 요구수량(㎥/d)
|
||||
const [usePurpose, setUsePurpose] = useState<string>(""); // 사용용도
|
||||
const [hopePrice, setHopePrice] = useState<string>(""); // 희망단가
|
||||
|
||||
// 요구수질
|
||||
const [coliform, setColiform] = useState<string>(""); // 총대장균군수
|
||||
@@ -63,9 +62,9 @@ export default function DemandRegisterPage() {
|
||||
const [cand3, setCand3] = useState("");
|
||||
//const [candidateList, setCandidateList] = useState<SubsInfo[]>([]);
|
||||
|
||||
const useTypesData = useDmStore((state) => state.Sdm.useTypesData);
|
||||
const getUseTypesData = useDmStore((state) => state.getUseTypesData);
|
||||
const [openSimilar, setOpenSimilar] = useState(false);
|
||||
const useTypesData = useDmStore((state) => state.Sdm.useTypesData); // 사용용도
|
||||
const getUseTypesData = useDmStore((state) => state.getUseTypesData); // 사용용도 함수
|
||||
const [openSimilar, setOpenSimilar] = useState(false); // 유사사례 modal open
|
||||
|
||||
// 후보지 팝업 메시지 핸들러
|
||||
const candsRef = useRef<{ name: string; code: string }[]>([]);
|
||||
@@ -115,15 +114,16 @@ export default function DemandRegisterPage() {
|
||||
const getSimilarCaseList = useDmStore((state) => state.getSimilarCaseList);
|
||||
const [farmText, setFarmText] = useState("이하");
|
||||
|
||||
const insertSdmInfoData = useDmStore(
|
||||
(state) => state.insertSdmInfoData
|
||||
);
|
||||
const insertSdmInfoData = useDmStore( (state) => state.insertSdmInfoData ); // 수요정보등록
|
||||
|
||||
useEffect(() => {
|
||||
getUseTypesData();
|
||||
getUseTypesData(); // 사용용도 조회
|
||||
}, [getUseTypesData]);
|
||||
|
||||
// 후보지 선택 팝업이 window.postMessage로 넘긴 선택 결과를 받아
|
||||
// 현재 화면의 후보지 입력칸(cand1~3)에 반영한다.
|
||||
useEffect(() => {
|
||||
// message 이벤트의 payload를 읽어 후보지 목록 상태를 갱신하는 핸들러
|
||||
const handler = (event: MessageEvent) => {
|
||||
if (!event.data) return;
|
||||
|
||||
@@ -200,25 +200,26 @@ export default function DemandRegisterPage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 유사사례
|
||||
const rows: SimilarCaseRow[] = similarCaseList.map((el, idx) => ({
|
||||
caseId: String(idx),
|
||||
projectName: el.ruseBizNm,
|
||||
period: `${el.bizBgngDate} ~ ${el.bizEndDate}`,
|
||||
projectName: el.ruseBizNm, // 사업명
|
||||
period: `${el.bizBgngDate} ~ ${el.bizEndDate}`, // 사업기간
|
||||
|
||||
supplyFacilityType: el.splyFcltKnd,
|
||||
supplyName: el.splyFcltNm,
|
||||
supplyCapacity: Number(el.splyFcltCpct),
|
||||
supplyFacilityType: el.splyFcltKnd, // 시설종류
|
||||
supplyName: el.splyFcltNm, // 공급처명
|
||||
supplyCapacity: Number(el.splyFcltCpct), // 공급처 시설용량
|
||||
|
||||
demandName: el.dmdFcltNm,
|
||||
demandAddress: el.dmdFcltAddr,
|
||||
demandName: el.dmdFcltNm, // 수요처명
|
||||
demandAddress: el.dmdFcltAddr, // 수요처 주소
|
||||
|
||||
facilityCapacity: Number(el.ruseFcltCpct),
|
||||
pipeKm: Number(el.ruseFcltPplLen),
|
||||
purpose: el.ruseFcltUsg,
|
||||
facilityCapacity: Number(el.ruseFcltCpct), // 재이용시설 시설용량
|
||||
pipeKm: Number(el.ruseFcltPplLen), // 재이용시설 관로길이
|
||||
purpose: el.ruseFcltUsg, // 재이용시설 용도
|
||||
|
||||
supplyAmt: Number(el.ruseFcltSplyCpct),
|
||||
treatment: el.ruseFcltPrcsMsrc,
|
||||
usePurpose: el.ruseFcltUsg,
|
||||
supplyAmt: Number(el.ruseFcltSplyCpct), // 재이용시설 공급(용)량
|
||||
treatment: el.ruseFcltPrcsMsrc, // 재이용시설 처리공법
|
||||
usePurpose: el.ruseFcltUsg, // 재이용시설 용도
|
||||
}));
|
||||
|
||||
const detailsByCaseId: Record<string, SimilarCaseDetail> = {};
|
||||
@@ -312,6 +313,7 @@ export default function DemandRegisterPage() {
|
||||
}
|
||||
}
|
||||
|
||||
// 후보지 조회 팝업 Open
|
||||
const searchCandidate = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -328,28 +330,27 @@ export default function DemandRegisterPage() {
|
||||
window.open(url, "candidatePopup", popupOptions);
|
||||
};
|
||||
|
||||
// 유사기준
|
||||
const getCriterionCode = (criterion: string) => {
|
||||
switch (criterion) {
|
||||
case "supplyAmt":
|
||||
return "S";
|
||||
case "money":
|
||||
return "M";
|
||||
case "usePurpose":
|
||||
return "U";
|
||||
default:
|
||||
return "";
|
||||
case "supplyAmt": return "S"; // 공급량
|
||||
case "money": return "M"; // 단가?
|
||||
case "usePurpose": return "U"; // 이용용도
|
||||
default: return "";
|
||||
}
|
||||
};
|
||||
|
||||
// 유사사례 Modal Open
|
||||
const searchSimilarCase = async (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setOpenSimilar(true);
|
||||
};
|
||||
|
||||
// 수요정보 입력란 초기화
|
||||
const initQualityForm = () => {
|
||||
setColiform("");
|
||||
setNoDetect(false);
|
||||
setNoDetect(false); // 총대장균군수 불검출
|
||||
setTn("");
|
||||
setResidualCl("");
|
||||
setTp("");
|
||||
@@ -364,6 +365,7 @@ export default function DemandRegisterPage() {
|
||||
setFarmText("이하");
|
||||
}
|
||||
|
||||
// 사용용도별 초기화
|
||||
const fncSetOptionValue = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = e.target.value;
|
||||
initQualityForm();
|
||||
@@ -422,261 +424,278 @@ export default function DemandRegisterPage() {
|
||||
setUsePurpose(value);
|
||||
}
|
||||
|
||||
const pageTitle = "수요정보등록";
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<main className="bg-white">
|
||||
<main className="min-w-0 flex-1">
|
||||
<div className="relative">
|
||||
{/* 서브 타이틀 + 브레드크럼 */}
|
||||
<SubPageHeader
|
||||
title={pageTitle}
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "수요신청", href: "/apply/dmdco" },
|
||||
{ label: pageTitle },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 서브 타이틀 + 브레드크럼 */}
|
||||
<SubPageHeader
|
||||
title="수요정보등록"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "수요신청", href: "/reuse/apply" },
|
||||
{ label: "수요정보등록" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 컨텐츠 영역 */}
|
||||
<div>
|
||||
|
||||
{/* 기본정보 테이블 */}
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
<FormRow label="요구시기" required>
|
||||
<DateInput value={reqDate} onChange={setReqDate} />
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="요구수량(m²/d)" required>
|
||||
<TextInput value={reqQty} onChange={setReqQty} placeholder="" />
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="사용용도" required>
|
||||
<select
|
||||
value={usePurpose}
|
||||
onChange={(e) => fncSetOptionValue(e)}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
<option value="">선택하세요</option>
|
||||
{useTypesData.map((item) => (
|
||||
<option key={item.val} value={item.val}>
|
||||
{item.text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</FormRow>
|
||||
|
||||
<Divider />
|
||||
|
||||
<FormRow label="희망단가(원)" required>
|
||||
<TextInput value={hopePrice} onChange={setHopePrice} />
|
||||
</FormRow>
|
||||
</div>
|
||||
|
||||
{/* 요구수질 섹션 타이틀 바 */}
|
||||
<div className="grid grid-cols-12">
|
||||
<div className="col-span-12 lg:col-span-2 inline-flex justify-center items-center gap-3 px-6 py-6 text-center">
|
||||
<span className="text-lg font-semibold text-neutral-800">요구수질</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpenRef(true)}
|
||||
className="rounded border-[2px] border-[#DE3412] px-2 py-3 text-[15px] font-medium text-[#4F4F4F] hover:bg-orange-50"
|
||||
>
|
||||
수질기준 참고
|
||||
</button>
|
||||
{/* 수질기준참고 모달 */}
|
||||
<WaterQualityRefModal
|
||||
open={openRef}
|
||||
onClose={() => setOpenRef(false)}
|
||||
imageSrc="/images/landing/water_quality_ref_1.png"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 요구수질 테이블 */}
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
{/* 1행 */}
|
||||
<QualityRow
|
||||
leftLabel="총대장균군수(개/mg)"
|
||||
|
||||
leftControl={
|
||||
<div className="flex items-center gap-4">
|
||||
<TextInput
|
||||
value={coliform}
|
||||
onChange={setColiform}
|
||||
disabled={noDetect}
|
||||
className={noDetect ? "bg-neutral-100 text-neutral-400" : ""}
|
||||
/>
|
||||
<label className="flex items-center gap-2 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={noDetect}
|
||||
onChange={(e) => {
|
||||
setNoDetect(e.target.checked);
|
||||
if (e.target.checked) setColiform("");
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
불검출
|
||||
</label>
|
||||
{/* 컨텐츠 영역 */}
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
{/* 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{pageTitle}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
}
|
||||
rightLabel="총질소(T-N)(mg/L)"
|
||||
rightControl={
|
||||
<InlineSuffixInput value={tn} onChange={setTn} suffix="이하" />
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 구분선 */}
|
||||
<Hr className="mt-[40px]" />
|
||||
|
||||
{/* 2행 */}
|
||||
<QualityRow
|
||||
leftLabel="결합잔류염소(mg/L)"
|
||||
/* leftRequired */
|
||||
leftControl={<TextInput value={residualCl} onChange={setResidualCl} />}
|
||||
rightLabel="총인(T-P)(mg/L)"
|
||||
rightControl={
|
||||
<InlineSuffixInput value={tp} onChange={setTp} suffix="이하" />
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
{/* 필수값 안내 */}
|
||||
<p className="my-4 text-[16px] text-neutral-600 font-medium">
|
||||
<span className="text-red-500">*</span> 는 필수 값입니다.
|
||||
</p>
|
||||
|
||||
{/* 3행 */}
|
||||
<QualityRow
|
||||
leftLabel="탁도(NTU)"
|
||||
/* leftRequired */
|
||||
leftControl={
|
||||
<InlineSuffixInput value={turbidity} onChange={setTurbidity} suffix="이하" />
|
||||
}
|
||||
rightLabel="수소이온농도(pH)"
|
||||
rightControl={
|
||||
<div className="flex items-center gap-2">
|
||||
<TextInput
|
||||
value={ph.min}
|
||||
onChange={(v) => setPh((p) => ({ ...p, min: v }))}
|
||||
className="w-32"
|
||||
/>
|
||||
<span className="text-neutral-500">~</span>
|
||||
<TextInput
|
||||
value={ph.max}
|
||||
onChange={(v) => setPh((p) => ({ ...p, max: v }))}
|
||||
className="w-32"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
{/* 기본정보 테이블 */}
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
<FormRow label="요구시기" required>
|
||||
<DateInput value={reqDate} onChange={setReqDate} />
|
||||
</FormRow>
|
||||
|
||||
{/* 4행 */}
|
||||
<QualityRow
|
||||
leftLabel={
|
||||
<>
|
||||
생물화학적산소요구량
|
||||
<br />
|
||||
(BOD)(mg/L)
|
||||
</>
|
||||
}
|
||||
/* leftRequired */
|
||||
leftControl={<InlineSuffixInput value={bod} onChange={setBod} suffix="이하" />}
|
||||
rightLabel="염화물(mgCL/L)"
|
||||
rightControl={<InlineSuffixInput value={chloride} onChange={setChloride} suffix="이하" />}
|
||||
/>
|
||||
<Divider />
|
||||
<Divider />
|
||||
|
||||
{/* 5행 */}
|
||||
<QualityRow
|
||||
leftLabel="냄새"
|
||||
/* leftRequired */
|
||||
leftControl={
|
||||
<select
|
||||
value={odor}
|
||||
onChange={(e) => setOdor(e.target.value)}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
<option>불쾌하지 않을 것</option>
|
||||
<option>약간 있음</option>
|
||||
<option>있음</option>
|
||||
</select>
|
||||
}
|
||||
rightLabel="전기전도도(µs/cm)"
|
||||
rightControl={<InlineSuffixInput value={ec} onChange={setEc} suffix={farmText} />}
|
||||
/>
|
||||
<Divider />
|
||||
<FormRow label="요구수량(m²/d)" required>
|
||||
<TextInput value={reqQty} onChange={setReqQty} placeholder="" />
|
||||
</FormRow>
|
||||
|
||||
{/* 6행 */}
|
||||
<QualityRow
|
||||
leftLabel="색도(도)"
|
||||
/* leftRequired */
|
||||
leftControl={<InlineSuffixInput value={color} onChange={setColor} suffix="이하" />}
|
||||
rightLabel="비고"
|
||||
rightControl={
|
||||
<textarea
|
||||
value={remark}
|
||||
onChange={(e) => setRemark(e.target.value)}
|
||||
className="h-11 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm outline-none focus:border-neutral-500"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
<Divider />
|
||||
|
||||
{/* 후보지 선택 */}
|
||||
<div className="grid grid-cols-12">
|
||||
<LabelCell>
|
||||
공급처 후보지 선택
|
||||
<br />
|
||||
(필수아님)
|
||||
</LabelCell>
|
||||
|
||||
<div className="col-span-12 md:col-span-10 p-6">
|
||||
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:gap-4">
|
||||
<TextInput value={cand1} onChange={setCand1} className="max-w-[220px]" />
|
||||
<TextInput value={cand2} onChange={setCand2} className="max-w-[220px]" />
|
||||
<TextInput value={cand3} onChange={setCand3} className="max-w-[220px]" />
|
||||
|
||||
<div className="flex items-center gap-3 xl:ml-2">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#888] px-5 text-base text-white hover:bg-neutral-700"
|
||||
onClick={(e) => {
|
||||
searchCandidate(e);
|
||||
|
||||
//splyList => 해당객체가 대민에서 선택한 후보처 리스트
|
||||
//setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
|
||||
}}>
|
||||
후보지 조회
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#888] px-5 text-base text-white hover:bg-neutral-600"
|
||||
onClick={(e) => {
|
||||
// TODO: 유사사례 연결
|
||||
searchSimilarCase(e);
|
||||
}}
|
||||
<FormRow label="사용용도" required>
|
||||
<select
|
||||
value={usePurpose}
|
||||
onChange={(e) => fncSetOptionValue(e)}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
유사사례
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<option value="">선택하세요</option>
|
||||
{useTypesData.map((item) => (
|
||||
<option key={item.val} value={item.val}>
|
||||
{item.text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</FormRow>
|
||||
|
||||
{/* 하단 버튼 */}
|
||||
<div className="flex justify-center py-12">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#0B4EA2] px-10 text-lg text-white hover:opacity-90"
|
||||
onClick={() => {
|
||||
// TODO: 저장 처리
|
||||
saveInfo();
|
||||
}}
|
||||
>
|
||||
수요정보 등록
|
||||
</button>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
<FormRow label="희망단가(원)" required>
|
||||
<TextInput value={hopePrice} onChange={setHopePrice} />
|
||||
</FormRow>
|
||||
</div>
|
||||
|
||||
{/* 요구수질 섹션 타이틀 바 */}
|
||||
<div className="grid grid-cols-12">
|
||||
<div className="col-span-12 lg:col-span-2 inline-flex justify-center items-center gap-3 px-2 py-6 text-center">
|
||||
<span className="text-lg font-semibold text-neutral-800">요구수질</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpenRef(true)}
|
||||
className="rounded border-[2px] border-[#DE3412] px-2 py-3 text-[15px] font-medium text-[#4F4F4F] hover:bg-orange-50"
|
||||
>
|
||||
수질기준 참고
|
||||
</button>
|
||||
{/* 수질기준참고 모달 */}
|
||||
<WaterQualityRefModal
|
||||
open={openRef}
|
||||
onClose={() => setOpenRef(false)}
|
||||
imageSrc="/images/landing/water_quality_ref_1.png"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 요구수질 테이블 */}
|
||||
<div className="border border-neutral-200 bg-white">
|
||||
{/* 1행 */}
|
||||
<QualityRow
|
||||
leftLabel="총대장균군수(개/mg)"
|
||||
|
||||
leftControl={
|
||||
<div className="flex items-center gap-4">
|
||||
<TextInput
|
||||
value={coliform}
|
||||
onChange={setColiform}
|
||||
disabled={noDetect}
|
||||
className={noDetect ? "bg-neutral-100 text-neutral-400" : ""}
|
||||
/>
|
||||
<label className="flex items-center gap-2 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={noDetect}
|
||||
onChange={(e) => {
|
||||
setNoDetect(e.target.checked);
|
||||
if (e.target.checked) setColiform("");
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
불검출
|
||||
</label>
|
||||
</div>
|
||||
}
|
||||
rightLabel="총질소(T-N)(mg/L)"
|
||||
rightControl={
|
||||
<InlineSuffixInput value={tn} onChange={setTn} suffix="이하" />
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 2행 */}
|
||||
<QualityRow
|
||||
leftLabel="결합잔류염소(mg/L)"
|
||||
/* leftRequired */
|
||||
leftControl={<TextInput value={residualCl} onChange={setResidualCl} />}
|
||||
rightLabel="총인(T-P)(mg/L)"
|
||||
rightControl={
|
||||
<InlineSuffixInput value={tp} onChange={setTp} suffix="이하" />
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 3행 */}
|
||||
<QualityRow
|
||||
leftLabel="탁도(NTU)"
|
||||
/* leftRequired */
|
||||
leftControl={
|
||||
<InlineSuffixInput value={turbidity} onChange={setTurbidity} suffix="이하" />
|
||||
}
|
||||
rightLabel="수소이온농도(pH)"
|
||||
rightControl={
|
||||
<div className="flex items-center gap-2">
|
||||
<TextInput
|
||||
value={ph.min}
|
||||
onChange={(v) => setPh((p) => ({ ...p, min: v }))}
|
||||
className="w-32"
|
||||
/>
|
||||
<span className="text-neutral-500">~</span>
|
||||
<TextInput
|
||||
value={ph.max}
|
||||
onChange={(v) => setPh((p) => ({ ...p, max: v }))}
|
||||
className="w-32"
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 4행 */}
|
||||
<QualityRow
|
||||
leftLabel={
|
||||
<>
|
||||
생물화학적산소요구량
|
||||
<br />
|
||||
(BOD)(mg/L)
|
||||
</>
|
||||
}
|
||||
/* leftRequired */
|
||||
leftControl={<InlineSuffixInput value={bod} onChange={setBod} suffix="이하" />}
|
||||
rightLabel="염화물(mgCL/L)"
|
||||
rightControl={<InlineSuffixInput value={chloride} onChange={setChloride} suffix="이하" />}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 5행 */}
|
||||
<QualityRow
|
||||
leftLabel="냄새"
|
||||
/* leftRequired */
|
||||
leftControl={
|
||||
<select
|
||||
value={odor}
|
||||
onChange={(e) => setOdor(e.target.value)}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
<option>불쾌하지 않을 것</option>
|
||||
<option>약간 있음</option>
|
||||
<option>있음</option>
|
||||
</select>
|
||||
}
|
||||
rightLabel="전기전도도(µs/cm)"
|
||||
rightControl={<InlineSuffixInput value={ec} onChange={setEc} suffix={farmText} />}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 6행 */}
|
||||
<QualityRow
|
||||
leftLabel="색도(도)"
|
||||
/* leftRequired */
|
||||
leftControl={<InlineSuffixInput value={color} onChange={setColor} suffix="이하" />}
|
||||
rightLabel="비고"
|
||||
rightControl={
|
||||
<textarea
|
||||
value={remark}
|
||||
onChange={(e) => setRemark(e.target.value)}
|
||||
className="h-11 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm outline-none focus:border-neutral-500"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
|
||||
{/* 후보지 선택 */}
|
||||
<div className="grid grid-cols-12">
|
||||
<LabelCell>
|
||||
<span className="flex flex-col items-center leading-6">
|
||||
<span>공급처 후보지 선택</span>
|
||||
<span className="text-sm">(필수아님)</span>
|
||||
</span>
|
||||
</LabelCell>
|
||||
|
||||
<div className="col-span-12 md:col-span-10 p-6">
|
||||
<div className="flex flex-col gap-3 xl:flex-row xl:items-center xl:gap-4">
|
||||
<TextInput value={cand1} onChange={setCand1} className="max-w-[220px]" />
|
||||
<TextInput value={cand2} onChange={setCand2} className="max-w-[220px]" />
|
||||
<TextInput value={cand3} onChange={setCand3} className="max-w-[220px]" />
|
||||
|
||||
<div className="flex items-center gap-3 xl:ml-2">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#888] px-5 text-base text-white hover:bg-neutral-700"
|
||||
onClick={(e) => {
|
||||
searchCandidate(e);
|
||||
|
||||
//splyList => 해당객체가 대민에서 선택한 후보처 리스트
|
||||
//setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
|
||||
}}
|
||||
>
|
||||
후보지 조회
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#888] px-5 text-base text-white hover:bg-neutral-600"
|
||||
onClick={(e) => searchSimilarCase(e)}
|
||||
>
|
||||
유사사례
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 하단 버튼 */}
|
||||
<div className="flex justify-center py-12">
|
||||
<button
|
||||
type="button"
|
||||
className="h-11 rounded-md bg-[#0B4EA2] px-10 text-lg text-white hover:opacity-90"
|
||||
onClick={() => {
|
||||
// TODO: 저장 처리
|
||||
saveInfo();
|
||||
}}
|
||||
>
|
||||
수요정보 등록
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -686,7 +705,7 @@ export default function DemandRegisterPage() {
|
||||
rows={rows} // 실제 데이터
|
||||
detailsByCaseId={detailsByCaseId}
|
||||
onSearch={async (criterion) => {
|
||||
const code = getCriterionCode(criterion);
|
||||
const code = getCriterionCode(criterion); // 'S' 공급량, 'U' 사용용도
|
||||
if (!code) return;
|
||||
// 값은 상황에 맞게 (예시: usePurpose)
|
||||
console.log(code+' req: '+reqQty+' purpose :'+usePurpose);
|
||||
@@ -836,6 +855,10 @@ function InlineSuffixInput({
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function DateInput({
|
||||
value,
|
||||
onChange,
|
||||
@@ -3,9 +3,14 @@ import Lnb from "@/components/Lnb";
|
||||
|
||||
export default function ApplyLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex gap-0">
|
||||
<div className="relative">
|
||||
{/* 우측 메뉴 */}
|
||||
<Lnb />
|
||||
<main className="flex-1 px-[100px] pt-[80px]">{children}</main>
|
||||
|
||||
{/* 본문 */}
|
||||
<div className="flex max-w-[1400px] gap-[56px] py-[50px]">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,20 @@
|
||||
/**
|
||||
* 수요신청현황
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useDmStore } from "@/app/store";
|
||||
import type { DemandCompBizType } from "@/app/store/sdmMng";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import UiPageNations from "@common/business_components/ui/table/uiPageNations";
|
||||
import { useEffect, useMemo, useRef, useState } from "react";
|
||||
|
||||
type TopField = { label: string; key: string, value: string; required?: boolean };
|
||||
type PairField = { label: string; key: string, value: string };
|
||||
type SessionUser = { dmdInfoId?: string };
|
||||
|
||||
const PAGE_SIZE = 10;
|
||||
|
||||
const formatDate = (value: string | Date | "" | undefined) => {
|
||||
if (!value) return "-";
|
||||
const date = new Date(value);
|
||||
@@ -123,7 +129,7 @@ function DateInput({
|
||||
}
|
||||
|
||||
export default function DemandStatusPage() {
|
||||
const { getSdmInfoStatusList, updateSdmInfoData, Sdm, Common } = useDmStore((state) => state);
|
||||
const { getSdmInfoStatusList, updateSdmInfoData, Sdm, Common, initializeCommonData } = useDmStore((state) => state);
|
||||
const { successLogin, sdmInfoStatusList } = Sdm;
|
||||
const { selectSidoData, selectSigunData } = Common;
|
||||
|
||||
@@ -132,6 +138,7 @@ export default function DemandStatusPage() {
|
||||
const [isLoginStorageInfo, setIsLoginStorageInfo] = useState(false);
|
||||
const [selectedDmdInfoSn, setSelectedDmdInfoSn] = useState<number | "" | null>(null);
|
||||
const [transformedSdmInfoStatusList, setTransformedSdmInfoStatusList] = useState<DemandCompBizType[]>([]);
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
// 수정 유무
|
||||
const [ isEditMode, setIsEditMode ] = useState(false);
|
||||
@@ -151,8 +158,9 @@ export default function DemandStatusPage() {
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
getUseTypesData();
|
||||
}, [getUseTypesData]);
|
||||
getUseTypesData(); // 용도코드 조회
|
||||
initializeCommonData(); // 시도, 시군구 조회
|
||||
}, [getUseTypesData, initializeCommonData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!successLogin && !isLoginStorageInfo) return;
|
||||
@@ -209,6 +217,18 @@ export default function DemandStatusPage() {
|
||||
);
|
||||
}, [transformedSdmInfoStatusList, selectedDmdInfoSn]);
|
||||
|
||||
const pagedSdmInfoStatusList = useMemo(() => {
|
||||
const startIndex = (currentPage - 1) * PAGE_SIZE;
|
||||
return transformedSdmInfoStatusList.slice(startIndex, startIndex + PAGE_SIZE);
|
||||
}, [currentPage, transformedSdmInfoStatusList]);
|
||||
|
||||
useEffect(() => {
|
||||
const totalPages = Math.max(1, Math.ceil(transformedSdmInfoStatusList.length / PAGE_SIZE));
|
||||
if (currentPage > totalPages) {
|
||||
setCurrentPage(totalPages);
|
||||
}
|
||||
}, [currentPage, transformedSdmInfoStatusList.length]);
|
||||
|
||||
const topFields: TopField[] = useMemo(() => {
|
||||
if (!selectedSdmInfoStatus) return [];
|
||||
|
||||
@@ -261,7 +281,15 @@ export default function DemandStatusPage() {
|
||||
}
|
||||
setSelectedDmdInfoSn(dmdInfoSn);
|
||||
requestAnimationFrame(() => {
|
||||
detailRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
if (!detailRef.current) return;
|
||||
const headerOffset = 120;
|
||||
const nextTop =
|
||||
detailRef.current.getBoundingClientRect().top + window.scrollY - headerOffset;
|
||||
|
||||
window.scrollTo({
|
||||
top: nextTop,
|
||||
behavior: "smooth",
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -326,402 +354,440 @@ export default function DemandStatusPage() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const pageTitle = "수요신청현황";
|
||||
|
||||
return (
|
||||
|
||||
<main className="bg-white">
|
||||
<SubPageHeader
|
||||
title="수요신청현황"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "수요신청", href: "/apply/status" },
|
||||
{ label: "수요신청현황" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
/>
|
||||
<main className="min-w-0 flex-1">
|
||||
<div className="relative">
|
||||
{/* 서브 타이틀 + 브레드크럼 */}
|
||||
<SubPageHeader
|
||||
title={pageTitle}
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "수요신청", href: "/apply/dmdco" },
|
||||
{ label: pageTitle },
|
||||
]}
|
||||
/>
|
||||
|
||||
<div>
|
||||
<section className="mt-6">
|
||||
<div className="text-2xl font-bold text-slate-700">수요정보 신청현황</div>
|
||||
{/* 컨텐츠 영역 */}
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
{/* 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{pageTitle}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="text-2xl font-bold text-slate-700 text-center w-full">수요정보 신청현황</div>
|
||||
|
||||
<div className="mt-3 border border-neutral-200">
|
||||
<table className="w-full table-fixed">
|
||||
<thead className="bg-[#E7F4E2]">
|
||||
<tr className="text-sm text-slate-700">
|
||||
<th className="w-[60px] px-3 py-3 text-center">NO.</th>
|
||||
<th className="w-[120px] px-3 py-3 text-center">시도</th>
|
||||
<th className="w-[120px] px-3 py-3 text-center">시군구</th>
|
||||
<th className="w-[180px] px-3 py-3 text-center">회사명</th>
|
||||
<th className="px-3 py-3 text-center">공장주소</th>
|
||||
<th className="w-[140px] px-3 py-3 text-center">전화번호</th>
|
||||
<th className="w-[120px] px-3 py-3 text-center">요구시기</th>
|
||||
<th className="w-[120px] px-3 py-3 text-center">등록일자</th>
|
||||
<th className="w-[120px] px-3 py-3 text-center">수정일자</th>
|
||||
<th className="w-[110px] px-3 py-3 text-center">진행상태</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<div className="mt-3 border border-neutral-200">
|
||||
<table className="w-full table-fixed">
|
||||
<thead className="bg-[#E7F4E2]">
|
||||
<tr className="text-sm text-slate-700">
|
||||
<th className="w-[4%] px-3 py-3 text-center">NO.</th>
|
||||
<th className="w-[9%] px-3 py-3 text-center">시도</th>
|
||||
<th className="w-[9%] px-3 py-3 text-center">시군구</th>
|
||||
<th className="w-[13%] px-3 py-3 text-center">회사명</th>
|
||||
<th className="px-3 py-3 text-center">주소</th>
|
||||
<th className="w-[11%] px-3 py-3 text-center">전화번호</th>
|
||||
<th className="w-[9%] px-3 py-3 text-center">요구시기</th>
|
||||
<th className="w-[9%] px-3 py-3 text-center">등록일자</th>
|
||||
<th className="w-[9%] px-3 py-3 text-center">수정일자</th>
|
||||
<th className="w-[6%] px-3 py-3 text-center">진행상태</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="bg-white text-base text-slate-700">
|
||||
{transformedSdmInfoStatusList.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={8} className="px-3 py-10 text-center text-slate-400">
|
||||
조회된 신청내역이 없습니다.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
transformedSdmInfoStatusList.map((sdmInfoStatus, index) => {
|
||||
const active = sdmInfoStatus.dmdInfoSn === selectedSdmInfoStatus?.dmdInfoSn;
|
||||
return (
|
||||
<tr
|
||||
key={`${sdmInfoStatus.dmdInfoSn}-${index}`}
|
||||
role="row"
|
||||
tabIndex={0}
|
||||
onClick={() => onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn);
|
||||
}
|
||||
}}
|
||||
<tbody className="bg-white text-base text-slate-700">
|
||||
{transformedSdmInfoStatusList.length === 0 ? (
|
||||
<tr>
|
||||
<td colSpan={10} className="px-3 py-10 text-center text-slate-400">
|
||||
조회된 신청내역이 없습니다.
|
||||
</td>
|
||||
</tr>
|
||||
) : (
|
||||
pagedSdmInfoStatusList.map((sdmInfoStatus, index) => {
|
||||
const active = sdmInfoStatus.dmdInfoSn === selectedSdmInfoStatus?.dmdInfoSn;
|
||||
return (
|
||||
<tr
|
||||
key={`${sdmInfoStatus.dmdInfoSn}-${index}`}
|
||||
role="row"
|
||||
tabIndex={0}
|
||||
onClick={() => onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
onSelectSdmInfoStatus(sdmInfoStatus.dmdInfoSn);
|
||||
}
|
||||
}}
|
||||
className={[
|
||||
"border-t border-neutral-200 outline-none",
|
||||
"cursor-pointer",
|
||||
active ? "bg-slate-50" : "hover:bg-slate-50",
|
||||
"focus-visible:ring-2 focus-visible:ring-slate-300",
|
||||
].join(" ")}
|
||||
aria-selected={active}
|
||||
>
|
||||
<td className="px-3 py-3 text-center">
|
||||
{sdmInfoStatus.index || (currentPage - 1) * PAGE_SIZE + index + 1}
|
||||
</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.ctpvCd)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.sggCd)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.dmdCoNm)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.daddr)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.telno)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.dmndTmng)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.regDt)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.mdfcnDt)}</td>
|
||||
<td className="px-3 py-3 text-center">
|
||||
<StatusBadge v={formatValue(sdmInfoStatus.status)} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{transformedSdmInfoStatusList.length > 0 ? (
|
||||
<div
|
||||
className={[
|
||||
"border-t border-neutral-200 outline-none",
|
||||
"cursor-pointer",
|
||||
active ? "bg-slate-50" : "hover:bg-slate-50",
|
||||
"focus-visible:ring-2 focus-visible:ring-slate-300",
|
||||
"mt-2 flex justify-center",
|
||||
"[&_nav]:mt-0 [&_nav]:text-sm",
|
||||
"[&_ul]:gap-0.5",
|
||||
"[&_a]:h-8 [&_a]:min-w-8 [&_a]:px-2 [&_a]:py-1 [&_a]:text-sm",
|
||||
"[&_svg]:h-4 [&_svg]:w-4",
|
||||
"[&_span]:text-sm",
|
||||
].join(" ")}
|
||||
aria-selected={active}
|
||||
>
|
||||
<td className="px-3 py-3 text-center">{sdmInfoStatus.index || index + 1}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.ctpvCd)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.sggCd)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.dmdCoNm)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.daddr)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatValue(sdmInfoStatus.telno)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.dmndTmng)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.regDt)}</td>
|
||||
<td className="px-3 py-3 text-center">{formatDate(sdmInfoStatus.mdfcnDt)}</td>
|
||||
<td className="px-3 py-3 text-center">
|
||||
<StatusBadge v={formatValue(sdmInfoStatus.status)} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="mt-10 pb-16" ref={detailRef}>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-2xl font-bold text-slate-700">수요신청 상세현황</div>
|
||||
{
|
||||
!isEditMode ? (
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
setOriginData(selectedSdmInfoStatus);
|
||||
setEditData({...selectedSdmInfoStatus});
|
||||
setIsEditMode(true)}
|
||||
}
|
||||
>
|
||||
수정
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
saveInfo();
|
||||
}}
|
||||
>
|
||||
저장
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
setEditData(originData);
|
||||
setIsEditMode(false)
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
{!selectedSdmInfoStatus ? (
|
||||
<div className="mt-3 border border-neutral-200 px-6 py-10 text-center text-sm text-slate-400">
|
||||
상세 정보를 표시할 신청건이 없습니다.
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-3 border border-neutral-200">
|
||||
<div className="bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
요구수량
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-12 text-sm">
|
||||
{topFields.map((field) => (
|
||||
<div
|
||||
key={field.label}
|
||||
className="col-span-12 md:col-span-6 grid grid-cols-12 border-b border-neutral-200"
|
||||
>
|
||||
<div className="col-span-4 bg-sky-50 px-4 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
{field.label}
|
||||
{field.required ? <span className="ml-1 text-red-500">*</span> : null}
|
||||
</div>
|
||||
{
|
||||
!isEditMode ?
|
||||
<div className="col-span-8 px-4 py-4 flex items-center justify-center text-center">
|
||||
{field.key==='useUsgCd'? useTypesData.filter(item=>item.val===field.value)[0]?.text: field.value}
|
||||
</div>
|
||||
: <div className="col-span-8 px-4 py-4 flex items-center justify-center text-center">
|
||||
{field.key ==='dmndTmng'?
|
||||
<DateInput value={editData?.[field.key] ?? ""} onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
} />
|
||||
: field.key ==='useUsgCd'?
|
||||
<select
|
||||
value={editData?.[field.key] ?? ""}
|
||||
onChange={(e) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: e.target.value,
|
||||
}))
|
||||
}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
<option value="">선택하세요</option>
|
||||
{useTypesData.map((item) => (
|
||||
<option key={item.val} value={item.val}>
|
||||
{item.text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
: <TextInput value={editData?.[field.key] ?? ""} onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
}></TextInput>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
요구수질
|
||||
</div>
|
||||
|
||||
{waterFields.map((field) => {
|
||||
const { a, b } = editData?.ph ? getRange(editData.ph) : { a: "", b: "" };
|
||||
return (
|
||||
<div
|
||||
key={field.label}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
{field.label}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{
|
||||
!isEditMode ? field.value
|
||||
:
|
||||
field.key === "grssClfg" ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<TextInput
|
||||
value={
|
||||
editData?.grssClfg === -1
|
||||
? ""
|
||||
: editData?.grssClfg ?? ""
|
||||
}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: v,
|
||||
}))
|
||||
}
|
||||
disabled={editData?.grssClfg === -1}
|
||||
className={
|
||||
editData?.grssClfg === -1
|
||||
? "bg-neutral-100 text-neutral-400"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
<label className="flex items-center gap-2 w-3/4 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={editData?.grssClfg === -1}
|
||||
onChange={(e) => {
|
||||
const checked = e.target.checked;
|
||||
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: checked ? -1 : "",
|
||||
}));
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
불검출
|
||||
</label>
|
||||
</div>
|
||||
) : field.key === 'smll' ? (
|
||||
<select
|
||||
value={
|
||||
editData?.smll === -1
|
||||
? ""
|
||||
: editData?.smll ?? ""
|
||||
}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: v,
|
||||
}))
|
||||
}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500">
|
||||
<option>불쾌하지 않을 것</option>
|
||||
<option>약간 있음</option>
|
||||
<option>있음</option>
|
||||
</select>
|
||||
) : field.key === 'ph' ? (
|
||||
<>
|
||||
<TextInput
|
||||
value={a}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: `${v}~${b}`,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
~
|
||||
<TextInput
|
||||
value={b}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: `${a}~${v}`,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TextInput
|
||||
value={editData?.[field.key] ?? ""}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
{
|
||||
field.key==='tn'||field.key === 'tph'||field.key === 'trbt'||field.key==='blgcOxygDmndQnt'||field.key==='clIonQnt'||field.key==='cdtnv'|| field.key=='crmty'
|
||||
?<>이하</>:<></>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)})}
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
공급처 후보지
|
||||
{
|
||||
isEditMode &&
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 w-25 ml-12 rounded-md bg-[#888] px-2 text-base text-white hover:bg-neutral-700"
|
||||
onClick={(e) => {
|
||||
searchCandidate(e);
|
||||
|
||||
//splyList => 해당객체가 대민에서 선택한 후보처 리스트
|
||||
//setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
|
||||
}}>
|
||||
후보지 조회
|
||||
</button>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
{candidateNames.map((candidateName, index) => (
|
||||
isEditMode ? (
|
||||
<div
|
||||
key={`candidate-${index}`}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
후보지{index + 1}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
<TextInput
|
||||
value={candidateName}
|
||||
disabled={true}
|
||||
onChange={(v) => {
|
||||
setEditData((prev) => {
|
||||
const newList = [...(prev.splyCndtList || [])];
|
||||
newList[index] = {
|
||||
...newList[index],
|
||||
ruseUtztnFcltNm: v,
|
||||
};
|
||||
return {
|
||||
...prev,
|
||||
splyCndtList: newList,
|
||||
};
|
||||
});
|
||||
}}
|
||||
<UiPageNations
|
||||
total={transformedSdmInfoStatusList.length}
|
||||
cur={currentPage}
|
||||
pageSize={PAGE_SIZE}
|
||||
onPageChange={setCurrentPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) :
|
||||
<div
|
||||
key={`candidate-${index}`}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
후보지{index + 1}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{candidateName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-t border-b border-neutral-200">
|
||||
수요신청 진행상태
|
||||
<section className="mt-10 pb-16" ref={detailRef}>
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="text-2xl font-bold text-slate-700">수요신청 상세현황</div>
|
||||
{
|
||||
!isEditMode ? (
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
if (!selectedSdmInfoStatus) return;
|
||||
if (selectedSdmInfoStatus.status !== "신청") {
|
||||
alert("진행상태 '신청'인 경우에만 수정이 가능합니다.");
|
||||
return;
|
||||
}
|
||||
setOriginData(selectedSdmInfoStatus);
|
||||
setEditData({...selectedSdmInfoStatus});
|
||||
setIsEditMode(true)}
|
||||
}
|
||||
>
|
||||
수정
|
||||
</button>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
saveInfo();
|
||||
}}
|
||||
>
|
||||
저장
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 rounded-md bg-[#888] px-4 text-base text-white hover:bg-neutral-700"
|
||||
onClick={() => {
|
||||
setEditData(originData);
|
||||
setIsEditMode(false)
|
||||
}}
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200">
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
진행상태
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{formatValue(selectedSdmInfoStatus.status)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200">
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
공급처매칭결과
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{formatValue(selectedSdmInfoStatus.ruseUtztnFcltNm)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!selectedSdmInfoStatus ? (
|
||||
<div className="mt-3 border border-neutral-200 px-6 py-10 text-center text-sm text-slate-400">
|
||||
상세 정보를 표시할 신청건이 없습니다.
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-3 border border-neutral-200">
|
||||
<div className="bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
요구수량
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-12 text-sm">
|
||||
{topFields.map((field) => (
|
||||
<div
|
||||
key={field.label}
|
||||
className="col-span-12 md:col-span-6 grid grid-cols-12 border-b border-neutral-200"
|
||||
>
|
||||
<div className="col-span-4 bg-sky-50 px-4 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
{field.label}
|
||||
{field.required ? <span className="ml-1 text-red-500">*</span> : null}
|
||||
</div>
|
||||
{
|
||||
!isEditMode ?
|
||||
<div className="col-span-8 px-4 py-4 flex items-center justify-center text-center">
|
||||
{field.key==='useUsgCd'? useTypesData.filter(item=>item.val===field.value)[0]?.text: field.value}
|
||||
</div>
|
||||
: <div className="col-span-8 px-4 py-4 flex items-center justify-center text-center">
|
||||
{field.key ==='dmndTmng'?
|
||||
<DateInput value={editData?.[field.key] ?? ""} onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
} />
|
||||
: field.key ==='useUsgCd'?
|
||||
<select
|
||||
value={editData?.[field.key] ?? ""}
|
||||
onChange={(e) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: e.target.value,
|
||||
}))
|
||||
}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500"
|
||||
>
|
||||
<option value="">선택하세요</option>
|
||||
{useTypesData.map((item) => (
|
||||
<option key={item.val} value={item.val}>
|
||||
{item.text}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
: <TextInput value={editData?.[field.key] ?? ""} onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
}></TextInput>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
요구수질
|
||||
</div>
|
||||
|
||||
{waterFields.map((field) => {
|
||||
const { a, b } = editData?.ph ? getRange(editData.ph) : { a: "", b: "" };
|
||||
return (
|
||||
<div
|
||||
key={field.label}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
{field.label}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{
|
||||
!isEditMode ? field.value
|
||||
:
|
||||
field.key === "grssClfg" ? (
|
||||
<div className="flex items-center gap-4">
|
||||
<TextInput
|
||||
value={
|
||||
editData?.grssClfg === -1
|
||||
? ""
|
||||
: editData?.grssClfg ?? ""
|
||||
}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: v,
|
||||
}))
|
||||
}
|
||||
disabled={editData?.grssClfg === -1}
|
||||
className={
|
||||
editData?.grssClfg === -1
|
||||
? "bg-neutral-100 text-neutral-400"
|
||||
: ""
|
||||
}
|
||||
/>
|
||||
<label className="flex items-center gap-2 w-3/4 text-sm text-neutral-700">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={editData?.grssClfg === -1}
|
||||
onChange={(e) => {
|
||||
const checked = e.target.checked;
|
||||
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: checked ? -1 : "",
|
||||
}));
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-300"
|
||||
/>
|
||||
불검출
|
||||
</label>
|
||||
</div>
|
||||
) : field.key === 'smll' ? (
|
||||
<select
|
||||
value={
|
||||
editData?.smll === -1
|
||||
? ""
|
||||
: editData?.smll ?? ""
|
||||
}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
grssClfg: v,
|
||||
}))
|
||||
}
|
||||
className="h-11 w-full max-w-[200px] rounded-md border border-neutral-300 bg-white px-3 text-sm outline-none focus:border-neutral-500">
|
||||
<option>불쾌하지 않을 것</option>
|
||||
<option>약간 있음</option>
|
||||
<option>있음</option>
|
||||
</select>
|
||||
) : field.key === 'ph' ? (
|
||||
<>
|
||||
<TextInput
|
||||
value={a}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: `${v}~${b}`,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
~
|
||||
<TextInput
|
||||
value={b}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: `${a}~${v}`,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<TextInput
|
||||
value={editData?.[field.key] ?? ""}
|
||||
onChange={(v) =>
|
||||
setEditData((prev) => ({
|
||||
...prev,
|
||||
[field.key]: v,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
{
|
||||
field.key==='tn'||field.key === 'tph'||field.key === 'trbt'||field.key==='blgcOxygDmndQnt'||field.key==='clIonQnt'||field.key==='cdtnv'|| field.key=='crmty'
|
||||
?<>이하</>:<></>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)})}
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-b border-neutral-200">
|
||||
공급처 후보지
|
||||
{
|
||||
isEditMode &&
|
||||
<button
|
||||
type="button"
|
||||
className="h-8 w-25 ml-12 rounded-md bg-[#888] px-2 text-base text-white hover:bg-neutral-700"
|
||||
onClick={(e) => {
|
||||
searchCandidate(e);
|
||||
|
||||
//splyList => 해당객체가 대민에서 선택한 후보처 리스트
|
||||
//setLastSub => 해당 스테이트가 선택한 마지막 후보처를 넣어야하는 스테이트
|
||||
}}>
|
||||
후보지 조회
|
||||
</button>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
{candidateNames.map((candidateName, index) => (
|
||||
isEditMode ? (
|
||||
<div
|
||||
key={`candidate-${index}`}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
후보지{index + 1}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
<TextInput
|
||||
value={candidateName}
|
||||
disabled={true}
|
||||
onChange={(v) => {
|
||||
setEditData((prev) => {
|
||||
const newList = [...(prev.splyCndtList || [])];
|
||||
newList[index] = {
|
||||
...newList[index],
|
||||
ruseUtztnFcltNm: v,
|
||||
};
|
||||
return {
|
||||
...prev,
|
||||
splyCndtList: newList,
|
||||
};
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) :
|
||||
<div
|
||||
key={`candidate-${index}`}
|
||||
className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200"
|
||||
>
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
후보지{index + 1}
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{candidateName}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<div className="col-span-12 bg-sky-50 px-4 py-3 text-sm font-semibold text-slate-700 border-t border-b border-neutral-200">
|
||||
수요신청 진행상태
|
||||
</div>
|
||||
|
||||
<div className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200">
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
진행상태
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{formatValue(selectedSdmInfoStatus.status)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-span-12 md:col-span-4 grid grid-cols-12 border-b md:border-b-0 border-neutral-200">
|
||||
<div className="col-span-5 bg-sky-50 px-3 py-4 flex items-center justify-center text-center font-semibold text-slate-700">
|
||||
공급처매칭결과
|
||||
</div>
|
||||
<div className="col-span-7 px-3 py-4 flex items-center justify-center text-center">
|
||||
{formatValue(selectedSdmInfoStatus.ruseUtztnFcltNm)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,781 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
type Slide = {
|
||||
src: string;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
type ContentBlock = {
|
||||
title: string;
|
||||
items: TextItem[];
|
||||
};
|
||||
|
||||
type InfoCard = {
|
||||
title: string;
|
||||
desc: string;
|
||||
};
|
||||
|
||||
type TableData = {
|
||||
headers: string[];
|
||||
rows: string[][];
|
||||
};
|
||||
|
||||
type OverviewCompareRow = {
|
||||
country: string;
|
||||
flag?: string;
|
||||
overseas: string;
|
||||
current: string;
|
||||
future: string;
|
||||
};
|
||||
|
||||
type GlobalCase =
|
||||
| {
|
||||
type: "overview";
|
||||
id: string;
|
||||
title: string;
|
||||
}
|
||||
| {
|
||||
type: "default";
|
||||
id: string;
|
||||
title: string;
|
||||
slides: Slide[];
|
||||
blocks?: ContentBlock[];
|
||||
cards?: InfoCard[];
|
||||
table?: TableData;
|
||||
};
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle">
|
||||
<span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" />
|
||||
<span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" />
|
||||
</span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type TextItem =
|
||||
| string
|
||||
| {
|
||||
text: React.ReactNode;
|
||||
depth?: number; // 0: 기본 / 1: 하위 / 2: 더 하위
|
||||
};
|
||||
|
||||
function ContentList({ items }: { items: TextItem[] }) {
|
||||
return (
|
||||
<ul className="mt-4 space-y-2 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
{items.map((item, index) => {
|
||||
const isString = typeof item === "string";
|
||||
const text = isString ? item : item.text;
|
||||
const depth = isString ? 0 : item.depth ?? 0;
|
||||
|
||||
if (depth === 3) {
|
||||
return (
|
||||
<li key={index} className="ml-[56px] list-none text-[#777]">
|
||||
* {text}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
if (depth === 2) {
|
||||
return (
|
||||
<li key={index} className="ml-[52px] list-none text-[#666]">
|
||||
{text}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
if (depth === 1) {
|
||||
return (
|
||||
<li key={index} className="ml-[34px] list-none text-[#777]">
|
||||
- {text}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={index} className="ml-6 list-disc pl-1 font-semibold text-[#555]">
|
||||
{text}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
function ContentBlocks({
|
||||
blocks,
|
||||
cards,
|
||||
table,
|
||||
}: {
|
||||
blocks?: ContentBlock[];
|
||||
cards?: InfoCard[];
|
||||
table?: TableData;
|
||||
}) {
|
||||
if (!blocks?.length) return null;
|
||||
|
||||
return (
|
||||
<div className="mt-10 space-y-9 px-10">
|
||||
{blocks.map((block, index) => (
|
||||
<div key={`${block.title}-${index}`}>
|
||||
<BlockTitle>{block.title}</BlockTitle>
|
||||
|
||||
{!!block.items.length && (
|
||||
<ContentList items={block.items} />
|
||||
)}
|
||||
|
||||
{block.title.includes("재생 수자원 개발법") && (
|
||||
<InfoCards cards={cards} />
|
||||
)}
|
||||
|
||||
{block.title.includes("대만 재이용 시설별 재이용수 공급단지") && (
|
||||
<DataTable table={table} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoCards({ cards }: { cards?: InfoCard[] }) {
|
||||
if (!cards?.length) return null;
|
||||
|
||||
return (
|
||||
<div className="mt-8 grid grid-cols-1 gap-5 md:grid-cols-2 xl:grid-cols-4">
|
||||
{cards.map((card) => (
|
||||
<div
|
||||
key={card.title}
|
||||
className="rounded-[8px] border-2 border-[#01ACE5] bg-[#EAF5F5] p-5"
|
||||
>
|
||||
<strong className="block text-[20px] font-bold text-[#333]">
|
||||
{card.title}
|
||||
</strong>
|
||||
<p className="mt-3 text-[15px] leading-[1.6] text-[#555]">
|
||||
{card.desc}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function DataTable({ table }: { table?: TableData }) {
|
||||
if (!table) return null;
|
||||
|
||||
return (
|
||||
<div className="mt-8 px-[120px]">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
{table.headers.map((header, index) => (
|
||||
<th
|
||||
key={header}
|
||||
className={[
|
||||
"border border-[#D8DDE3] px-4 py-3 text-[18px] font-medium",
|
||||
index === 0
|
||||
? "bg-[#BFC9D2] text-[#222]"
|
||||
: index === 1
|
||||
? "bg-[#14A9D9] text-white"
|
||||
: "bg-[#70BF4B] text-white",
|
||||
].join(" ")}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{table.rows.map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
key={`${rowIndex}-${cellIndex}`}
|
||||
className={[
|
||||
"border border-[#D8DDE3] px-4 py-3 text-[16px] text-[#4F4F4F]",
|
||||
cellIndex === 1
|
||||
? "bg-[#EAF7F8]"
|
||||
: cellIndex === 2
|
||||
? "bg-[#EAF8E6]"
|
||||
: "bg-white",
|
||||
].join(" ")}
|
||||
>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
사례개요 전용 컴포넌트
|
||||
========================= */
|
||||
|
||||
const compareRows: OverviewCompareRow[] = [
|
||||
{
|
||||
country: "한국",
|
||||
flag: "/images/landing/business/globalCase/flag_kr.png",
|
||||
overseas: "12.1%",
|
||||
current: "15.6%",
|
||||
future: "17%",
|
||||
},
|
||||
{
|
||||
country: "대만",
|
||||
flag: "/images/landing/business/globalCase/flag_tw.png",
|
||||
overseas: "2.4%",
|
||||
current: "4.3%",
|
||||
future: "16.5%",
|
||||
},
|
||||
];
|
||||
|
||||
const reuseRateRows = [
|
||||
["이스라엘", "중동&북아프리카", "92.3", "튀르키예", "동유럽&중앙아시아", "26.6"],
|
||||
["쿠웨이트", "중동&북아프리카", "89.3", "독일", "서유럽", "25.0"],
|
||||
["호주", "동아시아&태평양", "49.0", "스페인", "서유럽", "24.4"],
|
||||
["싱가포르", "동아시아&태평양", "44.4", "한국", "동아시아&태평양", "12.1"],
|
||||
["그리스", "서유럽", "39.1", "미국", "북아메리카", "13.1"],
|
||||
["사우디아라비아", "중동&북아프리카", "32.8", "프랑스", "서유럽", "9.5"],
|
||||
["이탈리아", "서유럽", "31.7", "대만", "동아시아&태평양", "2.4"],
|
||||
["룩셈부르크", "서유럽", "29.1", "일본", "동아시아&태평양", "0.8"],
|
||||
["포르투갈", "서유럽", "29.0", "", "", ""],
|
||||
];
|
||||
|
||||
function WaterBullet() {
|
||||
return (
|
||||
<Image
|
||||
src="/images/landing/business/globalCase/icon_bullet_water.png"
|
||||
alt="bullet"
|
||||
className="h-[33px] w-[27px] mt-1"
|
||||
width={27}
|
||||
height={33}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function OverviewNoticeBox() {
|
||||
return (
|
||||
<div className="mx-auto mt-5 max-w-[1000px] rounded-[20px] bg-[#EAF7F8] px-8 py-7">
|
||||
<div className="flex items-start gap-2">
|
||||
<WaterBullet />
|
||||
<div>
|
||||
<strong className="block text-[26px] font-extrabold text-[#007FA4]">
|
||||
우리나라 하수처리수 재이용률은 15% 수준
|
||||
</strong>
|
||||
|
||||
<ul className="mt-5 list-disc space-y-2 pl-5 text-[16px] leading-[1.7] text-[#555]">
|
||||
<li>
|
||||
2024년 하수도 통계 기준 <b>15.6%</b>이며, 제2차 물 재이용 기본 계획을 고려한{" "}
|
||||
<b style={{ color: "#1A1A1A" }}>2030년 목표는 17%</b>
|
||||
</li>
|
||||
<li>
|
||||
대만은 가뭄과 공업용수 증가에 대응하여 <b style={{ color: "#1A1A1A" }}>현재 4.3% 수준이며, 16.5%까지 확대 계획 수립</b>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OverviewCompareTable() {
|
||||
return (
|
||||
<div className="mx-auto mt-5 max-w-[1000px] overflow-x-auto">
|
||||
<table className="w-full min-w-[760px] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-[#BFC9D2] px-4 py-4 text-[18px] font-medium text-[#222] rounded-tl-[10px] rounded-tr-[10px]">
|
||||
국가
|
||||
</th>
|
||||
<th className="bg-[#14A9D9] px-4 py-4 text-[18px] font-medium text-white rounded-tl-[10px] rounded-tr-[10px]">
|
||||
해외정보
|
||||
</th>
|
||||
<th className="bg-[#55BDA5] px-4 py-4 text-[18px] font-medium text-white rounded-tl-[10px] rounded-tr-[10px]">
|
||||
현재
|
||||
</th>
|
||||
<th className="bg-[#6AB84E] px-4 py-4 text-[18px] font-medium text-white rounded-tl-[10px] rounded-tr-[10px]">
|
||||
미래(목표, 예측치)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{compareRows.map((row) => (
|
||||
<tr key={row.country}>
|
||||
<td className="border border-[#AEB9C2] bg-white px-4 py-4 text-[20px] font-bold text-[#333]">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
{row.flag && (
|
||||
<Image
|
||||
key={row.country}
|
||||
src={row.flag}
|
||||
alt={`${row.country} 국기`}
|
||||
className="h-[28px] w-[42px] object-cover"
|
||||
width={42}
|
||||
height={28}
|
||||
/>
|
||||
)}
|
||||
{row.country}
|
||||
</div>
|
||||
</td>
|
||||
<td className="border border-[#AEB9C2] bg-[#EAF7F8] px-4 py-4 text-[28px] font-bold text-[#4F4F4F]">
|
||||
{row.overseas}
|
||||
</td>
|
||||
<td className="border border-[#AEB9C2] bg-white px-4 py-4 text-[28px] font-bold text-[#4F4F4F]">
|
||||
{row.current}
|
||||
</td>
|
||||
<td className="border border-[#AEB9C2] bg-[#EAF8E6] px-4 py-4 text-[28px] font-bold text-[#4F4F4F]">
|
||||
{row.future}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OverviewDescriptionBox() {
|
||||
return (
|
||||
<div className="mx-auto mt-5 max-w-[1000px] rounded-[20px] bg-[#EAF7F8] px-7 py-6">
|
||||
<ul className="list-disc space-y-2 pl-5 text-[16px] leading-[1.7] text-[#555]">
|
||||
<li>
|
||||
전 세계적으로 물 재이용은 호주, 유럽, 아프리카, 미국 등 가뭄이 잦거나 심각한 가뭄이 발생한 국가에서 많이 이루어짐
|
||||
</li>
|
||||
<li>
|
||||
이스라엘, 쿠웨이트 등 중동 및 북아프리카 지역은 가용 수자원이 제한되어 있어 재이용량 비중도 매우 높은 편
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OverviewReuseRateTable() {
|
||||
return (
|
||||
<div className="mx-auto mt-5 max-w-[1000px] overflow-x-auto">
|
||||
<table className="w-full min-w-[960px] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
{["국가명", "지역", "재이용률", "국가명", "지역", "재이용률"].map((header, index) => (
|
||||
<th
|
||||
key={`${header}-${index}`}
|
||||
className={[
|
||||
"px-4 py-3 text-[17px] font-bold rounded-tl-[10px] rounded-tr-[10px]",
|
||||
index % 3 === 0
|
||||
? "bg-[#BFC9D2] text-[#222]"
|
||||
: index % 3 === 1
|
||||
? "bg-[#14A9D9] text-white"
|
||||
: "bg-[#6AB84E] text-white",
|
||||
].join(" ")}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{reuseRateRows.map((row, rowIndex) => (
|
||||
<tr key={rowIndex}>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
key={`${rowIndex}-${cellIndex}`}
|
||||
className={[
|
||||
"border border-[#AEB9C2] px-4 py-3 text-[15px] text-[#4F4F4F]",
|
||||
cellIndex % 3 === 1
|
||||
? "bg-[#EAF7F8]"
|
||||
: cellIndex % 3 === 2
|
||||
? "bg-[#EAF8E6]"
|
||||
: "bg-white font-semibold",
|
||||
].join(" ")}
|
||||
>
|
||||
{cell}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function OverviewSection() {
|
||||
return (
|
||||
<div className="mt-10 px-10">
|
||||
<div>
|
||||
<BlockTitle>주요 국가 물 재이용 계획</BlockTitle>
|
||||
<OverviewNoticeBox />
|
||||
<OverviewCompareTable />
|
||||
</div>
|
||||
|
||||
<div className="mt-8">
|
||||
<BlockTitle>주요 국가의 하수처리수 재이용률 (%)</BlockTitle>
|
||||
<OverviewDescriptionBox />
|
||||
<OverviewReuseRateTable />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/* =========================
|
||||
데이터
|
||||
========================= */
|
||||
|
||||
const globalCases: GlobalCase[] = [
|
||||
{
|
||||
type: "overview",
|
||||
id: "example-global-overview",
|
||||
title: "사례 개요",
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
id: "example-global-taiwan",
|
||||
title: "[대만] 국가 주도의 공업용수 공급",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/globalCase/taiwan_01.png",
|
||||
alt: "대만 국가 주도의 공업용수 공급 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/taiwan_02.png",
|
||||
alt: "대만 국가 주도의 공업용수 공급 2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/taiwan_03.png",
|
||||
alt: "대만 국가 주도의 공업용수 공급 3",
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
title: "재생 수자원 개발법 제정 (2015년제정, 2022개정)",
|
||||
items: [
|
||||
{ text: "목적: 수자원 지속 가능성 향상, 재생 수자원 활용을 통해 물부족 문제 해결" },
|
||||
{ text: "내용: 재생 수자원 정의·분류, 이용 의무, 이용 제한, 수질 기준 등" }
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "대만정부 하수재이용 사업 시행",
|
||||
items: [
|
||||
{ text: "대만은 물 재이용에 적극적이며 국가주도로 하수재이용 사업 시행(안핑)" },
|
||||
{ text: "1,518억의 사업비 중 92% 국비, 지방비 8% 투입", depth: 1 },
|
||||
{ text: "재이용수는 24km 관로로 타이난 TSMC로 공급", depth: 1 },
|
||||
{ text: "안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능" },
|
||||
{ text: "공급단가는 수질과 운영조건에 따라 다르게 책정" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "대만 재이용 시설별 재이용수 공급단지",
|
||||
items: [],
|
||||
},
|
||||
{
|
||||
title: "특이사항",
|
||||
items: [
|
||||
"대만은 강수량이 풍부하지만 지역별 편차로 수자원 확보에 어려움을 겪고 있으며, 특히 남부 지역은 우기와 건기의 강수 차이가 큼",
|
||||
"최근 수년간 심각한 가뭄이 발생하면서 반도체 산업을 국가 핵심 산업으로 지정, 공업용수 수요가 크게 증가",
|
||||
"주요 반도체 공장이 남부에 집중되어 있어 물 부족 문제가 지속적으로 발생, 2021년 가뭄 당시 산업단지 용수 공급에도 차질 발생",
|
||||
"정부는 2023년부터 산업용수 추가 요금을 부과하고, 농업용수 규제를 강화하는 등 용수 관리 정책 시행",
|
||||
"기후변화 대응을 위해 수자원 보호 및 재이용 정책을 지속적으로 확대",
|
||||
"산업용수 다소비 산업과 하수처리수를 재이용수 주요 공급원으로 활용 계획",
|
||||
],
|
||||
},
|
||||
],
|
||||
cards: [
|
||||
{
|
||||
title: "제4조.",
|
||||
desc: "물 소비 계획에 따른 재이용수 이용 관련 내용 (일정 비율 재이용수 사용 규정)",
|
||||
},
|
||||
{
|
||||
title: "제5조.",
|
||||
desc: "재이용수의 운영자, 사용자 관련 내용 (지자체에서 하수 무상공급 제공 가능)",
|
||||
},
|
||||
{
|
||||
title: "제7조.",
|
||||
desc: "재이용수 이용 범위, 이용량 재이용수 수질 기준 내용",
|
||||
},
|
||||
{
|
||||
title: "제8조.",
|
||||
desc: "재이용수 사업 주체, 책임 등에 관한 내용 (광역인 경우, 정부가 지자체 지정)",
|
||||
},
|
||||
],
|
||||
table: {
|
||||
headers: ["사업", "가격(NTD/㎥)", "가격(원/㎥)"],
|
||||
rows: [
|
||||
["Fongshan(가오슝)", "23.39", "1,038원"],
|
||||
["Linhai(가오슝)", "19.28", "858원"],
|
||||
["Anping(타이난)", "38.28", "1,699원"],
|
||||
["Yongkang(타이난)", "23.67", "1,050원"],
|
||||
["Rende(타이난)", "28.26", "1,254원"],
|
||||
["Shuinan(타이중)", "15.00", "668원"],
|
||||
["Quaitou(가오슝)", "35.44", "1,573원"],
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
id: "example-global-singapore",
|
||||
title: "[싱가포르] 수요가 높은 산업의 물 재이용 의무화",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/globalCase/singapore_01.png",
|
||||
alt: "싱가포르 NEWater 사례 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/singapore_02.png",
|
||||
alt: "싱가포르 NEWater 사례 2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/singapore_03.png",
|
||||
alt: "싱가포르 NEWater 사례 3",
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
title: "싱가포르 NEWater 비중 확대",
|
||||
items: [
|
||||
"2003년 공식 NEWater 공장을 가동했으며 신뢰확보를 위해 NEWater Visitor Center를 함께 건설",
|
||||
"NEWater 전체파이프 공급 → 반도체 공장에서 가장 많이 사용",
|
||||
"2060년까지 NEWater 비중 55%까지 높이는 것이 싱가포르의 목표",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "싱가포르 재이용수 의무화 및 지원정책",
|
||||
items: [
|
||||
{ text: "공업용수 수요가 높은 산업에 대해 물 재이용을 의무화" },
|
||||
{ text: "반도체업계 전체 비 재이용수(non-domestic용수) 17% 차지 → 공업용수 수요 원인", depth: 1 },
|
||||
{ text: "2024년 1월 1일부터 연간 최소 60,000㎥를 소비하는 사업자는 재이용수 사용 의무화", depth: 1 },
|
||||
{ text: "반도체 공장의 경우 최소 50% 이상의 재이용수를 요구함(현재는 43% 정도)", depth: 1 },
|
||||
{ text: "재정 지원을 통해 재이용 활성화를 지원" },
|
||||
{ text: "재이용 설비를 기준으로 2025년까지 싱가포르 수자원공사의 2가지 금융상품 제공", depth: 1 },
|
||||
{ text: "1. Water Efficiency Fund", depth: 2 },
|
||||
{ text: "2. Industrial Water Solutions Demonstration Fund", depth: 2 },
|
||||
{ text: "2025년 기준 지원 한도 상향 → 물 재이용 활성 도모", depth: 1 },
|
||||
{ text: "투입비용 50% → 75% 지원 / WSDP: $400만 → $500만", depth: 3 },
|
||||
{ text: "물 효율이 높은 장비를 도입하는 데에도 최대 $300만 지원 가능", depth: 3 },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "특이사항",
|
||||
items: [
|
||||
{ text: "싱가포르는 천연 수자원이 많지 않아, 말레이시아 조호르와 1962년 맺은 협약으로 물을 수입해왔으며, 재이용수를 늘리기 위한 노력을 계속하고 있음" },
|
||||
{ text: "싱가포르는 국토 내에 호수와 하천이 적고 집수공간도 적어 빗물로 확보할 수 있는 수자원량은 전체 물 수요의 20%에 불과함", depth: 1 },
|
||||
|
||||
{ text: "싱가포르는 2002년부터 하수 재이용수인 NEWater를 개발, 비음용수와 간접음용수로 활용하고 있음" },
|
||||
{ text: "재이용수를 NEWater라는 이름으로 명명하고 NEWater에 대한 신뢰 확보를 위해 NEWater 방문자 센터도 건설함", depth: 1 },
|
||||
{ text: "비음용수로 많이 사용하며 건기에는 NEWater를 저수지에 추가하여 원수와 섞음. 원수는 상수도에서 처리 한 후 가정에 수돗물로 공급함", depth: 1 },
|
||||
{ text: "NEWater는 전용 파이프를 통해 주로 웨이퍼 제조 공장, 산업단지, 상업용 건물에서 산업 및 냉각수로 사용. 제일 많이 사용하는 곳은 웨이퍼 제조 공장", depth: 1 },
|
||||
|
||||
{ text: "2024년 4월 기준 1㎥ 당 공업용수 가격은 $1.66이며, Non-domestic 기준 NEWater 가격은 $2.41" },
|
||||
{ text: "공업용수 가격 $1.66은 수도요금(water tariff)과 수자원세(waterborne tax)로 구분됨. $1.66에는 $1만큼의 수자원세가 포함됨" },
|
||||
{ text: "2023년 싱가포르는 산업용수 수요가 높은 산업에 대해 물 재이용을 의무화했으며, 정부는 기금을 통해 물 재이용을 지원함" },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
id: "example-global-australia",
|
||||
title: "[호주] 생활 속 물 재이용 / 광역 물 재이용수 공급",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/globalCase/australia_01.png",
|
||||
alt: "호주 생활 속 물 재이용 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/australia_02.png",
|
||||
alt: "호주 광역 물 재이용수 공급 2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/australia_03.png",
|
||||
alt: "호주 광역 물 재이용수 공급 3",
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
title: "생활용수로 재이용수의 적극 활용",
|
||||
items: [
|
||||
"재이용수를 공원, 골프장 등의 관계용수로 공급(초록색 지역)",
|
||||
"Dual pipe로 재이용수를 특정 주거단지로 공급(보라색 지역)",
|
||||
"현재 7,000여 가구가 재이용수를 사용",
|
||||
"2030년까지 43,000가구로 확대 예정",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "호주 재이용수 광역 활용(퀸즈랜드주 브리즈번)",
|
||||
items: [
|
||||
{ text: "SEQ Water Grid는 브리즈번 남동부지역의 식수 공급을 위해 설정한 광역 수로망" },
|
||||
{ text: "이 광역수로망을 활용하여 물이 부족한 지역으로 물을 공급" },
|
||||
|
||||
{
|
||||
text: (
|
||||
<>
|
||||
12개 댐, 32개 수처리 시설, 3개 재이용시설, 담수화 플랜트 1개, 저수지 28개와{" "}
|
||||
<span className="text-[#01ACE5]">
|
||||
600km 이상의 물 공급 관로를 건설
|
||||
</span>
|
||||
</>
|
||||
),
|
||||
depth: 1,
|
||||
},
|
||||
{
|
||||
text: (
|
||||
<>
|
||||
지역적으로 강수량이 고르지 않아,{" "}
|
||||
<span className="text-[#01ACE5]">
|
||||
댐의 저수율이 불균등 할 때
|
||||
</span>{" "}
|
||||
지역의 물 공급을 보충하는 중요한 역할 수행
|
||||
</>
|
||||
),
|
||||
depth: 1,
|
||||
},
|
||||
|
||||
{ text: "안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능" },
|
||||
{ text: "공급단가는 수질과 운영조건에 따라 다르게 책정" },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "특이사항",
|
||||
items: [
|
||||
"호주는 2000~2009년 강수량 감소로 밀레니엄 가뭄이 발생하여 심각한 물 부족 현상이 발생함",
|
||||
"호주는 해안 도시에 인구가 밀집하여 거주하고 있으며 인구 증가율이 타 선진국 대비 높아, 향후 도시에 공급할 수자원 확보가 필요함",
|
||||
"호주정부는 1990년대부터 하·폐수가 방류될 경우의 환경 피해에 대해 인식하기 시작하고 배출규제와 위원회를 구성하기 시작",
|
||||
"연방정부는 주에 인센티브를 주는 방식으로 물 정책을 장려하고 토지소유권과 물 권리를 구분하여 거래를 허용하고 물 재이용 활성화를 위한 Water Smart Australia 프로그램을 시행",
|
||||
"호주의 용수공급 체계는 영국과 미국의 특성들이 존재하지만, 주의 특성과 주정부의 정책 방향에 따라 주마다 많은 차이가 있음",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "default",
|
||||
id: "example-global-eu",
|
||||
title: "[EU] 법률을 제정, EU국가에서 물 재이용 유도",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/globalCase/eu_01.png",
|
||||
alt: "EU 물 재이용 기술 사례",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/globalCase/eu_02.png",
|
||||
alt: "EU 물 재이용 유도 사례",
|
||||
},
|
||||
],
|
||||
blocks: [
|
||||
{
|
||||
title: "물 재이용에 대한 법률을 제정(2020 제정, 2023 시행)",
|
||||
items: [
|
||||
"유럽 지역의 물 부족 문제를 인식하고 ’20년 물 재이용 법률을 제정 (2020/741)",
|
||||
"2023년부터 모든 EU국가에서 시행",
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "유럽연합 회원국 물 재이용 지원정책",
|
||||
items: [
|
||||
{ text: "유럽연합 차원에서 다양한 물 재이용 프로그램 자금 지원" },
|
||||
|
||||
{ text: "2021년부터 3년간 Life RECYCLIO 프로그램 지원 → 세부내용 물 재이용 프로그램", depth: 1 },
|
||||
|
||||
{ text: "1. 환경 관련 사업에 자금을 지원하는 Life 프로그램의 일환으로 80만 유로를 지원", depth: 2 },
|
||||
{ text: "2. 식품·농업 분야 물 재이용 확대를 위해 기술 이전 및 평가 실시", depth: 2 },
|
||||
|
||||
{ text: "물 재이용 정책 수행을 위해 BOOST-INN 180만 유로를 지원", depth: 1 },
|
||||
|
||||
{ text: "프랑스, 이탈리아, 스페인 등 유럽 선진국 솔루션 공유", depth: 3 },
|
||||
|
||||
{ text: "EU산하 JRC(공동연구센터)에서 물 재이용 기술제공 지원" },
|
||||
|
||||
{ text: (
|
||||
<>
|
||||
‘물 재이용 회원 관리 실무 커뮤니티(Water Reuse Management Community of Practice)’ 시범사업 수행
|
||||
<br />
|
||||
유럽 전역에 성공적인 모델 활용 및 혁신적인 물 재이용 솔루션을 확장할 수 있는 기술을 제공
|
||||
</>
|
||||
),
|
||||
depth: 1
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "특이사항",
|
||||
items: [
|
||||
"2019년 기준 EU 인구의 38%가 물 부족의 영향을 받을 것으로 예상됨. 이 흐름대로면 2030년까지 유럽 유역 전반에 물 부족 문제가 발생하게 될 것으로 예상. 인구 증가, 도시화의 확대, 염분화 및 기후변화들이 수자원 문제들의 주요 요인이며, 식습관 변화와 산업용수 수요 증가 등으로 악화되고 있는 상황",
|
||||
"유럽의 물 부족 상황은 지역에 따라 다르게 나타나 스페인을 비롯한 남부유럽에서 특히 심각함. 전반적으로 북유럽에 비해 남유럽의 물 스트레스가 심하며, 특히 스페인과 포르투갈, 프랑스 동부지역의 물 스트레스가 심각함",
|
||||
"유럽 연합에서도 물 부족 문제를 인식하고 2000년 EU Water Framework Directive를 개발, 이후 2020년 재이용에 관한 법률을 제정",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function DefaultGlobalSection({
|
||||
item,
|
||||
}: {
|
||||
item: Extract<GlobalCase, { type: "default" }>;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={item.slides}
|
||||
/>
|
||||
|
||||
<ContentBlocks
|
||||
blocks={item.blocks}
|
||||
cards={item.cards}
|
||||
table={item.table}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GlobalCaseSection() {
|
||||
const title = "해외 재이용 사례";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
{globalCases.map((item, index) => (
|
||||
<section key={item.id} id={item.id} className="scroll-mt-[120px]">
|
||||
<SectionTitle>{item.title}</SectionTitle>
|
||||
|
||||
{item.type === "overview" ? (
|
||||
<OverviewSection />
|
||||
) : (
|
||||
<DefaultGlobalSection item={item} />
|
||||
)}
|
||||
|
||||
{index !== globalCases.length - 1 && <Hr className="my-20" />}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
"use client";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle"> <span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" /> <span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" /></span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type SupplyRow = {
|
||||
facility: string;
|
||||
usage: string;
|
||||
amount: string;
|
||||
};
|
||||
|
||||
function SupplyTable({ rows }: { rows: SupplyRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 px-10">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-2 text-[18px] font-medium text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#40B5A5] px-4 py-2 text-[18px] font-medium text-white">
|
||||
이용용도
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#73BF4B] px-4 py-2 text-[18px] font-medium text-white">
|
||||
공급량(㎥)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.usage}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-2 text-[16px] text-[#4F4F4F]">
|
||||
{row.amount}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
totalCost,
|
||||
treatment,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
totalCost: string;
|
||||
treatment: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-10 px-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>총사업비 : {totalCost}</li>
|
||||
<li>처리공법 : {treatment}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GreywaterExampleSection() {
|
||||
const title = "중수 재이용 사례";
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-greywater-ansan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 안산시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="안산시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="81억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "경기테크노파크", usage: "청소·화장실용수", amount: "40㎥/일" },
|
||||
{ facility: "올림픽수영장", usage: "청소·화장실/세척·살수용수", amount: "35㎥/일" },
|
||||
{ facility: "와스타디움", usage: "청소·화장실/조경용수", amount: "90㎥/일" },
|
||||
{ facility: "호수실내수영장", usage: "청소·화장실/조경/세척·살수용수", amount: "50㎥/일" },
|
||||
{ facility: "한양대학교 에리카캠퍼스", usage: "청소·화장실용수", amount: "370㎥/일" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
<section id="example-greywater-yongin" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 용인시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="용인시 중수도 설치사업"
|
||||
purpose="물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용"
|
||||
totalCost="40억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{ facility: "여성회관", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "수지아르피아", usage: "청소·화장실용수", amount: "35㎥/일" },
|
||||
{ facility: "지상작전사령부", usage: "청소·화장실/조경/세척·살수용수", amount: "53㎥/일" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
<section id="example-greywater-pyeongtaek" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 평택시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="평택시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="21억원"
|
||||
treatment="MBLM공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "평택대학교",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "50㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle">
|
||||
<span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" />
|
||||
<span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" />
|
||||
</span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
details,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
details: string[];
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-9 px-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
{details.map((item, index) => (
|
||||
<li key={index}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type RainTableRow = {
|
||||
facility: string;
|
||||
storage: string;
|
||||
supply: string;
|
||||
pipe: string;
|
||||
};
|
||||
|
||||
function RainTable({ rows }: { rows: RainTableRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-3 text-[18px] font-medium text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#39B2BD] px-4 py-3 text-[18px] font-medium text-white">
|
||||
저류조용량(㎥)
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#58B99A] px-4 py-3 text-[18px] font-medium text-white">
|
||||
빗물공급기
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#70BF4B] px-4 py-3 text-[18px] font-medium text-white">
|
||||
빗물분사장치(m)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.storage}
|
||||
</td>
|
||||
|
||||
{index === 0 && (
|
||||
<td
|
||||
rowSpan={rows.length}
|
||||
className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]"
|
||||
>
|
||||
{row.supply}
|
||||
</td>
|
||||
)}
|
||||
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-3 text-[16px] text-[#4F4F4F]">
|
||||
{row.pipe}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RainExampleSection() {
|
||||
const title = "빗물 재이용 사례";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
<section id="example-rain-suwon" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 수원시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/suwon_01.png", alt: "수원시 빗물이용시설 사례 1" },
|
||||
{ src: "/images/landing/business/rain/suwon_02.png", alt: "수원시 빗물이용시설 사례 2" },
|
||||
{ src: "/images/landing/business/rain/suwon_03.png", alt: "수원시 빗물이용시설 사례 3" },
|
||||
{ src: "/images/landing/business/rain/suwon_04.png", alt: "수원시 빗물이용시설 사례 4" },
|
||||
{ src: "/images/landing/business/rain/suwon_05.png", alt: "수원시 빗물이용시설 사례 5" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<InfoList
|
||||
projectName="수원시 빗물이용시설 설치사업"
|
||||
purpose="월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물공급기를 설치하여 축서기 도시 열섬효과 및 도로 비산먼지 저감"
|
||||
details={[
|
||||
"총사업비 : 20억원",
|
||||
"위치 : 수원시 영통구, 팔달구 및 장안구 일원",
|
||||
]}
|
||||
/>
|
||||
|
||||
<div className="mt-9 px-10">
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
|
||||
<RainTable
|
||||
rows={[
|
||||
{
|
||||
facility: "광교중학교",
|
||||
storage: "4,500",
|
||||
supply: "1대",
|
||||
pipe: "160",
|
||||
},
|
||||
{
|
||||
facility: "수원월드컵경기장",
|
||||
storage: "12,000",
|
||||
supply: "1대",
|
||||
pipe: "451",
|
||||
},
|
||||
{
|
||||
facility: "수원종합운동장",
|
||||
storage: "10,000",
|
||||
supply: "1대",
|
||||
pipe: "231",
|
||||
},
|
||||
{
|
||||
facility: "수원컨벤션센터",
|
||||
storage: "3,600",
|
||||
supply: "1대",
|
||||
pipe: "460",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
<section id="example-rain-jeju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>제주특별자치도</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/jeju_01.png", alt: "제주특별자치도 빗물이용시설 사례 1" },
|
||||
{ src: "/images/landing/business/rain/jeju_02.png", alt: "제주특별자치도 빗물이용시설 사례 2" },
|
||||
{ src: "/images/landing/business/rain/jeju_03.png", alt: "제주특별자치도 빗물이용시설 사례 3" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<InfoList
|
||||
projectName="제주특별자치도 중규모 빗물이용시설 설치사업"
|
||||
purpose="강우시 비닐하우스에서 발생하는 대체수자원인 빗물을 순환 활용하고자 하는 사업으로, 지하수 사용 저감과 집중호우시 도로와 농경지 침수피해 예방"
|
||||
details={[
|
||||
"총사업비 : 278억원",
|
||||
"위치 : 제주특별자치도 서귀포시 남원읍 위미리 일원",
|
||||
"사업량 : 저류조 7,000㎥, 배수갑 500㎡, 관로 21.7km",
|
||||
"사업기간 : 2024. 5. ~ 2028. 12.",
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
"use client";
|
||||
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
type SewageCase = {
|
||||
id: string;
|
||||
title: string;
|
||||
slides: {
|
||||
src: string;
|
||||
alt: string;
|
||||
}[];
|
||||
projectName: string;
|
||||
purpose: string[];
|
||||
method?: string[];
|
||||
details: string[];
|
||||
note?: string[];
|
||||
};
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="relative inline-block h-[16px] w-[27px] align-middle">
|
||||
<span className="absolute left-0 top-0 h-[16px] w-[16px] rounded-full bg-[#01ACE5]/40" />
|
||||
<span className="absolute left-[7px] top-0 h-[16px] w-[16px] rounded-full bg-[#6DBB4F]/40" />
|
||||
</span>
|
||||
<h4 className="text-[20px] font-semibold text-[#1A1A1A]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function InfoBlock({
|
||||
title,
|
||||
items,
|
||||
}: {
|
||||
title: string;
|
||||
items?: string[];
|
||||
}) {
|
||||
if (!items || items.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<BlockTitle>{title}</BlockTitle>
|
||||
<ul className="mt-4 list-disc space-y-2 pl-8 text-[16px] leading-[1.8] text-[#4F4F4F]">
|
||||
{items.map((item, index) => (
|
||||
<li key={`${title}-${index}`}>{item}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({ item }: { item: SewageCase }) {
|
||||
return (
|
||||
<div className="mt-10 space-y-9 px-10">
|
||||
<InfoBlock title="사업명" items={[item.projectName]} />
|
||||
<InfoBlock title="사업목적" items={item.purpose} />
|
||||
<InfoBlock title="사업방식" items={item.method} />
|
||||
<InfoBlock title="사업 주요 내용" items={item.details} />
|
||||
<InfoBlock title="특이사항" items={item.note} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sewageCases: SewageCase[] = [
|
||||
{
|
||||
id: "example-sewage-guri",
|
||||
title: "경기도 구리시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_01.png",
|
||||
alt: "구리시 도로자동청소시스템",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_02.png",
|
||||
alt: "구리시 도로자동청소시스템 위치도",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_03.png",
|
||||
alt: "구리시 도로자동청소시스템 위치도2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_04.png",
|
||||
alt: "구리시 도로자동청소시스템 원리",
|
||||
},
|
||||
],
|
||||
projectName: "구리시 도로자동청소시스템 설치사업",
|
||||
purpose: [
|
||||
"고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 61억원",
|
||||
"위치 : 구리시 경춘로 및 갈매중앙로 일대 등",
|
||||
"관로 : 5.8km",
|
||||
"공급량 : 178㎥/일",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-bucheon",
|
||||
title: "경기도 부천시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/bucheon_01.png",
|
||||
alt: "부천시 하수 재이용수 생산시설",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/bucheon_02.png",
|
||||
alt: "부천시 하수처리수 재이용 사례",
|
||||
},
|
||||
],
|
||||
projectName: "부천시 하수 재이용수 생산시설 설치 운영",
|
||||
purpose: [
|
||||
"굴포하수처리시설 하수처리수 재이용수 생산시설 설치 운영을 통해 여월천 등 부천 관내 하천의 건천화 방지, 하천 생태계 보존 및 주민들에게 친수 공간 제공",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 212억원",
|
||||
"위치 : 부천시 여월천, 삼정천, 심곡천 및 시민의 강",
|
||||
"관로 : 23.6km",
|
||||
"공급량 : 50,000㎥/일",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-suwon",
|
||||
title: "경기도 수원시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_01.png",
|
||||
alt: "수원시 황구지천1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_02.png",
|
||||
alt: "수원시 황구지천2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/suwon_03.png",
|
||||
alt: "수원시 황구지천 지도",
|
||||
},
|
||||
],
|
||||
projectName: "수원시 황구지천 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"황구지천 공공하수처리시설 하수처리수 재이용사업으로 황구 호매실천 및 금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전",
|
||||
],
|
||||
details: [
|
||||
"총사업비 : 110억원",
|
||||
"위치 : 수원시 권선구 호매실천, 금곡천",
|
||||
"관로 : 3.9km",
|
||||
"공급량 : 25,000㎥/일(하수처리시설 용량 : 45,000㎥/일)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-paju",
|
||||
title: "경기도 파주시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_01.png",
|
||||
alt: "파주시 하수처리수 시설 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_02.png",
|
||||
alt: "파주시 하수처리수 시설 전경2",
|
||||
},
|
||||
],
|
||||
projectName: "파주시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"운정 및 금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주LCD산업단지 내 3개 업체에 공업용수를 공급(LG디스플레이, 희성전자, 엔비텍)",
|
||||
"하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)"],
|
||||
details: [
|
||||
"총사업비 : 651억원",
|
||||
"위치 : 파주시 금촌동 714번지 일원",
|
||||
"관로 : 13.6km",
|
||||
"공급량 : 40,000㎥/일(시설용량 : 41,200㎥/일)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-pohang",
|
||||
title: "경상북도 포항시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_01.png",
|
||||
alt: "포항시 하수처리수 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_02.png",
|
||||
alt: "포항시 하수처리수 전경2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/pohang_03.png",
|
||||
alt: "포항시 하수처리수 재이용 공급계통",
|
||||
},
|
||||
],
|
||||
projectName: "포항시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"포항시 내 공단 개발 확대로 지속적이고 안정적인 공업용수 공급 추진(포스코, 동국산업 등)",
|
||||
],
|
||||
method: ["민간투자사업(운영사 : ㈜포웰, 한국수자원공사)"],
|
||||
details: [
|
||||
"총사업비 : 1,400억원",
|
||||
"위치 : 포항시 남구 상도동 125번지 일원",
|
||||
"관로 : 11.7km",
|
||||
"공급량 : 100,000㎥/일(시설용량 : 100,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-ulsan",
|
||||
title: "울산광역시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_01.png",
|
||||
alt: "울산광역시 폐수처리수 전경1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_02.png",
|
||||
alt: "울산광역시 폐수처리수 전경2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_03.png",
|
||||
alt: "울산광역시 폐수처리수 시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/ulsan_04.png",
|
||||
alt: "울산광역시 폐수처리수 시설2",
|
||||
},
|
||||
],
|
||||
projectName: "울산광역시 폐수처리수 재이용사업",
|
||||
purpose: [
|
||||
"’18년 갈수기시 낙동강 수질 악화로 울산석유화학단지 용수 공급 부족을 해소하기 위해 미포산단 내 한화케미칼 등 8개사 공업용수 공급",
|
||||
],
|
||||
method: ["민간투자사업(시행사 :울산화이텍워터(주))"],
|
||||
details: [
|
||||
"총사업비 : 1,729억원",
|
||||
"위치 : 울산광역시 울주군 청량읍 상개로 14 일원",
|
||||
"관로 : 11.7km",
|
||||
"공급량 : 17,000㎥/일(시설용량 : 86,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O, EDI)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-yeosu",
|
||||
title: "전라남도 여수시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_01.png",
|
||||
alt: "여수시 하수처리수 재이용시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_02.png",
|
||||
alt: "여수시 하수처리수 재이용시설2",
|
||||
},
|
||||
],
|
||||
projectName: "여수시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"수요자가 원하는 양질의 공업용수를 저렴하게 공급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선을 위한 여수국가산단 내 공업용수 공급(GS칼텍스, YNCC 등)",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 여수그린허브(주), 운영사 : 테크로스)"],
|
||||
details: [
|
||||
"총사업비 : 750억원",
|
||||
"위치 : 여수시 신월로 284 일원",
|
||||
"관로 : 18.6km",
|
||||
"공급량 : 50,000㎥/일(시설용량 : 50,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-boryeong",
|
||||
title: "충청남도 보령시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/boryeong_01.png",
|
||||
alt: "보령시 하수처리수 재이용시설1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/boryeong_02.png",
|
||||
alt: "보령시 하수처리수 재이용시설2",
|
||||
},
|
||||
],
|
||||
projectName: "보령시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"한국중부발전(주)의 보령 및 신보령화력발전소에 10,000㎥/일의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진",
|
||||
"국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(기후에너지환경부, 충청남도, 보령시, 한국중부발전(주))를 바탕으로 추진되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모",
|
||||
],
|
||||
method: ["재정사업(2017년 한국환경공단과 사업대행 위·수탁협약 체결)"],
|
||||
details: [
|
||||
"총사업비 : 315억원",
|
||||
"위치 : 보령시 대천방조제로 322 일원",
|
||||
"관로 : 8.7km",
|
||||
"공급량 : 10,000㎥/일(시설용량 : 10,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
note: [
|
||||
"국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕으로 추진 되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모",
|
||||
],
|
||||
},
|
||||
{
|
||||
id: "example-sewage-asan",
|
||||
title: "충청남도 아산시",
|
||||
slides: [
|
||||
{
|
||||
src: "/images/landing/business/sewage/asan_01.png",
|
||||
alt: "아산시 하수처리수 재이용시설",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/asan_02.png",
|
||||
alt: "아산시 하수처리수 재이용 공정도",
|
||||
},
|
||||
],
|
||||
projectName: "아산시 하수처리수 재이용사업",
|
||||
purpose: [
|
||||
"신도시 개발에 따른 인구 급증으로 발생하는 하수를 재처리하여 탕정산업단지(삼성디스플레이)에 공업용수를 안정적으로 공급함으로써 지역기반 및 활성화를 도모",
|
||||
],
|
||||
method: ["민간투자사업(시행사 : 아산스마트원터, 운영사 : 한국수자원공사)"],
|
||||
details: [
|
||||
"총사업비 : 297억원",
|
||||
"위치 : 아산시 탕정면 갈산리 807 일원",
|
||||
"관로 : 4.2km",
|
||||
"공급량 : 27,000㎥/일(시설용량 : 45,000㎥/일)",
|
||||
"처리공법 : 전처리시설(U/F) + 주처리시설(R/O)",
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function SewageExampleSection() {
|
||||
const title = "하수처리 재이용 사례";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10 pb-[120px]">
|
||||
{sewageCases.map((item, index) => (
|
||||
<section key={item.id} id={item.id} className="scroll-mt-[120px]">
|
||||
<SectionTitle>{item.title}</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={item.slides}
|
||||
/>
|
||||
|
||||
<InfoList item={item} />
|
||||
|
||||
{index !== sewageCases.length - 1 && <Hr className="my-20" />}
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/globalCase/australia/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="[호주] 생활 속 물 재이용 / 광역 물 재이용수 공급"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
{ label: "호주", href: "/reuse/business/globalCase/australia" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/globalCase/australia_01.png", alt: "호주 사례 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 생활용수로 재이용수의 적극 활용 */}
|
||||
<section className="mt-8">
|
||||
<SectionTitle>생활용수로 재이용수의 적극 활용</SectionTitle>
|
||||
<div className="px-4 mt-6">
|
||||
<ul className="space-y-4 text-base text-[#4F4F4F]">
|
||||
<li>
|
||||
– 재이용수를 공원, 골프장 등의 관개용수로 공급(초록색 지역)
|
||||
</li>
|
||||
<li>
|
||||
– Dual pipe로 재이용수를 특정 주거단지로 공급(보라색 지역)
|
||||
</li>
|
||||
<li>
|
||||
– 현재 7,000여 가구가 재이용수를 사용
|
||||
</li>
|
||||
<li>
|
||||
– 2030년까지 43,000가구로 확대 예정
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 호주 재이용수 광역 활용(퀸즈랜드주 브리즈번) */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>호주 재이용수 광역 활용(퀸즈랜드주 브리즈번)</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• SEQ Water Grid는 브리즈번 남동부지역의 식수 공급을 위해 설정한 광역 수로망
|
||||
</li>
|
||||
<li>
|
||||
• 이 광역수로망을 활용하여 물이 부족한 지역으로 물을 공급<br />
|
||||
<ul className="list-inside text-[#767676] ml-4 mt-2">
|
||||
<li>– 1,618억의 사업비 중 92% 국비, 지방비 8% 투입</li>
|
||||
<li>– 재이용수는 24km 관로로 타이난 TSMC로 공급</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• 12개 댐, 32개 수처리 시설, 3개 재이용시설, 담수화 플랜트 1개, 저수지 28개와 600km 이상의 물 공급 관로를 건설
|
||||
</li>
|
||||
<li>
|
||||
• 지역적으로 강수량이 고르지 않아, 댐의 저수율이 불균등할 때 지역의 물 공급을 보충하는 중요한 역할을 수행
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>호주는 2000~2009년 강수량 감소로 밀레니엄 가뭄이 발생하여 심각한 물 부족 현상이 발생함</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>호주는 해안 도시에 인구가 밀집하여 거주하고 있으며 인구 증가율이 타 선진국 대비 높아, 향후 도시에 공급할 수자원 확보가 필요함</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>호주정부는 1990년대부터 하·폐수가 방류될 경우의 환경 피해에 대해 인식하기 시작하고 배출규제와 위원회를 구성하기 시작</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>연방정부는 주에 인센티브를 주는 방식으로 물 정책을 장려하고 토지소유권과 물 권리를 구분하여 거래를 허용하고 물 재이용 활성화를 위한 Water Smart Australia 프로그램을 시행</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>호주의 용수공급 체계는 영국과 미국의 특성들이 존재하지만, 주의 특성과 주 정부의 정책 방향에 따라 주마다도 많은 차이가 있음. 특히 대부분의 주에서 수 자원 개발 기능과 수도 공급의 기능을 분리하여 주정부<br/>산하 기관에서 운영하는게 특징</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/globalCase/eu/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="[EU] 법률을 제정, EU국가에서 물 재이용 유도"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
{ label: "EU", href: "/reuse/business/globalCase/eu" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/globalCase/eu_01.png", alt: "EU 사례 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 물 재이용에 대한 법률을 제정(2020 제정, 2023 시행) */}
|
||||
<section className="mt-8">
|
||||
<SectionTitle>물 재이용에 대한 법률을 제정(2020 제정, 2023 시행)</SectionTitle>
|
||||
<div className="px-4 mt-6">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 유럽 지역의 물 부족 문제를 인식하고 ’20년 물 재이용 법률을 제정 (2020/741)
|
||||
</li>
|
||||
<li>
|
||||
• 2023년부터 모든 EU국가에서 시행
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 유럽연합 회원국 물 재이용 지원정책 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>유럽연합 회원국 물 재이용 지원정책</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 유럽연합 차원에서 다양한 물 재이용 프로그램 자금 지원<br />
|
||||
<ul className="list-inside space-y-4 text-[#767676] text-sm ml-4 mt-2">
|
||||
<li>– 2021년부터 3년간 Life RECYCLIO 프로그램 지원 → 세부내용 물 재이용 프로그램<br/>
|
||||
<ul className="list-inside space-y-2 text-[#767676] ml-4 mt-2">
|
||||
<li>1. 환경 관련 사업에 자금을 지원하는 Life 프로그램의 일환으로 80만 유로를 지원</li>
|
||||
<li>2. 식품·농업 분야 물 재이용 확대를 위해 기술 이전 및 평가 실시</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>– 물 재이용 정책 수행을 위해 BOOST-INN 180만 유로를 지원<br/>
|
||||
<ul className="list-inside space-y-2 text-[#767676] ml-4 mt-2">
|
||||
<li> *프랑스, 이탈리아, 스페인 등 유럽 선진국 솔루션 공유</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• EU산하 JRC(공동연구센터)에서 물 재이용 기술제공 지원<br />
|
||||
<ul className="list-inside space-y-2 text-[#767676] text-sm ml-4 mt-2">
|
||||
<li>‘물 재이용 회원 관리 실무 커뮤니티(Water Reuse Management Community of Practice)’ 시범사업 수행</li>
|
||||
<li>유럽 전역에 성공적인 모델 활용 및 혁신적인 물 재이용 솔루션을 확장할 수 있는 기술을 제공</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>2019년 기준 EU 인구의 38%가 물 부족의 영향을 받을 것으로 예상됨. 이 흐름대로면 2030년까지 유럽 유역 전반에 물 부족 문제가 발생하게 될 것으로 예상. 인구 증가, 도시화의 확대, 염분화 및 기후변화들이 수자원 문제들의 주요 요인이며, 식습관 변화와 산업용수 수요 증가 등으로 악화되고 있는 상황</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>유럽의 물 부족 상황은 지역에 따라 다르게 나타나 스페인을 비롯한 남부유럽 에서 특히 심각함. 전반적으로 북유럽에 비해 남유럽의 물 스트레스가 심하며, 특히 스페인과 포르투갈, 프랑스 동부지역의 물 스트레스가 심각함</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>유럽 연합에서도 물 부족 문제를 인식하고 2000년 EU Water Framework Directive을 개발, 이후 2020년 재이용에 관한 법률을 제정</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/globalCase/overview/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="해외 재이용 사례 개요"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
{ label: "사례개요", href: "/reuse/business/globalCase/overview" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/globalCase/overview_01.png", alt: "해외 재이용 사례 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="rounded-md bg-[#e9f0f1] p-8">
|
||||
<h3 className="text-xl font-bold text-gray-800 leading-relaxed">
|
||||
2024년 하수도 통계 기준 15.6%이며,<br />
|
||||
제2차 물 재이용 기본 계획을 고려한 2030년 목표는 <span className="font-extrabold">17%</span>
|
||||
</h3>
|
||||
|
||||
{/* 진행 바 영역 */}
|
||||
<div className="mt-6 space-y-4">
|
||||
{/* 2024 */}
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600">’24년도</span>
|
||||
<div className="relative h-7 rounded-r-lg">
|
||||
<div className="h-7 w-[112px] rounded-r-lg bg-gray-400" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-700">15.6%</span>
|
||||
</div>
|
||||
|
||||
{/* 2030 */}
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600">’30년도</span>
|
||||
<div className="relative">
|
||||
<div className="h-10 w-[327px] rounded-r-lg bg-gradient-to-r from-sky-500 to-green-500" />
|
||||
</div>
|
||||
<span className="text-xl font-bold text-gray-900">17%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 설명 문구 */}
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 우리나라의 하수 처리 재이용률은 <span className="text-sky-600 ">15% 수준</span>
|
||||
</li>
|
||||
<li>
|
||||
• 대만은 가뭄과 공업용수 증가에 대응하여
|
||||
<span className="text-sky-600 ">
|
||||
{" "}현재 4.3% 수준이며, 16.5%까지 확대 계획 수립
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{/* 테이블 */}
|
||||
<div className="mt-6">
|
||||
<table className="w-full text-center text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-[#C2CAD2] px-4 py-3 text-black rounded-t-lg">국가</th>
|
||||
<th className="bg-sky-600 px-4 py-3 text-white rounded-t-lg">해외정보</th>
|
||||
<th className="bg-teal-500 px-4 py-3 text-white rounded-t-lg">현재</th>
|
||||
<th className="bg-green-500 px-4 py-3 text-white rounded-t-lg">미래(목표, 예측치)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="bg-white border border-t-0">
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 font-medium">한국</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">12.1%</td>
|
||||
<td className="px-4 py-3">15.6%</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">17%</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 font-medium">대만</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">2.4%</td>
|
||||
<td className="px-4 py-3">4.3%</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">16.5%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 주요 국가의 하수처리수 재이용률 (%) */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>주요 국가의 하수처리수 재이용률 (%)</SectionTitle>
|
||||
{/* 설명 문구 */}
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 전 세계적으로 물 재이용은 호주, 유럽, 아프리카, 미국 등 가뭄이 잦거나 심각한 가뭄이 발생한 국가에서 많이 이루어짐
|
||||
</li>
|
||||
<li>
|
||||
• 이스라엘, 쿠웨이트 등 중동 및 북아프리카 지역은 가용 수자원이 제한되어 있어 재이용량 비중도 매우 높은 편
|
||||
</li>
|
||||
</ul>
|
||||
{/* 테이블 (2단 병렬: 좌/우 데이터) */}
|
||||
<div className="mt-6">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="min-w-[900px] w-full text-center text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
{/* 좌측 3컬럼 */}
|
||||
<th className="bg-[#C2CAD2] px-4 py-3 text-black rounded-t-lg">국가명</th>
|
||||
<th className="bg-sky-600 px-4 py-3 text-white rounded-t-lg">지역</th>
|
||||
<th className="bg-green-500 px-4 py-3 text-white rounded-t-lg">재이용률</th>
|
||||
|
||||
{/* 우측 3컬럼 */}
|
||||
<th className="bg-[#C2CAD2] px-4 py-3 text-black rounded-t-lg">국가명</th>
|
||||
<th className="bg-sky-600 px-4 py-3 text-white rounded-t-lg">지역</th>
|
||||
<th className="bg-green-500 px-4 py-3 text-white rounded-t-lg">재이용률</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="bg-white border border-t-0">
|
||||
{[
|
||||
{
|
||||
L: { c: "이스라엘", r: "중동&북아프리카", v: "92.3" },
|
||||
R: { c: "튀르키예", r: "동유럽&중앙아시아", v: "26.6" },
|
||||
},
|
||||
{
|
||||
L: { c: "쿠웨이트", r: "중동&북아프리카", v: "89.3" },
|
||||
R: { c: "독일", r: "서유럽", v: "25.0" },
|
||||
},
|
||||
{
|
||||
L: { c: "호주", r: "동아시아&태평양", v: "49.0" },
|
||||
R: { c: "스페인", r: "서유럽", v: "24.4" },
|
||||
},
|
||||
{
|
||||
L: { c: "싱가포르", r: "동아시아&태평양", v: "44.4" },
|
||||
R: { c: "한국", r: "동아시아&태평양", v: "12.1" },
|
||||
},
|
||||
{
|
||||
L: { c: "그리스", r: "서유럽", v: "39.1" },
|
||||
R: { c: "미국", r: "북아메리카", v: "13.1" },
|
||||
},
|
||||
{
|
||||
L: { c: "사우디아라비아", r: "중동&북아프리카", v: "32.8" },
|
||||
R: { c: "프랑스", r: "서유럽", v: "9.5" },
|
||||
},
|
||||
{
|
||||
L: { c: "이탈리아", r: "서유럽", v: "31.7" },
|
||||
R: { c: "대만", r: "동아시아&태평양", v: "2.4" },
|
||||
},
|
||||
{
|
||||
L: { c: "룩셈부르크", r: "서유럽", v: "29.1" },
|
||||
R: { c: "일본", r: "동아시아&태평양", v: "0.8" },
|
||||
},
|
||||
{
|
||||
L: { c: "포르투갈", r: "서유럽", v: "29.0" },
|
||||
R: { c: "", r: "", v: "" }, // 마지막 우측은 빈칸(이미지처럼)
|
||||
},
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="border-t">
|
||||
{/* 좌측 */}
|
||||
<td className="px-4 py-3 font-medium text-gray-700">{row.L.c}</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">{row.L.r}</td>
|
||||
<td className="px-4 py-3 bg-green-50 text-gray-700">{row.L.v}</td>
|
||||
|
||||
{/* 우측 */}
|
||||
<td className="px-4 py-3 font-medium text-gray-700">{row.R.c}</td>
|
||||
<td className="px-4 py-3 bg-sky-50 text-gray-700">{row.R.r}</td>
|
||||
<td className="px-4 py-3 bg-green-50 text-gray-700">{row.R.v}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,844 @@
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function GlobalCasePage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="해외 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 사례 개요 */}
|
||||
<section id="overview" className="scroll-mt-[120px]">
|
||||
<SectionTitle>사례 개요</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/overview_01.png",
|
||||
alt: "해외 재이용 사례 개요 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<div className="rounded-md bg-[#e9f0f1] p-8">
|
||||
<h3 className="text-xl font-bold leading-relaxed text-gray-800">
|
||||
2024년 하수도 통계 기준 15.6%이며,
|
||||
<br />
|
||||
제2차 물 재이용 기본 계획을 고려한 2030년 목표는{" "}
|
||||
<span className="font-extrabold">17%</span>
|
||||
</h3>
|
||||
|
||||
<div className="mt-6 space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600">’24년도</span>
|
||||
<div className="relative h-7 rounded-r-lg">
|
||||
<div className="h-7 w-[112px] rounded-r-lg bg-gray-400" />
|
||||
</div>
|
||||
<span className="text-sm text-gray-700">15.6%</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-sm text-gray-600">’30년도</span>
|
||||
<div className="relative">
|
||||
<div className="h-10 w-[327px] rounded-r-lg bg-gradient-to-r from-sky-500 to-green-500" />
|
||||
</div>
|
||||
<span className="text-xl font-bold text-gray-900">17%</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 우리나라의 하수 처리 재이용률은{" "}
|
||||
<span className="text-sky-600">15% 수준</span>
|
||||
</li>
|
||||
<li>
|
||||
• 대만은 가뭄과 공업용수 증가에 대응하여
|
||||
<span className="text-sky-600">
|
||||
{" "}
|
||||
현재 4.3% 수준이며, 16.5%까지 확대 계획 수립
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-6 overflow-x-auto">
|
||||
<table className="w-full min-w-[640px] text-center text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="rounded-t-lg bg-[#C2CAD2] px-4 py-3 text-black">
|
||||
국가
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-sky-600 px-4 py-3 text-white">
|
||||
해외정보
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-teal-500 px-4 py-3 text-white">
|
||||
현재
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-green-500 px-4 py-3 text-white">
|
||||
미래(목표, 예측치)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="border border-t-0 bg-white">
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 font-medium">한국</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">12.1%</td>
|
||||
<td className="px-4 py-3">15.6%</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">17%</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 font-medium">대만</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">2.4%</td>
|
||||
<td className="px-4 py-3">4.3%</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">16.5%</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>주요 국가의 하수처리수 재이용률 (%)</SectionTitle>
|
||||
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 전 세계적으로 물 재이용은 호주, 유럽, 아프리카, 미국 등 가뭄이
|
||||
잦거나 심각한 가뭄이 발생한 국가에서 많이 이루어짐
|
||||
</li>
|
||||
<li>
|
||||
• 이스라엘, 쿠웨이트 등 중동 및 북아프리카 지역은 가용 수자원이
|
||||
제한되어 있어 재이용량 비중도 매우 높은 편
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-6 overflow-x-auto">
|
||||
<table className="min-w-[900px] w-full text-center text-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="rounded-t-lg bg-[#C2CAD2] px-4 py-3 text-black">
|
||||
국가명
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-sky-600 px-4 py-3 text-white">
|
||||
지역
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-green-500 px-4 py-3 text-white">
|
||||
재이용률
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-[#C2CAD2] px-4 py-3 text-black">
|
||||
국가명
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-sky-600 px-4 py-3 text-white">
|
||||
지역
|
||||
</th>
|
||||
<th className="rounded-t-lg bg-green-500 px-4 py-3 text-white">
|
||||
재이용률
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="border border-t-0 bg-white">
|
||||
{[
|
||||
{
|
||||
L: { c: "이스라엘", r: "중동&북아프리카", v: "92.3" },
|
||||
R: { c: "튀르키예", r: "동유럽&중앙아시아", v: "26.6" },
|
||||
},
|
||||
{
|
||||
L: { c: "쿠웨이트", r: "중동&북아프리카", v: "89.3" },
|
||||
R: { c: "독일", r: "서유럽", v: "25.0" },
|
||||
},
|
||||
{
|
||||
L: { c: "호주", r: "동아시아&태평양", v: "49.0" },
|
||||
R: { c: "스페인", r: "서유럽", v: "24.4" },
|
||||
},
|
||||
{
|
||||
L: { c: "싱가포르", r: "동아시아&태평양", v: "44.4" },
|
||||
R: { c: "한국", r: "동아시아&태평양", v: "12.1" },
|
||||
},
|
||||
{
|
||||
L: { c: "그리스", r: "서유럽", v: "39.1" },
|
||||
R: { c: "미국", r: "북아메리카", v: "13.1" },
|
||||
},
|
||||
{
|
||||
L: { c: "사우디아라비아", r: "중동&북아프리카", v: "32.8" },
|
||||
R: { c: "프랑스", r: "서유럽", v: "9.5" },
|
||||
},
|
||||
{
|
||||
L: { c: "이탈리아", r: "서유럽", v: "31.7" },
|
||||
R: { c: "대만", r: "동아시아&태평양", v: "2.4" },
|
||||
},
|
||||
{
|
||||
L: { c: "룩셈부르크", r: "서유럽", v: "29.1" },
|
||||
R: { c: "일본", r: "동아시아&태평양", v: "0.8" },
|
||||
},
|
||||
{
|
||||
L: { c: "포르투갈", r: "서유럽", v: "29.0" },
|
||||
R: { c: "", r: "", v: "" },
|
||||
},
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="border-t">
|
||||
<td className="px-4 py-3 font-medium text-gray-700">
|
||||
{row.L.c}
|
||||
</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">
|
||||
{row.L.r}
|
||||
</td>
|
||||
<td className="bg-green-50 px-4 py-3 text-gray-700">
|
||||
{row.L.v}
|
||||
</td>
|
||||
|
||||
<td className="px-4 py-3 font-medium text-gray-700">
|
||||
{row.R.c}
|
||||
</td>
|
||||
<td className="bg-sky-50 px-4 py-3 text-gray-700">
|
||||
{row.R.r}
|
||||
</td>
|
||||
<td className="bg-green-50 px-4 py-3 text-gray-700">
|
||||
{row.R.v}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 대만 */}
|
||||
<section id="taiwan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>대만</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/taiwan_01.png",
|
||||
alt: "대만 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-8">
|
||||
<SectionTitle>재생 수자원 개발법 제정(2015년제정, 2022개정)</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 목적: 수자원 지속 가능성 향상, 재생 수자원 활용을 통해 물부족
|
||||
문제 해결
|
||||
</li>
|
||||
<li>
|
||||
• 내용: 재생 수자원 정의·분류, 이용 의무, 이용 제한, 수질 기준 등
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-5 rounded-sm bg-[#e9f0f1] p-8">
|
||||
<ul className="space-y-1 text-2xl text-[#1A1A1A]">
|
||||
<li>
|
||||
– 제4조. 물 소비 계획에 따른 재이용수 이용 관련 내용 (일정 비율
|
||||
재이용수 사용 규정)
|
||||
</li>
|
||||
<li>
|
||||
– 제5조. 재이용수의 운영자, 사용자 관련 내용 (지자체에서 하수
|
||||
무상공급 제공 가능)
|
||||
</li>
|
||||
<li>
|
||||
– 제7조. 재이용수 이용 범위, 이용량 재이용수 수질 기준 내용
|
||||
(식품, 제약산업 사용 불가)
|
||||
</li>
|
||||
<li>
|
||||
– 제8조. 재이용수 사업 주체, 책임 등에 관한 내용 (광역인 경우,
|
||||
정부가 지자체 지정)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>대만정부 하수재이용 사업 시행</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 대만은 물 재이용에 적극적이며 국가주도로 하수재이용 사업
|
||||
시행(안핑)
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-[#767676]">
|
||||
<li>– 1,618억의 사업비 중 92% 국비, 지방비 8% 투입</li>
|
||||
<li>– 재이용수는 24km 관로로 타이난 TSMC로 공급</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>• 안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능</li>
|
||||
<li>• 공급단가는 수질과 운영조건에 따라 다르게 책정</li>
|
||||
</ul>
|
||||
|
||||
<div className="mt-3 overflow-x-auto">
|
||||
<p className="py-2 text-center text-2xl">
|
||||
〈대만 재이용 시설별 재이용수 공급단지〉
|
||||
</p>
|
||||
<table className="min-w-[900px] w-full text-center text-sm">
|
||||
<thead className="border">
|
||||
<tr>
|
||||
<th className="bg-[#C2CAD2] px-4 py-3 text-black">사업</th>
|
||||
<th className="bg-sky-600 px-4 py-3 text-white">
|
||||
가격(NTD/㎥)
|
||||
</th>
|
||||
<th className="bg-green-500 px-4 py-3 text-white">
|
||||
가격(원/㎥)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="border border-t-0 bg-white text-[#4F4F4F]">
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Fongshan(가오슝)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">23.39</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">1,038원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Linhai(가오슝)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">19.28</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">858원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Anping(타이난)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">38.28</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">1,699원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Yongkang(타이난)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">23.67</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">1,050원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Rende(타이난)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">28.26</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">1,254원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Shuinan(타이중)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">15.00</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">668원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="bg-white px-4 py-3">Quaitou(가오슝)</td>
|
||||
<td className="bg-[#EAF5F5] px-4 py-3">35.44</td>
|
||||
<td className="bg-[#E7F4E2] px-4 py-3">1,573원</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
대만은 풍부한 강수량에도, 지형적 특성으로 인해 수자원 확보에
|
||||
어려움을 겪고 있으며, 대만 남부지역의 경우 우기와 건기 사이의
|
||||
강수량 차이가 큼
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
대만은 최근 심각한 가뭄이 여러 해에 걸쳐서 발생하고 있으며,
|
||||
강수량 감소와 함께 대만 정부 주도하에 반도체 산업을 국가 핵심
|
||||
산업으로 선정하고 반도체 산업 육성 계획을 세움. 그 결과,
|
||||
2023년 파운드리 시장의 68.3%를 차지하는 반도체 강국으로
|
||||
성장했으며 이로 인한 공업용수 수요가 크게 증가
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
반도체 산업은 물 소비가 많아, 안정적인 용수공급이 매우
|
||||
중요한데 현재 19개의 반도체 공장이 물이 부족한 대만 남부지역에
|
||||
위치함. 2021년 가뭄 발생 당시, 산업단지 기업들도 용수 공급에
|
||||
어려움을 겪음. 이는 대만 남부 지역의 물 부족, 물 분배 등에
|
||||
대한 문제를 야기함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
대만 정부는 산업단지 기업들에게 2023년부터 물에 대한
|
||||
추가요금 부과를 시작했으며, 농업용수에 대해서는 강력한
|
||||
억제정책을 시행함. 안정적인 용수 공급을 위해서 정부와
|
||||
기업에서 자체적으로 재이용수 시설에 대한 투자가 이루어지고
|
||||
있음.
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
대만은 2004년 기후변화로 인한 물 부족 상황을 해결하기 위해
|
||||
수자원 보호 전략을 승인하고, 2015년 대만 환경부는 재생 수자원
|
||||
개발법을 발표, 2022년에는 한 차례 개정
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
대만은 물 소비량이 많은 산업(high water consuming
|
||||
industries)과 하수처리장(municipal wastewater treatment
|
||||
plants), 두 곳을 재이용수의 주요 공급원으로 계획
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 호주 */}
|
||||
<section id="australia" className="scroll-mt-[120px]">
|
||||
<SectionTitle>호주</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/australia_01.png",
|
||||
alt: "호주 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-8">
|
||||
<SectionTitle>생활용수로 재이용수의 적극 활용</SectionTitle>
|
||||
<div className="mt-6 px-4">
|
||||
<ul className="space-y-4 text-base text-[#4F4F4F]">
|
||||
<li>– 재이용수를 공원, 골프장 등의 관개용수로 공급(초록색 지역)</li>
|
||||
<li>
|
||||
– Dual pipe로 재이용수를 특정 주거단지로 공급(보라색 지역)
|
||||
</li>
|
||||
<li>– 현재 7,000여 가구가 재이용수를 사용</li>
|
||||
<li>– 2030년까지 43,000가구로 확대 예정</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>호주 재이용수 광역 활용(퀸즈랜드주 브리즈번)</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• SEQ Water Grid는 브리즈번 남동부지역의 식수 공급을 위해
|
||||
설정한 광역 수로망
|
||||
</li>
|
||||
<li>
|
||||
• 이 광역수로망을 활용하여 물이 부족한 지역으로 물을 공급
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-[#767676]">
|
||||
<li>– 1,618억의 사업비 중 92% 국비, 지방비 8% 투입</li>
|
||||
<li>– 재이용수는 24km 관로로 타이난 TSMC로 공급</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• 12개 댐, 32개 수처리 시설, 3개 재이용시설, 담수화 플랜트 1개,
|
||||
저수지 28개와 600km 이상의 물 공급 관로를 건설
|
||||
</li>
|
||||
<li>
|
||||
• 지역적으로 강수량이 고르지 않아, 댐의 저수율이 불균등할 때
|
||||
지역의 물 공급을 보충하는 중요한 역할을 수행
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
호주는 2000~2009년 강수량 감소로 밀레니엄 가뭄이 발생하여
|
||||
심각한 물 부족 현상이 발생함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
호주는 해안 도시에 인구가 밀집하여 거주하고 있으며 인구
|
||||
증가율이 타 선진국 대비 높아, 향후 도시에 공급할 수자원 확보가
|
||||
필요함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
호주정부는 1990년대부터 하·폐수가 방류될 경우의 환경 피해에
|
||||
대해 인식하기 시작하고 배출규제와 위원회를 구성하기 시작
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
연방정부는 주에 인센티브를 주는 방식으로 물 정책을 장려하고
|
||||
토지소유권과 물 권리를 구분하여 거래를 허용하고 물 재이용
|
||||
활성화를 위한 Water Smart Australia 프로그램을 시행
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
호주의 용수공급 체계는 영국과 미국의 특성들이 존재하지만, 주의
|
||||
특성과 주 정부의 정책 방향에 따라 주마다도 많은 차이가 있음.
|
||||
특히 대부분의 주에서 수 자원 개발 기능과 수도 공급의 기능을
|
||||
분리하여 주정부 산하 기관에서 운영하는게 특징
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 싱가포르 */}
|
||||
<section id="singapore" className="scroll-mt-[120px]">
|
||||
<SectionTitle>싱가포르</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/singapore_01.png",
|
||||
alt: "싱가포르 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-8">
|
||||
<SectionTitle>싱가포르 NEWater 비중 확대</SectionTitle>
|
||||
<div className="mt-6 px-4">
|
||||
<ul className="space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 2003년 공식 NEWater 공장을 가동했으며 신뢰확보를 위해
|
||||
NEWater Visitor Center를 함께 건설
|
||||
</li>
|
||||
<li>• NEWater 전체파이프 공급 → 반도체 공장에서 가장 많이 사용</li>
|
||||
<li>
|
||||
• 2060년까지 NEWater 비중 55%까지 높이는 것이 싱가포르의 목표
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>싱가포르 재이용수 의무화 및 지원정책</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 공업용수 수요가 높은 산업에 대해 물 재이용을 의무화
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-sm text-[#767676]">
|
||||
<li>
|
||||
– 반도체업계 전체 비 재이용수(non-domestic용수) 17% 차지 →
|
||||
공업용수 수요 원인
|
||||
</li>
|
||||
<li>
|
||||
– 2024년 1월 1일부터 연간 최소 60,000㎥를 소비하는 사업자는
|
||||
재이용수 사용 의무화
|
||||
</li>
|
||||
<li>
|
||||
– 반도체 공장의 경우 최소 50% 이상의 재이용수를 요구함(현재는
|
||||
43% 정도)
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• 재정 지원을 통해 재이용 활성화를 지원
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-sm text-[#767676]">
|
||||
<li>
|
||||
– 재이용 설비를 기준으로 2025년까지 싱가포르 수자원공사의
|
||||
2가지 금융상품 제공
|
||||
<ul className="ml-4 mt-2 list-inside text-[#767676]">
|
||||
<li>1. Water Efficiency Fund</li>
|
||||
<li>2. Industrial Water Solutions Demonstration Fund</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="mt-2">
|
||||
– 2025년 기준 지원 한도 상향 → 물 재이용 활성 도모
|
||||
<ul className="ml-4 mt-2 list-inside text-[#767676]">
|
||||
<li>
|
||||
투입비용 50% → 75% 지원 / WSDP: $400만 → $500만
|
||||
</li>
|
||||
<li>
|
||||
물 효율이 높은 장비를 도입하는 데에도 최대 $300만 지원
|
||||
가능
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
싱가포르는 천연 수자원이 많지 않아, 말레이시아 조호르와 1962년
|
||||
맺은 협약으로 물을 수입해왔으며, 재이용수를 늘리기 위한 노력을
|
||||
계속하고 있음
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-sm text-[#767676]">
|
||||
<li>
|
||||
- 싱가포르는 국토 내에 호수와 하천이적고 집수공간도 적어
|
||||
빗물로 확보할 수 있는 수자원량은 전체 물 수요의 20%에
|
||||
불과함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
싱가포르는 도심에 저수지를 건설하 고 건물에 빗물이용시설을
|
||||
건설하여 빗물을 재이용하고 있음
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
싱가포르는 2002년부터 하수재이용수인 NEWater를 개발, 비음
|
||||
용수와 간접음용수로 활용하고 있음
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside text-sm/6 text-[#767676]">
|
||||
<li>
|
||||
- 재이용수를 NEWater라는 이름으로 명명하고 NEWater에 대한
|
||||
신뢰 확보를 위해 NEWater 방문자 센터도 건설함
|
||||
</li>
|
||||
<li>
|
||||
- 비음용수로 많이 사용하며 건기에는 NEWater를 저수지에
|
||||
추가하여 원수와 섞음. 원수는 상수도에서 처리 한 후 가정에
|
||||
수돗물로 공급함
|
||||
</li>
|
||||
<li>
|
||||
- NEWater는 전용 파이프를 통해 주로 웨이퍼 제조 공장,
|
||||
산업단지, 상업용 건물에서 산업 및 냉각수로 사용. 제일 많이
|
||||
사용하는 곳은 웨이퍼 제조 공장
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
2024년 4월 기준 1㎥ 당 공업용수 가격은 $1.66이며,
|
||||
Non-domestic 기준 NEWater 가격은 $2.41
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
공업용수 가격 $1.66은 수도요금(water tariff)과 수자원세
|
||||
(waterborne tax)로 구분됨. $1.66에는 $1만큼의 수자원세가
|
||||
포함됨
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
2023년 싱가포르는 산업용수 수요가 높은 산업에 대해 물 재
|
||||
이용을 의무화했으며, 정부는 기금을 통해 물 재이용을 지원함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* EU */}
|
||||
<section id="eu" className="scroll-mt-[120px] pb-[200px]">
|
||||
<SectionTitle>EU</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[1240px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/globalCase/eu_01.png",
|
||||
alt: "EU 사례 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-8">
|
||||
<SectionTitle>물 재이용에 대한 법률을 제정(2020 제정, 2023 시행)</SectionTitle>
|
||||
<div className="mt-6 px-4">
|
||||
<ul className="space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 유럽 지역의 물 부족 문제를 인식하고 ’20년 물 재이용 법률을
|
||||
제정 (2020/741)
|
||||
</li>
|
||||
<li>• 2023년부터 모든 EU국가에서 시행</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>유럽연합 회원국 물 재이용 지원정책</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 유럽연합 차원에서 다양한 물 재이용 프로그램 자금 지원
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside space-y-4 text-sm text-[#767676]">
|
||||
<li>
|
||||
– 2021년부터 3년간 Life RECYCLIO 프로그램 지원 → 세부내용
|
||||
물 재이용 프로그램
|
||||
<ul className="ml-4 mt-2 list-inside space-y-2 text-[#767676]">
|
||||
<li>
|
||||
1. 환경 관련 사업에 자금을 지원하는 Life 프로그램의
|
||||
일환으로 80만 유로를 지원
|
||||
</li>
|
||||
<li>
|
||||
2. 식품·농업 분야 물 재이용 확대를 위해 기술 이전 및 평가
|
||||
실시
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
– 물 재이용 정책 수행을 위해 BOOST-INN 180만 유로를 지원
|
||||
<ul className="ml-4 mt-2 list-inside space-y-2 text-[#767676]">
|
||||
<li>
|
||||
*프랑스, 이탈리아, 스페인 등 유럽 선진국 솔루션 공유
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• EU산하 JRC(공동연구센터)에서 물 재이용 기술제공 지원
|
||||
<br />
|
||||
<ul className="ml-4 mt-2 list-inside space-y-2 text-sm text-[#767676]">
|
||||
<li>
|
||||
‘물 재이용 회원 관리 실무 커뮤니티(Water Reuse Management
|
||||
Community of Practice)’ 시범사업 수행
|
||||
</li>
|
||||
<li>
|
||||
유럽 전역에 성공적인 모델 활용 및 혁신적인 물 재이용
|
||||
솔루션을 확장할 수 있는 기술을 제공
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
2019년 기준 EU 인구의 38%가 물 부족의 영향을 받을 것으로
|
||||
예상됨. 이 흐름대로면 2030년까지 유럽 유역 전반에 물 부족
|
||||
문제가 발생하게 될 것으로 예상. 인구 증가, 도시화의 확대,
|
||||
염분화 및 기후변화들이 수자원 문제들의 주요 요인이며, 식습관
|
||||
변화와 산업용수 수요 증가 등으로 악화되고 있는 상황
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
유럽의 물 부족 상황은 지역에 따라 다르게 나타나 스페인을
|
||||
비롯한 남부유럽 에서 특히 심각함. 전반적으로 북유럽에 비해
|
||||
남유럽의 물 스트레스가 심하며, 특히 스페인과 포르투갈, 프랑스
|
||||
동부지역의 물 스트레스가 심각함
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside space-y-1 pl-6 leading-relaxed">
|
||||
<li>
|
||||
유럽 연합에서도 물 부족 문제를 인식하고 2000년 EU Water
|
||||
Framework Directive을 개발, 이후 2020년 재이용에 관한 법률을
|
||||
제정
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/globalCase/singapore/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="[싱가포르] 수요가 높은 산업의 물 재이용 의무화"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
{ label: "싱가포르", href: "/reuse/business/globalCase/singapore" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/globalCase/singapore_01.png", alt: "싱가포르 사례 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 싱가포르 NEWater 비중 확대 */}
|
||||
<section className="mt-8">
|
||||
<SectionTitle>싱가포르 NEWater 비중 확대</SectionTitle>
|
||||
<div className="px-4 mt-6">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 2003년 공식 NEWater 공장을 가동했으며 신뢰확보를 위해 NEWater Visitor Center를 함께 건설
|
||||
</li>
|
||||
<li>
|
||||
• NEWater 전체파이프 공급 → 반도체 공장에서 가장 많이 사용
|
||||
</li>
|
||||
<li>
|
||||
• 2060년까지 NEWater 비중 55%까지 높이는 것이 싱가포르의 목표
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 싱가포르 재이용수 의무화 및 지원정책 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>싱가포르 재이용수 의무화 및 지원정책</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 공업용수 수요가 높은 산업에 대해 물 재이용을 의무화<br />
|
||||
<ul className="list-inside text-[#767676] text-sm ml-4 mt-2">
|
||||
<li>– 반도체업계 전체 비 재이용수(non-domestic용수) 17% 차지 → 공업용수 수요 원인</li>
|
||||
<li>– 2024년 1월 1일부터 연간 최소 60,000㎥를 소비하는 사업자는 재이용수 사용 의무화</li>
|
||||
<li>– 반도체 공장의 경우 최소 50% 이상의 재이용수를 요구함(현재는 43% 정도)</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• 재정 지원을 통해 재이용 활성화를 지원<br />
|
||||
<ul className="list-inside text-[#767676] text-sm ml-4 mt-2">
|
||||
<li>– 재이용 설비를 기준으로 2025년까지 싱가포르 수자원공사의 2가지 금융상품 제공<br/>
|
||||
<ul className="list-inside text-[#767676] ml-4 mt-2">
|
||||
<li>1. Water Efficiency Fund</li>
|
||||
<li>2. Industrial Water Solutions Demonstration Fund</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li className="mt-2">– 2025년 기준 지원 한도 상향 → 물 재이용 활성 도모<br/>
|
||||
<ul className="list-inside text-[#767676] ml-4 mt-2">
|
||||
<li>투입비용 50% → 75% 지원 / WSDP: $400만 → $500만</li>
|
||||
<li>물 효율이 높은 장비를 도입하는 데에도 최대 $300만 지원 가능</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>싱가포르는 천연 수자원이 많지 않아, 말레이시아 조호르와 1962년 맺은 협약으로 물을 수입해왔으며, 재이용수를 늘리기 위한 노력을 계속하고 있음<br/>
|
||||
<ul className="list-inside text-[#767676] text-sm ml-4 mt-2">
|
||||
<li>- 싱가포르는 국토 내에 호수와 하천이적고 집수공간도 적어 빗물로 확보할 수 있는 수자원량은 전체 물 수요의 20%에 불과함</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>싱가포르는 도심에 저수지를 건설하 고 건물에 빗물이용시설을 건설하여 빗물을 재이용하고 있음</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>싱가포르는 2002년부터 하수재이용수인 NEWater를 개발, 비음 용수와 간접음용수로 활용하고 있음<br/>
|
||||
<ul className="list-inside text-[#767676] text-sm/6 ml-4 mt-2">
|
||||
<li>- 재이용수를 NEWater라는 이름으로 명명하고 NEWater에 대한 신뢰 확보를 위해 NEWater 방문자 센터도 건설함</li>
|
||||
<li>- 비음용수로 많이 사용하며 건기에는 NEWater를 저수지에 추가하여 원수와 섞음. 원수는 상수도에서 처리 한 후 가정에 수돗물로 공급함</li>
|
||||
<li>- NEWater는 전용 파이프를 통해 주로 웨이퍼 제조 공장, 산업단지, 상업용 건물에서 산업 및 냉각수로 사용. 제일 많이 사용하는 곳은 웨이퍼 제조 공장</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>2024년 4월 기준 1㎥ 당 공업용수 가격은 $1.66이며, Non-domestic 기준 NEWater 가격은 $2.41</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>공업용수 가격 $1.66은 수도요금(water tariff)과 수자원세(waterborne tax)로 구분됨. $1.66에는 $1만큼의 수자원세가 포함됨</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>2023년 싱가포르는 산업용수 수요가 높은 산업에 대해 물 재 이용을 의무화했으며, 정부는 기금을 통해 물 재이용을 지원함</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,216 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/globalCase/taiwan/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="[대만] 국가 주도의 공업용수 공급"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "해외 재이용 사례" },
|
||||
{ label: "대만", href: "/reuse/business/globalCase/taiwan" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/globalCase/taiwan_01.png", alt: "대만 사례 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 재생 수자원 개발법 제정(2015년제정, 2022개정) */}
|
||||
<section className="mt-8">
|
||||
<SectionTitle>재생 수자원 개발법 제정(2015년제정, 2022개정)</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-2 text-base text-gray-600">
|
||||
<li>
|
||||
• 목적: 수자원 지속 가능성 향상, 재생 수자원 활용을 통해 물부족 문제 해결
|
||||
</li>
|
||||
<li>
|
||||
• 내용: 재생 수자원 정의·분류, 이용 의무, 이용 제한, 수질 기준 등
|
||||
</li>
|
||||
</ul>
|
||||
<div className="rounded-sm bg-[#e9f0f1] p-8 mt-5">
|
||||
<ul className="space-y-1 text-2xl text-[#1A1A1A]">
|
||||
<li>
|
||||
– 제4조. 물 소비 계획에 따른 재이용수 이용 관련 내용 (일정 비율 재이용수 사용 규정)
|
||||
</li>
|
||||
<li>
|
||||
– 제5조. 재이용수의 운영자, 사용자 관련 내용 (지자체에서 하수 무상공급 제공 가능)
|
||||
</li>
|
||||
<li>
|
||||
– 제7조. 재이용수 이용 범위, 이용량 재이용수 수질 기준 내용 (식품, 제약산업 사용 불가)
|
||||
</li>
|
||||
<li>
|
||||
– 제8조. 재이용수 사업 주체, 책임 등에 관한 내용 (광역인 경우, 정부가 지자체 지정)
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 대만정부 하수재이용 사업 시행 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>대만정부 하수재이용 사업 시행</SectionTitle>
|
||||
<div className="px-4">
|
||||
<ul className="mt-6 space-y-4 text-base text-gray-600">
|
||||
<li>
|
||||
• 대만은 물 재이용에 적극적이며 국가주도로 하수재이용 사업 시행(안핑)<br />
|
||||
<ul className="list-inside text-[#767676] ml-4 mt-2">
|
||||
<li>– 1,618억의 사업비 중 92% 국비, 지방비 8% 투입</li>
|
||||
<li>– 재이용수는 24km 관로로 타이난 TSMC로 공급</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
• 안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능
|
||||
</li>
|
||||
<li>
|
||||
• 공급단가는 수질과 운영조건에 따라 다르게 책정
|
||||
</li>
|
||||
</ul>
|
||||
{/* 테이블 (2단 병렬: 좌/우 데이터) */}
|
||||
<div className="mt-3">
|
||||
<div className="overflow-x-auto">
|
||||
<p className="text-center text-2xl py-2">〈대만 재이용 시설별 재이용수 공급단지〉</p>
|
||||
<table className="min-w-[900px] w-full text-center text-sm">
|
||||
<thead className="border">
|
||||
<tr>
|
||||
<th className="bg-[#C2CAD2] px-4 py-3 text-black">사업</th>
|
||||
<th className="bg-sky-600 px-4 py-3 text-white">가격(NTD/㎥)</th>
|
||||
<th className="bg-green-500 px-4 py-3 text-white">가격(원/㎥)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody className="bg-white text-[#4F4F4F] border border-t-0">
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Fongshan(가오슝)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">23.39</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">1,038원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Linhai(가오슝)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">19.28</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">858원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Anping(타이난)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">38.28</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">1,699원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Yongkang(타이난)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">23.67</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">1,050원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Rende(타이난)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">28.26</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">1,254원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Shuinan(타이중)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">15.00</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">668원</td>
|
||||
</tr>
|
||||
<tr className="border-t">
|
||||
<td className="px-4 py-3 bg-white">Quaitou(가오슝)</td>
|
||||
<td className="px-4 py-3 bg-[#EAF5F5]">35.44</td>
|
||||
<td className="px-4 py-3 bg-[#E7F4E2]">1,573원</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<ul className="mt-6 space-y-4 pl-4 text-base text-gray-600">
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>대만은 풍부한 강수량에도, 지형적 특성으로 인해 수자원 확보에 어려움을 겪고 있으며, 대만 남부지역의 경우 우기와 건기 사이의 강수량 차이가 큼</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>대만은 최근 심각한 가뭄이 여러 해에 걸쳐서 발생하고 있으며, 강수량 감소와 함께 대만 정부 주도하에 반도체 산업을 국가 핵심 산업으로 선정하고 반도체 산업 육성 계획을 세움. 그 결과, 2023년 파운드리 시장의 68.3%를 차지하는 반도체 강국으로 성장했으며 이로 인한 공업용수 수요가 크게 증가</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>반도체 산업은 물 소비가 많아, 안정적인 용수공급이 매우 중요한데 현재 19개의 반도체 공장이 물이 부족한 대만 남부지역에 위치함. 2021년 가뭄 발생 당시, 산업단지 기업들도 용수 공급에 어려움을 겪음. 이는 대만 남부 지역의 물 부족, 물 분배 등에 대한 문제를 야기함</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>대만 정부는 산업단지 기업들에게 2023년부터 물에 대한 추가요금 부과를 시작했으며, 농업용수에 대해서는 강력한 억제정책을 시행함. 안정적인 용수 공급을 위해서 정부와 기업에서 자체적으로 재이용수 시설에 대한 투자가 이루어지고 있음.</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>대만은 2004년 기후변화로 인한 물 부족 상황을 해결하기 위해 수자원 보호 전략을 승인하고, 2015년 대만 환경부는 재생 수자원 개발법을 발표, 2022년에는 한 차례 개정</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<ul className="list-disc list-outside pl-6 space-y-1 leading-relaxed">
|
||||
<li>대만은 물 소비량이 많은 산업(high water consuming industries)과 하수처리장(municipal wastewater treatment plants), 두 곳을 재이용수의 주요 공급원으로 계획</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-10 w-[4px] bg-sky-500" />
|
||||
<h3 className="text-[38px] font-bold leading-none text-neutral-900">
|
||||
{children}
|
||||
</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BlockTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="inline-block h-[14px] w-[24px] rounded-full bg-gradient-to-r from-sky-300 to-lime-300" />
|
||||
<h4 className="text-[28px] font-bold text-[#2B2B2B]">{children}</h4>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type SupplyRow = {
|
||||
facility: string;
|
||||
usage: string;
|
||||
amount: string;
|
||||
};
|
||||
|
||||
function SupplyTable({ rows }: { rows: SupplyRow[] }) {
|
||||
return (
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full min-w-[860px] border border-[#D8DDE3] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-[#D8DDE3] bg-[#14A9D9] px-4 py-5 text-[24px] font-bold text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#40B5A5] px-4 py-5 text-[24px] font-bold text-white">
|
||||
이용용도
|
||||
</th>
|
||||
<th className="border border-[#D8DDE3] bg-[#73BF4B] px-4 py-5 text-[24px] font-bold text-white">
|
||||
공급량(㎥)
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row, index) => (
|
||||
<tr key={`${row.facility}-${index}`}>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-4 text-[22px] text-[#4F4F4F]">
|
||||
{row.facility}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-4 text-[22px] text-[#4F4F4F]">
|
||||
{row.usage}
|
||||
</td>
|
||||
<td className="border border-[#D8DDE3] bg-white px-4 py-4 text-[22px] text-[#4F4F4F]">
|
||||
{row.amount}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function InfoList({
|
||||
projectName,
|
||||
purpose,
|
||||
totalCost,
|
||||
treatment,
|
||||
}: {
|
||||
projectName: string;
|
||||
purpose: string;
|
||||
totalCost: string;
|
||||
treatment: string;
|
||||
}) {
|
||||
return (
|
||||
<div className="mt-10 space-y-10">
|
||||
<div>
|
||||
<BlockTitle>사업명</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[22px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{projectName}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업목적</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[22px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>{purpose}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업 주요 내용</BlockTitle>
|
||||
<ul className="mt-4 list-disc pl-8 text-[22px] leading-[1.8] text-[#4F4F4F]">
|
||||
<li>총사업비 : {totalCost}</li>
|
||||
<li>처리공법 : {treatment}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<BlockTitle>사업량</BlockTitle>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="중수 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "중수 재이용 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 안산시 */}
|
||||
<section id="ansan" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 안산시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="안산시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="81억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "경기테크노파크",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "40㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "올림픽수영장",
|
||||
usage: "청소·화장실/세척·살수용수",
|
||||
amount: "35㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "와스타디움",
|
||||
usage: "청소·화장실/조경용수",
|
||||
amount: "90㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "호수실내수영장",
|
||||
usage: "청소·화장실/조경/세척·살수용수",
|
||||
amount: "50㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "한양대학교 에리카캠퍼스",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "370㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
{/* 용인시 */}
|
||||
<section id="yongin" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 용인시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="용인시 중수도 설치사업"
|
||||
purpose="물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용"
|
||||
totalCost="40억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "여성회관",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "35㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "수지아르피아",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "35㎥/일",
|
||||
},
|
||||
{
|
||||
facility: "지상작전사령부",
|
||||
usage: "청소·화장실/조경/세척·살수용수",
|
||||
amount: "53㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-20" />
|
||||
|
||||
{/* 평택시 */}
|
||||
<section id="pyeongtaek" className="scroll-mt-[120px] pb-[200px]">
|
||||
<SectionTitle>경기도 평택시</SectionTitle>
|
||||
|
||||
<InfoList
|
||||
projectName="평택시 중수도 설치사업"
|
||||
purpose="수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현"
|
||||
totalCost="21억원"
|
||||
treatment="MBR공법"
|
||||
/>
|
||||
|
||||
<SupplyTable
|
||||
rows={[
|
||||
{
|
||||
facility: "평택대학교",
|
||||
usage: "청소·화장실용수",
|
||||
amount: "50㎥/일",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Lnb from "@/components/Lnb";
|
||||
// import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
export default function BusinessLayout({ children }: { children: ReactNode }) {
|
||||
export default function ExampleLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex gap-0">
|
||||
<div className="relative">
|
||||
<Lnb />
|
||||
<main className="flex-1 px-[100px] pt-[80px]">{children}</main>
|
||||
<div className="flex max-w-[1400px] gap-[56px] py-[50px]">
|
||||
<main className="min-w-0 flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* 우측 메뉴 영역 */}
|
||||
{/* <BusinessSubNav /> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
|
||||
import RainExampleSection from "./_components/RainExampleSection";
|
||||
import GreywaterExampleSection from "./_components/GreywaterExampleSection";
|
||||
import SewageExampleSection from "./_components/SewageExampleSection";
|
||||
import GlobalCaseSection from "./_components/GlobalCaseSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "example-rain",
|
||||
label: "빗물 재이용 사례",
|
||||
sections: [
|
||||
{ id: "example-rain-suwon", label: "경기도 수원시" },
|
||||
{ id: "example-rain-jeju", label: "제주특별자치도" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-greywater",
|
||||
label: "중수 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-greywater-ansan", label: "경기도 안산시" },
|
||||
{ id: "example-greywater-yongin", label: "경기도 용인시" },
|
||||
{ id: "example-greywater-pyeongtaek", label: "경기도 평택시" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-sewage",
|
||||
label: "하수처리 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-sewage-guri", label: "경기도 구리시" },
|
||||
{ id: "example-sewage-bucheon", label: "경기도 부천시" },
|
||||
{ id: "example-sewage-suwon", label: "경기도 수원시" },
|
||||
{ id: "example-sewage-paju", label: "경기도 파주시" },
|
||||
{ id: "example-sewage-pohang", label: "경기도 포항시" },
|
||||
{ id: "example-sewage-ulsan", label: "울산광역시" },
|
||||
{ id: "example-sewage-yeosu", label: "전라남도 여수시" },
|
||||
{ id: "example-sewage-boryeong", label: "충청남도 보령시" },
|
||||
{ id: "example-sewage-asan", label: "충청남도 아산시" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "example-global",
|
||||
label: "해외 재이용 사례",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "example-global-overview", label: "사례 개요" },
|
||||
{ id: "example-global-taiwan", label: "대만" },
|
||||
{ id: "example-global-singapore", label: "싱가포르" },
|
||||
{ id: "example-global-australia", label: "호주" },
|
||||
{ id: "example-global-eu", label: "EU" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
// function Hr({ className = "" }: { className?: string }) {
|
||||
// return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
// }
|
||||
|
||||
export default function ExamplePage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 사례" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
<RainExampleSection />
|
||||
<GreywaterExampleSection />
|
||||
<SewageExampleSection />
|
||||
<GlobalCaseSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/ImageSlider";
|
||||
import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function RainPage() {
|
||||
return (
|
||||
<div className="relative bg-white">
|
||||
{/* ✅ 추가 */}
|
||||
<BusinessSubNav />
|
||||
|
||||
<SubPageHeader
|
||||
title="빗물 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 사례", href: "/example/rain" },
|
||||
{ label: "빗물 재이용 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
<section id="suwon" className="scroll-mt-[120px]">
|
||||
<SectionTitle>경기도 수원시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/rain_01.png", alt: "수원시 사례 1" },
|
||||
{ src: "/images/landing/business/rain/rain_02.png", alt: "수원시 사례 2" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
<section id="jeju" className="scroll-mt-[120px] pb-[120px]">
|
||||
<SectionTitle>제주특별자치도</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/jeju_01.png", alt: "제주 사례 1" },
|
||||
{ src: "/images/landing/business/rain/jeju_02.png", alt: "제주 사례 2" },
|
||||
]}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/rain/suwon/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="경기도 수원시 빗물이용과 중수도"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "빗물 재이용 사례" },
|
||||
{ label: "수원시", href: "/reuse/business/rain/suwon" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/rain/rain_01.png", alt: "수원시 하수 재이용 1" },
|
||||
{ src: "/images/landing/business/rain/rain_02.png", alt: "수원시 하수 재이용 2" },
|
||||
{ src: "/images/landing/business/rain/rain_03.png", alt: "수원시 하수 재이용 3" },
|
||||
{ src: "/images/landing/business/rain/rain_04.png", alt: "수원시 하수 재이용 4" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
경기도 수원시 빗물이용과 중수도
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 방식 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>사업 방식</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기 도시 열섬효과 및 도로 비산먼지 저감<br/>
|
||||
※ 시설 가동중 도로 노면 온도의 7.2 ~ 26.3℃ 감소 효과 확인(‘21.7월, 수원시청).<br/>
|
||||
다만, 가뭄기나 강우량이 적을 경우 빗물을 원활히 공급하기 다소 곤란<br/><br/>
|
||||
- (중수도) 수원시청, 장안구청, 월드컵경기장에 빗물 연계 중수도를 화장실·조경용수 등으로 사용하여 수도요금 절감(연 7백~30백만원)
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/bolyeong/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="충남보령 하수 재이용"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "충청남도 보령시", href: "/reuse/business/sewage/bolyeong" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/bolyeong_01.png", alt: "보령시 하수 재이용 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
충남 보령 하수재이용<br/>
|
||||
: 보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 방식 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 방식</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
2017년 한국환경공단과 사업대행 위 · 수탁 협약 체결
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
처리공정 : 전처리 UF + 후처리 RO<br/>
|
||||
시설용량 : 1만m³/일<br/>
|
||||
관로 : 8.7km<br/>
|
||||
사업비 : 315억원<br/>
|
||||
위치: 충청남도 보령시 대청방조제로 322 일원
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU(환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕으로 추진 되어 경제성보다 공익성을 바탕으로<br/>
|
||||
국가전력 기반시설의 안정적 운영 도모
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/cheongju/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="청주시 하수 재이용 시설"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "청주시", href: "/reuse/business/sewage/cheongju" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/cheongju_01.png", alt: "청주시 하수 재이용 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
청주시 하수 재이용 시설
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
청주시 하수처리수 재이용 민간투자사업(BTO)<br/>
|
||||
공급량 : 35,000㎥/일<br/>
|
||||
공급관로 : 8.3㎞<br/>
|
||||
사업비 : 622억원<br/>
|
||||
수요처 : SK하이닉스 등 일반산업단지<br/>
|
||||
지역 (위치) : 충청북도 청주시 흥덕구 가락리 135-9번지 일원<br/>
|
||||
처리용량 : 35.000(m³/일)<br/>
|
||||
적용기술 : 분리막 (UF : 전처리), (RO : 주처리)
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/guri/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="구리시 도로자동청소시스템"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "구리시", href: "/reuse/business/sewage/guri" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/guri_01.png", alt: "구리시 도로자동청소시스템 1" },
|
||||
{ src: "/images/landing/business/sewage/guri_02.png", alt: "구리시 도로자동청소시스템 2" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
구리시 도로자동청소시스템 설치사업
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업목적 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : 61억원<br/>
|
||||
위 치 : 구리시 경춘로 및 갈매중앙로 일대 등<br/>
|
||||
관 로 : 5.8km<br/>
|
||||
공급계획<br/>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 공급계획 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>공급계획</SectionTitle>
|
||||
{/* 데이터 테이블 */}
|
||||
<div className="overflow-x-auto mt-8">
|
||||
<table className="w-full border border-[#E3E7EA] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bg-[#01ACE5] text-white font-semibold py-4 border border-gray-300">
|
||||
시설명
|
||||
</th>
|
||||
<th className="bg-[#33B5C8] text-white font-semibold py-4 border border-gray-300">
|
||||
재이용수 용도
|
||||
</th>
|
||||
<th className="bg-[#55BCA2] text-white font-semibold py-4 border border-gray-300">
|
||||
공급량(㎥/일)
|
||||
</th>
|
||||
<th className="bg-[#6DBB4F] text-white font-semibold py-4 border border-gray-300">
|
||||
원수
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
className="bg-white py-6 border border-gray-300 text-gray-700"
|
||||
>
|
||||
도로자동청소시스템
|
||||
</td>
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
세척살수용수
|
||||
</td>
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
178
|
||||
</td>
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
구리 공공하수처리장 재이용수
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/hwanggu/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="수원시 황구지천 하천유지용수"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "수원시 황구지천", href: "/reuse/business/sewage/hwanggu" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/hwanggu_01.png", alt: "수원시 황구지천 하수 재이용 1" },
|
||||
{ src: "/images/landing/business/sewage/hwanggu_02.png", alt: "수원시 황구지천 하수 재이용 2" },
|
||||
{ src: "/images/landing/business/sewage/hwanggu_03.png", alt: "수원시 황구지천 하수 재이용 3" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
수원시 황수원시 황구지천 하수처리수 재이용사업
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업목적 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
황구지천 공공하수처리시설 하수처리수 재이용사업으로 향후 호매실천 및 금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : 110억원<br/>
|
||||
위 치 : 수원시 권선구 호매실천, 금곡천<br/>
|
||||
관 로 : 3.9km<br/>
|
||||
공 급 량 : 25,000㎥/일(하수처리시설 용량: 45,000㎥/일)<br/>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 공급계획 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>공급계획</SectionTitle>
|
||||
{/* 데이터 테이블 */}
|
||||
<div className="overflow-x-auto mt-8">
|
||||
<table className="w-full border border-[#E3E7EA] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
{/* 구분 */}
|
||||
<th className="w-1/4 bg-[#E3E7EA] text-black font-semibold py-4 border border-gray-300">
|
||||
구분
|
||||
</th>
|
||||
|
||||
{/* 공급량 */}
|
||||
<th className="w-2/4 bg-[#1FA3C6] text-white font-semibold py-4 border border-gray-300">
|
||||
공급량
|
||||
</th>
|
||||
|
||||
{/* 비고 */}
|
||||
<th className="w-2/4 bg-[#72B84F] text-white font-semibold py-4 border border-gray-300">
|
||||
비고
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
{/* 좌측 병합 */}
|
||||
<td
|
||||
rowSpan={3}
|
||||
className="bg-white font-medium text-gray-700 border border-gray-300"
|
||||
>
|
||||
하천 등 유지용수
|
||||
</td>
|
||||
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700 text-lg font-medium">
|
||||
12,500m³/일
|
||||
</td>
|
||||
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
호매실천 공급
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700 text-lg font-medium">
|
||||
6,250m³/일
|
||||
</td>
|
||||
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
금곡천1 공급
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700 text-lg font-medium">
|
||||
6,250m³/일
|
||||
</td>
|
||||
|
||||
<td className="bg-white py-6 border border-gray-300 text-gray-700">
|
||||
금곡천2 공급
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
export default function SewagePage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="하수 재이용 사례"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 보령 */}
|
||||
<section id="bolyeong" className="scroll-mt-[120px]">
|
||||
<SectionTitle>충청남도 보령시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/bolyeong_01.png",
|
||||
alt: "보령시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
충남 보령 하수재이용
|
||||
<br />: 보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해
|
||||
보령하수처리시설의 하수처리수 재이용 사업 추진
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 방식</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
2017년 한국환경공단과 사업대행 위 · 수탁 협약 체결
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
처리공정 : 전처리 UF + 후처리 RO
|
||||
<br />
|
||||
시설용량 : 1만m³/일
|
||||
<br />
|
||||
관로 : 8.7km
|
||||
<br />
|
||||
사업비 : 315억원
|
||||
<br />
|
||||
위치: 충청남도 보령시 대청방조제로 322 일원
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권
|
||||
가뭄 시 지역사회 기여 차원에서 기관간 MOU(환경부, 충청남도, 보령시,
|
||||
한국중부발전㈜)를 바탕으로 추진 되어 경제성보다 공익성을 바탕으로
|
||||
<br />
|
||||
국가전력 기반시설의 안정적 운영 도모
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 청주 */}
|
||||
<section id="cheongju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>청주시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/cheongju_01.png",
|
||||
alt: "청주시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
청주시 하수 재이용 시설
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
청주시 하수처리수 재이용 민간투자사업(BTO)
|
||||
<br />
|
||||
공급량 : 35,000㎥/일
|
||||
<br />
|
||||
공급관로 : 8.3㎞
|
||||
<br />
|
||||
사업비 : 622억원
|
||||
<br />
|
||||
수요처 : SK하이닉스 등 일반산업단지
|
||||
<br />
|
||||
지역 (위치) : 충청북도 청주시 흥덕구 가락리 135-9번지 일원
|
||||
<br />
|
||||
처리용량 : 35.000(m³/일)
|
||||
<br />
|
||||
적용기술 : 분리막 (UF : 전처리), (RO : 주처리)
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 구리 */}
|
||||
<section id="guri" className="scroll-mt-[120px]">
|
||||
<SectionTitle>구리시 도로자동청소시스템</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_01.png",
|
||||
alt: "구리시 도로자동청소시스템 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/guri_02.png",
|
||||
alt: "구리시 도로자동청소시스템 2",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
구리시 도로자동청소시스템 설치사업
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생
|
||||
재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염
|
||||
취약성 완화
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : 61억원
|
||||
<br />
|
||||
위 치 : 구리시 경춘로 및 갈매중앙로 일대 등
|
||||
<br />
|
||||
관 로 : 5.8km
|
||||
<br />
|
||||
공급계획
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>공급계획</SectionTitle>
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full border border-[#E3E7EA] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border border-gray-300 bg-[#01ACE5] py-4 font-semibold text-white">
|
||||
시설명
|
||||
</th>
|
||||
<th className="border border-gray-300 bg-[#33B5C8] py-4 font-semibold text-white">
|
||||
재이용수 용도
|
||||
</th>
|
||||
<th className="border border-gray-300 bg-[#55BCA2] py-4 font-semibold text-white">
|
||||
공급량(㎥/일)
|
||||
</th>
|
||||
<th className="border border-gray-300 bg-[#6DBB4F] py-4 font-semibold text-white">
|
||||
원수
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
도로자동청소시스템
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
세척살수용수
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
178
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
구리 공공하수처리장 재이용수
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 황구지천 */}
|
||||
<section id="hwanggu" className="scroll-mt-[120px]">
|
||||
<SectionTitle>수원시 황구지천</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_01.png",
|
||||
alt: "수원시 황구지천 하수 재이용 1",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_02.png",
|
||||
alt: "수원시 황구지천 하수 재이용 2",
|
||||
},
|
||||
{
|
||||
src: "/images/landing/business/sewage/hwanggu_03.png",
|
||||
alt: "수원시 황구지천 하수 재이용 3",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
수원시 황수원시 황구지천 하수처리수 재이용사업
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
황구지천 공공하수처리시설 하수처리수 재이용사업으로 향후 호매실천 및
|
||||
금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : 110억원
|
||||
<br />
|
||||
위 치 : 수원시 권선구 호매실천, 금곡천
|
||||
<br />
|
||||
관 로 : 3.9km
|
||||
<br />
|
||||
공 급 량 : 25,000㎥/일(하수처리시설 용량: 45,000㎥/일)
|
||||
<br />
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>공급계획</SectionTitle>
|
||||
<div className="mt-8 overflow-x-auto">
|
||||
<table className="w-full border border-[#E3E7EA] text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="w-1/4 border border-gray-300 bg-[#E3E7EA] py-4 font-semibold text-black">
|
||||
구분
|
||||
</th>
|
||||
<th className="w-2/4 border border-gray-300 bg-[#1FA3C6] py-4 font-semibold text-white">
|
||||
공급량
|
||||
</th>
|
||||
<th className="w-2/4 border border-gray-300 bg-[#72B84F] py-4 font-semibold text-white">
|
||||
비고
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
rowSpan={3}
|
||||
className="border border-gray-300 bg-white font-medium text-gray-700"
|
||||
>
|
||||
하천 등 유지용수
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-lg font-medium text-gray-700">
|
||||
12,500m³/일
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
호매실천 공급
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 bg-white py-6 text-lg font-medium text-gray-700">
|
||||
6,250m³/일
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
금곡천1 공급
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td className="border border-gray-300 bg-white py-6 text-lg font-medium text-gray-700">
|
||||
6,250m³/일
|
||||
</td>
|
||||
<td className="border border-gray-300 bg-white py-6 text-gray-700">
|
||||
금곡천2 공급
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 파주 */}
|
||||
<section id="paju" className="scroll-mt-[120px]">
|
||||
<SectionTitle>파주시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/paju_01.png",
|
||||
alt: "파주시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
경기도 파주시 하수재이용
|
||||
<br />: 운정·금촌하수처리시설의 하수처리수를 막공법(U/F, R/O)으로
|
||||
재처리하여 파주 LCD 산업단지내 3개 업체*에 공업용수 공급
|
||||
<br />
|
||||
*LG디스플레이(P10공장), 희성전자, 엔비텍 등
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 배경 및 목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
처리공정 : 전처리 UF + 후처리 RO
|
||||
<br />
|
||||
공급량 : 4만m³/일
|
||||
<br />
|
||||
관로 : 13.6km
|
||||
<br />
|
||||
사업비 : 651억원
|
||||
<br />
|
||||
위치 : 경기도 파주시 금촌동 714번지 일원
|
||||
<br />
|
||||
시설용량 : 41,20m³/일
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section>
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<Hr className="my-16" />
|
||||
|
||||
{/* 여수 */}
|
||||
<section id="yeosu" className="scroll-mt-[120px] pb-[200px]">
|
||||
<SectionTitle>여수시</SectionTitle>
|
||||
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{
|
||||
src: "/images/landing/business/sewage/yeosu_01.png",
|
||||
alt: "여수시 하수 재이용 1",
|
||||
},
|
||||
]}
|
||||
/>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
여수시 하수처리수 재이용 민간 투자사업(BTO)
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
수요자가 원하는 양질의 공업용수를 저렴하게 곰급하여 한정된
|
||||
수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한
|
||||
오염총량 저감 및 방류수역 수질개선
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : GS칼텍스, YNCC(주) 등 750억원
|
||||
<br />
|
||||
시행사 : 여수그린허브(주)
|
||||
<br />
|
||||
운영사 : 테크로스
|
||||
<br />
|
||||
위치: 여수시 신월로 2844일원 (여수 하수처리시설 부지 내)
|
||||
<br />
|
||||
처리공법 : 전처리시설(MF) + 주처리시설(RO)
|
||||
<br />
|
||||
시설용량 및 공급량 : 5만m³/일
|
||||
<br />
|
||||
관로 : 18.6km
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/paju/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="경기도 파주시 하수 재이용"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "파주시", href: "/reuse/business/sewage/paju" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/paju_01.png", alt: "파주시 하수 재이용 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
경기도 파주시 하수재이용<br/>
|
||||
: 운정·금촌하수처리시설의 하수처리수를 막공법(U/F, R/O)으로 재처리하여 파주 LCD 산업단지내 3개 업체*에 공업용수 공급<br/>
|
||||
*LG디스플레이(P10공장), 희성전자, 엔비텍 등
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 배경 및 목적 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 배경 및 목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
처리공정 : 전처리 UF + 후처리 RO<br/>
|
||||
공급량 : 4만m³/일<br/>
|
||||
관로 : 13.6km<br/>
|
||||
사업비 : 651억원<br/>
|
||||
위치 : 경기도 파주시 금촌동 714번지 일원<br/>
|
||||
시설용량 : 41,20m³/일
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 특이사항 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>특이사항</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/business/sewage/yeosu/page.tsx
|
||||
"use client";
|
||||
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import CaseImageSlider from "@/components/CaseImageSlider";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-2xl font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
// type ImgCardProps = {
|
||||
// src: string;
|
||||
// alt: string;
|
||||
// };
|
||||
|
||||
// function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// return (
|
||||
// <div className="w-full overflow-x-auto">
|
||||
// <img
|
||||
// src={src}
|
||||
// alt={alt}
|
||||
// className="block mx-auto max-w-none"
|
||||
// />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="여수시 하수처리수 재이용 민간투자사업(BTO)"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 사례" },
|
||||
{ label: "하수 재이용 사례" },
|
||||
{ label: "여수시", href: "/reuse/business/sewage/yeosu" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 슬라이드 이미지 영역 */}
|
||||
<CaseImageSlider
|
||||
className="mt-8"
|
||||
maxWidthClassName="max-w-[760px]"
|
||||
slides={[
|
||||
{ src: "/images/landing/business/sewage/yeosu_01.png", alt: "여수시 하수 재이용 1" },
|
||||
]}
|
||||
/>
|
||||
|
||||
{/* 사업명 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업명</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
여수시 하수처리수 재이용 민간 투자사업(BTO)
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 사업 목적 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>사업 목적</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
수요자가 원하는 양질의 공업용수를 저렴하게 곰급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>사업 주요 내용</SectionTitle>
|
||||
<p className="mt-8 text-base text-gray-600">
|
||||
총사업비 : GS칼텍스, YNCC(주) 등 750억원<br/>
|
||||
시행사 : 여수그린허브(주)<br/>
|
||||
운영사 : 테크로스<br/>
|
||||
위치: 여수시 신월로 2844일원 (여수 하수처리시설 부지 내)<br/>
|
||||
처리공법 : 전처리시설(MF) + 주처리시설(RO)<br/>
|
||||
시설용량 및 공급량 : 5만m³/일<br/>
|
||||
관로 : 18.6km
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import lawPlan from "@/public/images/landing/intro/law_timeline.png";
|
||||
import planPerformance from "@/public/images/landing/intro/performance_left.png";
|
||||
import domesticChange from "@/public/images/landing/intro/domestic_textbox.png";
|
||||
import policyHistory from "@/public/images/landing/intro/policy_timeline.png";
|
||||
import lawRelation from "@/public/images/landing/intro/rel_note.png";
|
||||
import domesticLinkage from "@/public/images/landing/intro/linkage.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function LawIntroSection() {
|
||||
const title = "물 재이용 법";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="law-plan"
|
||||
title="법률 제·개정 및 기본계획 수립"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={lawPlan} alt="법률 제·개정 및 기본계획 수립" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="plan-performance"
|
||||
title="제2차 계획 대비 실적 현황"
|
||||
>
|
||||
<ImgCard src={planPerformance} alt="제2차 계획 대비 실적 현황" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell id="domestic-change" title="우리나라의 수자원 현황 및 물이용 여건">
|
||||
<ImgCard src={domesticChange} alt="우리나라의 수자원 현황 및 물이용 여건" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="policy-history"
|
||||
title="물 재이용 정책 변화 추이"
|
||||
>
|
||||
<ImgCard src={policyHistory} alt="물 재이용 정책 변화 추이" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="law-relation"
|
||||
title="타법령과의 관계 검토"
|
||||
>
|
||||
<ImgCard src={lawRelation} alt="타법령과의 관계 검토" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="domestic-linkage"
|
||||
title="주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요"
|
||||
>
|
||||
<ImgCard src={domesticLinkage} alt="주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import rainSummary from "@/public/images/landing/intro/rainreuse_intro.png";
|
||||
import rainDuty from "@/public/images/landing/intro/rainreuse_mandatory.png";
|
||||
import rainDesign from "@/public/images/landing/intro/rainwater_design.png";
|
||||
import rainStandard from "@/public/images/landing/intro/rainwater_standards.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RainIntroSection() {
|
||||
const title = "빗물 이용시설";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell id="rain-intro" title="빗물 재이용 소개" className="mt-10">
|
||||
<ImgCard src={rainSummary} alt="빗물 재이용 소개" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="rain-mandatory"
|
||||
title="빗물이용시설 설치 관리 법적 의무 대상"
|
||||
>
|
||||
<ImgCard src={rainDuty} alt="빗물이용시설 설치 관리 법적 의무 대상" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell id="rain-design" title="빗물이용시설 설계 방안">
|
||||
<ImgCard src={rainDesign} alt="빗물이용시설 설계 방안" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="rain-standards"
|
||||
title="빗물이용시설 시설기준 및 관리기준"
|
||||
>
|
||||
<ImgCard src={rainStandard} alt="빗물이용시설 시설기준 및 관리기준" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import greywaterIntro from "@/public/images/landing/intro/greywater_intro.png";
|
||||
import greywaterMandatory from "@/public/images/landing/intro/greywater_mandatory.png";
|
||||
import greywaterStandard from "@/public/images/landing/intro/greywater_standard.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ReuseWaterIntroSection() {
|
||||
const title = "중수도";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="greywater-intro"
|
||||
title="중수 재이용 소개"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={greywaterIntro} alt="중수 재이용 소개" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="greywater-mandatory"
|
||||
title="중수도 설치 의무 대상"
|
||||
>
|
||||
<ImgCard src={greywaterMandatory} alt="중수도 설치 의무 대상" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="greywater-standard"
|
||||
title="중수도 시설기준 및 관리기준"
|
||||
>
|
||||
<ImgCard src={greywaterStandard} alt="중수도 시설기준 및 관리기준" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
|
||||
import treatedwaterIntro from "@/public/images/landing/intro/treatedwater_intro.png";
|
||||
import treatedwaterMandatory from "@/public/images/landing/intro/treatedwater_mandatory.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[120px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TreatedIntroSection() {
|
||||
const title = "하수처리수";
|
||||
|
||||
return (
|
||||
<div className="mt-[120px] bg-white">
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold leading-none tracking-tight text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute bottom-[-4px] left-0 z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-10">
|
||||
<SectionShell
|
||||
id="treatedwater-intro"
|
||||
title="하수처리수 소개"
|
||||
className="mt-10"
|
||||
>
|
||||
<ImgCard src={treatedwaterIntro} alt="하수처리수" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="treatedwater-mandatory"
|
||||
title="하수처리수 재이용시설 의무 대상"
|
||||
>
|
||||
<ImgCard
|
||||
src={treatedwaterMandatory}
|
||||
alt="하수처리수 재이용시설 의무 대상"
|
||||
/>
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/intro/greywater/page.tsx
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import greywaterIntro from "@/public/images/landing/intro/greywater_intro.png";
|
||||
import greywaterMandatory from "@/public/images/landing/intro/greywater_mandatory.png";
|
||||
import greywaterStandard from "@/public/images/landing/intro/greywater_standard.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type ImgCardProps = {
|
||||
src: StaticImageData;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
function ImgCard({ src, alt }: ImgCardProps) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="중수도"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 소개", href: "/reuse/intro/law" },
|
||||
{ label: "중수도" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 중수 재이용 소개 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>중수 재이용 소개</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterIntro} alt="중수 재이용 소개" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 중수도 설치 의무 대상 */}
|
||||
<section>
|
||||
<SectionTitle>중수도 설치 의무 대상</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterMandatory} alt="중수도 설치 의무 대상 설명" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 중수도 시설기준 및 관리기준 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>중수도 시설기준 및 관리기준</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterStandard} alt="중수도 시설기준 및 관리기준 설명" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// apps/dm/app/(landing)/reuse/intro/law/page.tsx
|
||||
// DM 물 재이용 소개(물 재이용 법) 페이지
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
// import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
import lawTimeline from "@/public/images/landing/intro/law_timeline.png";
|
||||
import performanceLeft from "@/public/images/landing/intro/performance_left.png";
|
||||
import domesticTextbox from "@/public/images/landing/intro/domestic_textbox.png";
|
||||
@@ -38,93 +40,124 @@ function ImgCard({ src, alt }: ImgCardProps) {
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
priority={false}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type SectionShellProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function SectionShell({ id, title, children, className = "" }: SectionShellProps) {
|
||||
return (
|
||||
<section
|
||||
id={id}
|
||||
className={[
|
||||
"scroll-mt-[140px]",
|
||||
className,
|
||||
].join(" ")}
|
||||
>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WaterReuseLawPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="relative bg-white">
|
||||
{/* <BusinessSubNav /> */}
|
||||
|
||||
<SubPageHeader
|
||||
title="물 재이용 법"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 소개", href: "/reuse/intro/law" },
|
||||
{ label: "물 재이용 법" },
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro/law" },
|
||||
{ label: "물 재이용 법" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 법제도/계획 수립 타임라인 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>법제도 · 계획 및 기본계획 수립</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={lawTimeline} alt="법제도 타임라인" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="law-plan"
|
||||
title="법률 제·개정 및 기본계획 수립"
|
||||
className="mt-6"
|
||||
>
|
||||
<ImgCard src={lawTimeline} alt="법률 제·개정 및 기본계획 수립" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 제2차 계획 대비 실적 현황 (2컬럼) */}
|
||||
<section>
|
||||
<SectionTitle>제 2차 계획 대비 실적 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={performanceLeft} alt="실적 현황 요약" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="plan-performance"
|
||||
title="제2차 계획 대비 실적 현황"
|
||||
>
|
||||
<ImgCard src={performanceLeft} alt="제2차 계획 대비 실적 현황" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 국내 관련 현황 */}
|
||||
<section>
|
||||
<SectionTitle>국내 관련 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={domesticTextbox} alt="국내 관련 현황 설명" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="domestic-change"
|
||||
title="국내 여건 변화"
|
||||
>
|
||||
<ImgCard src={domesticTextbox} alt="국내 여건 변화" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 정책 변천 추이 */}
|
||||
<section>
|
||||
<SectionTitle>물 재이용 정책 변천 추이</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={policyTimeline} alt="정책 변천 타임라인" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="policy-history"
|
||||
title="물 재이용 정책 변화 추이"
|
||||
>
|
||||
<ImgCard src={policyTimeline} alt="물 재이용 정책 변화 추이" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 5) 타 법령과의 관계도 */}
|
||||
<section>
|
||||
<SectionTitle>타법령과의 관계도</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={relNote} alt="관계도 설명" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="law-relation"
|
||||
title="타법령과의 관계 검토"
|
||||
>
|
||||
<ImgCard src={relNote} alt="타법령과의 관계 검토" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 6) 국내 연계 현황 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>국내 연계 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<SectionShell
|
||||
id="domestic-linkage"
|
||||
title="국내 연계 현황"
|
||||
className="pb-[250px]"
|
||||
>
|
||||
<div className="mx-[20px] mb-8">
|
||||
<p className="text-[24px] font-semibold mb-2">주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요</p>
|
||||
<p className="text-[#4F4F4F]">국가 상위계획의 정책 방향이 기후위기 대응 등 수자원의 공급 안전성을 위한 물 재이용 활성화로 정책 실효성 제고 필요<br />
|
||||
• 물관리 계획(국가수도기본계획, 국가 및 유역하수도종합계획, 하천유역수자원관리계획 등)과의 연계성 확보 → 통합적인 관점에서 물이용 체계 구축 필요
|
||||
<p className="mb-2 text-[24px] font-semibold">
|
||||
주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요
|
||||
</p>
|
||||
<p className="text-[#4F4F4F]">
|
||||
국가 상위계획의 정책 방향이 기후위기 대응 등 수자원의 공급 안전성을 위한
|
||||
물 재이용 활성화로 정책 실효성 제고 필요
|
||||
<br />
|
||||
• 물관리 계획(국가수도기본계획, 국가 및 유역하수도종합계획,
|
||||
하천유역수자원관리계획 등)과의 연계성 확보 → 통합적인 관점에서
|
||||
물이용 체계 구축 필요
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ImgCard src={linkage} alt="국내 연계 현황" />
|
||||
</div>
|
||||
</section>
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Lnb from "@/components/Lnb";
|
||||
|
||||
export default function IntroLayout({ children }: { children: ReactNode }) {
|
||||
export default function ExampleLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex gap-0">
|
||||
<div className="relative">
|
||||
<Lnb />
|
||||
<main className="flex-1 px-[100px] pt-[80px]">
|
||||
{children}
|
||||
</main>
|
||||
<div className="flex max-w-[1400px] gap-[56px] py-[50px]">
|
||||
<main className="min-w-0 flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* 우측 메뉴 영역 */}
|
||||
{/* <BusinessSubNav /> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// DM 물 재이용 소개 페이지
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import LawIntroSection from "./_components/LawIntroSection";
|
||||
import RainIntroSection from "./_components/RainIntroSection";
|
||||
import ReuseWaterIntroSection from "./_components/ReuseWaterIntroSection";
|
||||
import TreatedIntroSection from "./_components/TreatedIntroSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "intro-law",
|
||||
label: "물 재이용 법",
|
||||
sections: [
|
||||
{ id: "law-plan", label: "법률 제·개정 및 기본계획 수립" },
|
||||
{ id: "plan-performance", label: "제2차 계획 대비 실적 현황" },
|
||||
{ id: "domestic-change", label: "우리나라의 수자원 현황 및 물이용 여건" },
|
||||
{ id: "policy-history", label: "물 재이용 정책 변화 추이" },
|
||||
{ id: "law-relation", label: "타법령과의 관계 검토" },
|
||||
{ id: "domestic-linkage", label: "주요 국가계획의 정책방향과 연계한 물 재이용 정책 필요" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-rain",
|
||||
label: "빗물 이용시설",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "rain-intro", label: "빗물 재이용 소개" },
|
||||
{
|
||||
id: "rain-mandatory",
|
||||
label: "빗물이용시설 설치 관리 법적 의무 대상",
|
||||
},
|
||||
{ id: "rain-design", label: "빗물이용시설 설계 방안" },
|
||||
{
|
||||
id: "rain-standards",
|
||||
label: "빗물이용시설 시설기준 및 관리기준",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-greywater",
|
||||
label: "중수도",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "greywater-intro", label: "중수 재이용 소개" },
|
||||
{ id: "greywater-mandatory", label: "중수도 설치 의무 대상" },
|
||||
{ id: "greywater-standard", label: "중수도 시설기준 및 관리기준" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "intro-treated",
|
||||
label: "하수처리수",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{ id: "treatedwater-intro", label: "하수처리수 소개" },
|
||||
{
|
||||
id: "treatedwater-mandatory",
|
||||
label: "하수처리수 재이용시설 의무 대상",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function IntroPage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 소개"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro" },
|
||||
]}
|
||||
/>
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
{/* 본문 */}
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
<LawIntroSection />
|
||||
<RainIntroSection />
|
||||
<ReuseWaterIntroSection />
|
||||
<TreatedIntroSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
// apps/dm/app/(landing)/reuse/intro/law/page.tsx
|
||||
// DM 물 재이용 소개(빗물 이용시설) 페이지
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
import rainreuseIntro from "@/public/images/landing/intro/rainreuse_intro.png";
|
||||
import rainreuseMandatory from "@/public/images/landing/intro/rainreuse_mandatory.png";
|
||||
import rainwaterDesign from "@/public/images/landing/intro/rainwater_design.png";
|
||||
@@ -36,67 +38,102 @@ function ImgCard({ src, alt }: ImgCardProps) {
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type SectionShellProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: SectionShellProps) {
|
||||
return (
|
||||
<section
|
||||
id={id}
|
||||
className={["scroll-mt-[140px]", className].join(" ")}
|
||||
>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function RainwaterReusePage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="relative bg-white">
|
||||
<BusinessSubNav />
|
||||
|
||||
<SubPageHeader
|
||||
title="빗물 이용시설"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 소개", href: "/reuse/intro/law" },
|
||||
{ label: "빗물 이용시설" },
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro/law" },
|
||||
{ label: "빗물 이용시설" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 빗물 재이용 소개 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>빗물 재이용 소개</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<SectionShell
|
||||
id="rain-intro"
|
||||
title="빗물 재이용 소개"
|
||||
className="mt-6"
|
||||
>
|
||||
<ImgCard src={rainreuseIntro} alt="빗물 재이용 소개" />
|
||||
</div>
|
||||
</section>
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 빗물이용시설 설치 관리 법적 의무 대상 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>빗물이용시설 설치 관리 법적 의무 대상</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainreuseMandatory} alt="법적 의무 대상 설명" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="rain-mandatory"
|
||||
title="빗물이용시설 설치 관리 법적 의무 대상"
|
||||
>
|
||||
<ImgCard
|
||||
src={rainreuseMandatory}
|
||||
alt="빗물이용시설 설치 관리 법적 의무 대상"
|
||||
/>
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 빗물이용시설 설계 방안 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>빗물이용시설 설계 방안</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainwaterDesign} alt="빗물이용시설 설계 방안 설명" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="rain-design"
|
||||
title="빗물이용시설 설계 방안"
|
||||
>
|
||||
<ImgCard
|
||||
src={rainwaterDesign}
|
||||
alt="빗물이용시설 설계 방안"
|
||||
/>
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 빗물이용시설 시설기준 및 관리기준 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>빗물이용시설 시설기준 및 관리기준</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainwaterStandards} alt="빗물이용시설 시설기준 및 관리기준 설명" />
|
||||
</div>
|
||||
</section>
|
||||
<SectionShell
|
||||
id="rain-standards"
|
||||
title="빗물이용시설 시설기준 및 관리기준"
|
||||
className="pb-[200px]"
|
||||
>
|
||||
<ImgCard
|
||||
src={rainwaterStandards}
|
||||
alt="빗물이용시설 시설기준 및 관리기준"
|
||||
/>
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// DM 물 재이용 소개(중수도) 페이지
|
||||
"use client";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
import greywaterIntro from "@/public/images/landing/intro/greywater_intro.png";
|
||||
import greywaterMandatory from "@/public/images/landing/intro/greywater_mandatory.png";
|
||||
import greywaterStandard from "@/public/images/landing/intro/greywater_standard.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[140px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ReusePage() {
|
||||
return (
|
||||
<div className="relative bg-white">
|
||||
<BusinessSubNav />
|
||||
|
||||
<SubPageHeader
|
||||
title="중수도"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro/law" },
|
||||
{ label: "중수도" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
<SectionShell
|
||||
id="reuse-intro"
|
||||
title="중수 재이용 소개"
|
||||
className="mt-6"
|
||||
>
|
||||
<ImgCard src={greywaterIntro} alt="중수 재이용 소개" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="reuse-mandatory"
|
||||
title="중수도 설치 의무 대상"
|
||||
>
|
||||
<ImgCard src={greywaterMandatory} alt="중수도 설치 의무 대상" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="reuse-standard"
|
||||
title="중수도 시설기준 및 관리기준"
|
||||
className="pb-[200px]"
|
||||
>
|
||||
<ImgCard src={greywaterStandard} alt="중수도 시설기준 및 관리기준" />
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// apps/dm/app/(landing)/reuse/intro/treated/page.tsx
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import treatedwaterIntro from "@/public/images/landing/intro/treatedwater_intro.png";
|
||||
import treatedwaterMandatory from "@/public/images/landing/intro/treatedwater_mandatory.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type ImgCardProps = {
|
||||
src: StaticImageData;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
function ImgCard({ src, alt }: ImgCardProps) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TreatedWaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="하수처리수"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 소개", href: "/reuse/intro/law" },
|
||||
{ label: "하수처리수" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 하수처리수 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>하수처리수</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedwaterIntro} alt="하수처리수 설명" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 하수처리수 재이용시설 의무 대상 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>하수처리수 재이용시설 의무 대상</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedwaterMandatory} alt="하수처리수 재이용시설 의무 대상 설명" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// DM 물 재이용 소개(하수처리수) 페이지
|
||||
"use client";
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import BusinessSubNav from "@/components/BusinessSubNav";
|
||||
|
||||
import treatedwaterIntro from "@/public/images/landing/intro/treatedwater_intro.png";
|
||||
import treatedwaterMandatory from "@/public/images/landing/intro/treatedwater_mandatory.png";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function ImgCard({ src, alt }: { src: StaticImageData; alt: string }) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="mx-auto block max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SectionShell({
|
||||
id,
|
||||
title,
|
||||
children,
|
||||
className = "",
|
||||
}: {
|
||||
id: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<section id={id} className={`scroll-mt-[140px] ${className}`}>
|
||||
<SectionTitle>{title}</SectionTitle>
|
||||
<div className="mt-8">{children}</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export default function TreatedWaterPage() {
|
||||
return (
|
||||
<div className="relative bg-white">
|
||||
<BusinessSubNav />
|
||||
|
||||
<SubPageHeader
|
||||
title="하수처리수"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 소개", href: "/intro/law" },
|
||||
{ label: "하수처리수" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
<SectionShell
|
||||
id="treated-intro"
|
||||
title="하수처리수 개요"
|
||||
className="mt-6"
|
||||
>
|
||||
<ImgCard src={treatedwaterIntro} alt="하수처리수 개요" />
|
||||
</SectionShell>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
<SectionShell
|
||||
id="treated-mandatory"
|
||||
title="하수처리수 재이용시설 의무 대상"
|
||||
className="pb-[300px]"
|
||||
>
|
||||
<ImgCard
|
||||
src={treatedwaterMandatory}
|
||||
alt="하수처리수 재이용시설 의무 대상"
|
||||
/>
|
||||
</SectionShell>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
// app/(with-layout)/layout.tsx
|
||||
/**
|
||||
* 메인을 제외한 메뉴안 페이지의 layout
|
||||
*/
|
||||
|
||||
import "../reuse.module.css";
|
||||
import { StoreProvider } from "../store";
|
||||
import TopNav from "@/components/TopNav";
|
||||
@@ -10,10 +5,15 @@ import TopNav from "@/components/TopNav";
|
||||
export default function WithLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<StoreProvider>
|
||||
<div className="pt-[83px]">{/* TopNav 높이만큼 */}
|
||||
<TopNav />
|
||||
<main className="ml-[260px]">{children}</main>{/* Lnb 너비만큼 */}
|
||||
</div>
|
||||
<TopNav />
|
||||
|
||||
<div className="pt-[83px] flex">
|
||||
|
||||
{/* 본문 */}
|
||||
<main className="ml-[260px] w-full">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
</StoreProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
/**
|
||||
* 중수도
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { useEffect } from "react";
|
||||
// import SubPageHeader from "@/components/SubPageHeader";
|
||||
import Image from "next/image";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { formatUsage } from "@/app/common/transValueFormat";
|
||||
import type {
|
||||
DmStatusLawYearsType,
|
||||
DmStatusBildTpYearsType,
|
||||
DmStatusUsgYearsType,
|
||||
TableHeaderType,
|
||||
} from "@/app/store/dm-status/type";
|
||||
import { Col2Name } from "@/app/store/dm-status/type";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function GreywaterIntroSummary() {
|
||||
const items = [
|
||||
{
|
||||
icon: "/images/landing/icon_recycle.png",
|
||||
title: "지속적인 인프라 확산",
|
||||
desc: (
|
||||
<>
|
||||
전국 중수도 설치 시설은 2018년 대비 약 45% 증가하여,
|
||||
<br />
|
||||
현재 1,000여 개소에서 물 재이용이 활발히 이뤄지고 있습니다.)
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_factory.png",
|
||||
title: "조경용수의 핵심 자원",
|
||||
desc: (
|
||||
<>
|
||||
전체 중수도 이용량의 약 96%가 공업용수로 활용되어,
|
||||
<br />
|
||||
산업 현장에서의 상수도 의존도를 낮추는 데 크게 기여하고 있습니다.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_faucet.png",
|
||||
title: "민간·공공의 협력",
|
||||
desc: (
|
||||
<>
|
||||
공업용 외에도 청소·화장실용수로 꾸준히 사용되며
|
||||
<br />
|
||||
우리 생활 곳곳에서 물 자원 낭비를 막고 있습니다.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mb-[70px] px-[160px]">
|
||||
<div className="grid grid-cols-[1fr_400px] items-center gap-[60px]">
|
||||
<div>
|
||||
<h3 className="text-[40px] font-extrabold leading-[1.35] tracking-[-0.06em] text-neutral-900">
|
||||
건물 안에서 흐르는 또 하나의 자원,
|
||||
<br />
|
||||
<span className="inline-block bg-gradient-to-b from-[#6DBB4F] to-[#01ACE5] bg-clip-text text-transparent">
|
||||
중수도
|
||||
</span>
|
||||
가 물 절약을 실천합니다.
|
||||
</h3>
|
||||
|
||||
<p className="mt-5 text-[16px] leading-[1.8] text-neutral-600">
|
||||
중수도는 개별 건축물이나 단지 내에서 사용한 물을 다시 처리하여
|
||||
<br />
|
||||
청소, 조경, 공업용수로 사용하고 있습니다.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 space-y-8">
|
||||
{items.map((item) => (
|
||||
<div key={item.title} className="flex gap-3 items-center">
|
||||
<div className="mt-1 flex h-12 w-12 shrink-0 items-center justify-center">
|
||||
<Image src={item.icon} alt="" width={48} height={48} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong className="block text-[18px] font-extrabold text-neutral-900">
|
||||
{item.title}
|
||||
</strong>
|
||||
<p className="mt-2 text-[16px] leading-[1.7] tracking-tighter text-neutral-600">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/landing/greywater_status_img.png"
|
||||
alt="중수도 재이용 개념도"
|
||||
width={400}
|
||||
height={343}
|
||||
className="h-auto w-full max-w-[400px]"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
const headerColors6 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#17AFC7]",
|
||||
"bg-[#2CB2A9]",
|
||||
"bg-[#42B58B]",
|
||||
"bg-[#57B86D]",
|
||||
"bg-[#81C784]",
|
||||
];
|
||||
|
||||
export default function GreywaterStatusSection() {
|
||||
const getRswtrByLawYears = useDmStore((state) => state.getRswtrByLawYears);
|
||||
const getRswtrByBildTpYears = useDmStore((state) => state.getRswtrByBildTpYears);
|
||||
const getRswtrByUsgYears = useDmStore((state) => state.getRswtrByUsgYears);
|
||||
const rswtrYears = useDmStore((state) => state.rswtr.rswtrYears);
|
||||
const selectYear = rswtrYears.slice(-1)[0];
|
||||
const rswtrByLawYearsHeader = useDmStore((state) => state.rswtr.rswtrByLawYearsHeader);
|
||||
const rswtrByLawYears = useDmStore((state) => state.rswtr.rswtrByLawYears); // 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)
|
||||
const rswtrByLawYearsFactory = useDmStore((state) => state.rswtr.rswtrByLawYearsFactory); // 공장 및 발전시설 내 중수도 설치 현황
|
||||
const rswtrByBildTpYearsHeader = useDmStore((state) => state.rswtr.rswtrByBildTpYearsHeader);
|
||||
const rswtrByBildTpYears = useDmStore((state) => state.rswtr.rswtrByBildTpYears);
|
||||
const rswtrByUsgYearsHeader = useDmStore((state) => state.rswtr.rswtrByUsgYearsHeader);
|
||||
const rswtrByUsgYears = useDmStore((state) => state.rswtr.rswtrByUsgYears);
|
||||
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 하수도정보시스템 {selectYear} 하수도 통계
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
void getRswtrByLawYears('nofactory'); // rswtrByLawYears: 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)
|
||||
void getRswtrByLawYears('factory'); // rswtrByLawYearsFactory: 공장 및 발전시설 내 중수도 설치 현황
|
||||
void getRswtrByBildTpYears();
|
||||
void getRswtrByUsgYears();
|
||||
}, [getRswtrByLawYears, getRswtrByBildTpYears, getRswtrByUsgYears]);
|
||||
|
||||
const renderPartHeaders = (headers: string[], headerColors: string[]) => {
|
||||
return headers.map((header, index) => (
|
||||
<th
|
||||
key={header}
|
||||
className={`${tableStyles.th} ${headerColors[index] ?? "bg-[#81C784]"} text-white`}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
));
|
||||
};
|
||||
|
||||
const renderCells = (
|
||||
rowIdx: number,
|
||||
row: DmStatusLawYearsType,
|
||||
data: DmStatusLawYearsType[],
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const currentCol1 = String(rowData["COL1"] ?? "");
|
||||
|
||||
// 이전 값
|
||||
const prevCol1 = rowIdx > 0 ? data[rowIdx - 1]?.COL1 : null;
|
||||
|
||||
// 현재 그룹의 rowspan 계산
|
||||
const getRowSpan = () => {
|
||||
let count = 0;
|
||||
for (let i = rowIdx; i < data.length; i++) {
|
||||
if (data[i]?.COL1 === currentCol1) count++;
|
||||
else break;
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
|
||||
// COL1 처리 (rowspan 핵심)
|
||||
if (header.id === "COL1") {
|
||||
// 이전과 같으면 렌더링 안함
|
||||
if (currentCol1 === prevCol1) return null;
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
rowSpan={getRowSpan()}
|
||||
className={`${tableStyles.td} !font-bold !text-neutral-900`}
|
||||
>
|
||||
{value}
|
||||
</td>
|
||||
);
|
||||
} else if (header.id === "COL2") {
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} !font-bold !text-neutral-900`}
|
||||
dangerouslySetInnerHTML={{ __html: Col2Name[value as string] ?? "" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} ${[1, 3, 5].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{String(rowData["COL2"]).indexOf("CNT") > -1
|
||||
? Number(value).toLocaleString("ko-KR")
|
||||
: formatUsage(value as string | number, 10000, 1) }
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderBuildingUsageCells = (
|
||||
row: DmStatusBildTpYearsType|DmStatusUsgYearsType,
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const firstHeaderId = headers[0]?.id;
|
||||
const rowKey = firstHeaderId ? String(rowData[firstHeaderId] ?? "") : "";
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowKey}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 5].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{index === 0 ? value : formatUsage(value as string | number, 10000, 1)}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const title = "중수도(현황)";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-[70px] pb-[120px]">
|
||||
{/* 1) 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외) */}
|
||||
<section>
|
||||
{/* 인트로 영역 */}
|
||||
<GreywaterIntroSummary />
|
||||
|
||||
<div id="status-greywater-overall" className="mt-10 scroll-mt-[120px]">
|
||||
<SectionTitle>전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)</SectionTitle>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
{renderPartHeaders(rswtrYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rswtrByLawYears.map((row, idx, arr) => (
|
||||
<tr key={idx}>
|
||||
{renderCells(idx, row, arr, rswtrByLawYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 공장 및 발전시설 내 중수도 설치 현황 */}
|
||||
<section
|
||||
id="status-greywater-factory"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>공장 및 발전시설 내 중수도 설치 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
{renderPartHeaders(rswtrYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rswtrByLawYearsFactory.map((row, idx, arr) => (
|
||||
<tr key={idx}>
|
||||
{renderCells(idx, row, arr, rswtrByLawYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 중수도 건축물별 이용량 현황 */}
|
||||
<section
|
||||
id="status-greywater-building"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>중수도 건축물별 이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
이용량 (만m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rswtrYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rswtrByBildTpYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rswtrByBildTpYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 중수도 용도별 이용량 현황 */}
|
||||
<section
|
||||
id="status-greywater-usage"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>중수도 용도별 이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
rowSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
<th
|
||||
colSpan={6}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}
|
||||
>
|
||||
이용량 (만m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rswtrYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rswtrByUsgYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rswtrByUsgYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useMemo } from "react";
|
||||
// import SubPageHeader from "@/components/SubPageHeader";
|
||||
import Image from "next/image";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { formatUsage } from "@/app/common/transValueFormat";
|
||||
import type {
|
||||
DmStatusBildTpYearsType,
|
||||
DmStatusUsgBildTpType,
|
||||
DmStatusUsgYearsType,
|
||||
TableHeaderType,
|
||||
} from "@/app/store/dm-status/type";
|
||||
|
||||
|
||||
// 공통 섹션 타이틀 컴포넌트
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
/* 인트로 영역 */
|
||||
function RainIntroSummary() {
|
||||
const items = [
|
||||
{
|
||||
icon: "/images/landing/icon_chart.png",
|
||||
title: "지속적인 이용량 증가",
|
||||
desc: "2018년 약 797만m³이었던 빗물 이용량은 5년 사이 약 75% 증가하여 수자원 절약에 기여하고 있습니다.",
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_hand.png",
|
||||
title: "조경용수의 핵심 자원",
|
||||
desc: "재이용되는 빗물의 약 97%가 조경용수로 활용되고 있으며 이는 공원, 아파트 단지 등의 녹지 관리에 중요한 역할을 합니다.",
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_building.png",
|
||||
title: "민간·공공의 협력",
|
||||
desc: "골프장과 같은 대규모 스포츠 시설은 물론, 최근에는 공동주택·아파트 등 내 이용량이 급증하며 일상생활 속 빗물 활용이 확산되는 추세입니다.",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mb-[70px] px-[160px]">
|
||||
<div className="grid grid-cols-[1fr_400px] items-center gap-[60px]">
|
||||
{/* 좌측 */}
|
||||
<div>
|
||||
<h3 className="text-[40px] font-extrabold leading-[1.35] tracking-[-0.04em] text-neutral-900">
|
||||
기후위기 시대의 수자원 확보,
|
||||
<br />
|
||||
<span className="inline-block bg-gradient-to-b from-[#6DBB4F] to-[#01ACE5] bg-clip-text text-transparent">
|
||||
빗물 재이용
|
||||
</span>
|
||||
이 앞장섭니다.
|
||||
</h3>
|
||||
|
||||
<p className="mt-5 text-[16px] leading-[1.8] tracking-[-0.04em] text-neutral-600">
|
||||
2023년 기준 전국 3,500여 개 시설에서
|
||||
<br />
|
||||
연간 약 1,394만m³의 빗물을 소중한 자원으로 다시 활용하고 있습니다.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 space-y-8">
|
||||
{items.map((item) => (
|
||||
<div key={item.title} className="flex gap-3 items-center">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center">
|
||||
<Image
|
||||
src={item.icon}
|
||||
alt=""
|
||||
width={48}
|
||||
height={48}
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong className="block text-[18px] font-extrabold text-neutral-900">
|
||||
{item.title}
|
||||
</strong>
|
||||
<p className="mt-2 text-[16px] leading-[1.7] text-neutral-600 tracking-tighter">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우측 이미지 */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/landing/rain_status_img.png"
|
||||
alt="빗물 재이용 개념도"
|
||||
width={400}
|
||||
height={343}
|
||||
className="h-auto w-full max-w-[400px] object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
const headerColors6 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#17AFC7]",
|
||||
"bg-[#2CB2A9]",
|
||||
"bg-[#42B58B]",
|
||||
"bg-[#57B86D]",
|
||||
"bg-[#81C784]",
|
||||
];
|
||||
const headerColors8 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#10AED0]",
|
||||
"bg-[#20B0BA]",
|
||||
"bg-[#2FB2A5]",
|
||||
"bg-[#3FB58F]",
|
||||
"bg-[#4EB77A]",
|
||||
"bg-[#5EB964]",
|
||||
"bg-[#6DBB4F]",
|
||||
];
|
||||
export default function RainStatusSection() {
|
||||
const getRainyByBildTpYears = useDmStore((state) => state.getRainyByBildTpYears);
|
||||
const getRainyByUsgBildTp = useDmStore((state) => state.getRainyByUsgBildTp);
|
||||
const getRainyByUsgYears = useDmStore((state) => state.getRainyByUsgYears);
|
||||
const rainyYears = useDmStore((state) => state.rainy.rainyYears);
|
||||
const selectYear = rainyYears.slice(-1)[0];
|
||||
const rainyByBildTpYearsHeader = useDmStore((state) => state.rainy.rainyByBildTpYearsHeader);
|
||||
const rainyByBildTpYears = useDmStore((state) => state.rainy.rainyByBildTpYears);
|
||||
const rainyByUsgBildTpHeader = useDmStore((state) => state.rainy.rainyByUsgBildTpHeader);
|
||||
const rainyByUsgBildTp = useDmStore((state) => state.rainy.rainyByUsgBildTp);
|
||||
const rainyByUsgYearsHeader = useDmStore((state) => state.rainy.rainyByUsgYearsHeader);
|
||||
const rainyByUsgYears = useDmStore((state) => state.rainy.rainyByUsgYears);
|
||||
const partBildTpHeader = useMemo(
|
||||
() => rainyByUsgBildTpHeader.slice(3).map((header) => header.title),
|
||||
[rainyByUsgBildTpHeader]
|
||||
);
|
||||
|
||||
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 하수도정보시스템 {selectYear} 하수도 통계
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void getRainyByBildTpYears();
|
||||
void getRainyByUsgBildTp();
|
||||
void getRainyByUsgYears();
|
||||
}, [getRainyByBildTpYears, getRainyByUsgBildTp, getRainyByUsgYears]);
|
||||
|
||||
const renderPartHeaders = (headers: string[], headerColors: string[]) => {
|
||||
return headers.map((header, index) => (
|
||||
<th
|
||||
key={header}
|
||||
className={`${tableStyles.th} ${headerColors[index] ?? "bg-[#81C784]"} text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
));
|
||||
};
|
||||
|
||||
const renderBuildingUsageCells = (
|
||||
row: DmStatusBildTpYearsType | DmStatusUsgYearsType | DmStatusUsgYearsType,
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const firstHeaderId = headers[0]?.id;
|
||||
const rowKey = firstHeaderId ? String(rowData[firstHeaderId] ?? "") : "";
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowKey}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 5].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{index === 0 ? value : formatUsage(value as string | number, 1000, 1)}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderRainyByUsgBildTpCells = (row: DmStatusUsgBildTpType) => {
|
||||
return rainyByUsgBildTpHeader.map((header, index) => {
|
||||
const value = row[header.id as keyof DmStatusUsgBildTpType];
|
||||
const displayValue =
|
||||
index === 0
|
||||
? value
|
||||
: header.id === "CNT"
|
||||
? Number(value).toLocaleString("ko-KR")
|
||||
: formatUsage(value as string | number, 1000, 1);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${row.USG_NM}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 2].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{displayValue == 0.0 ? "-" : displayValue}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const title = "빗물이용시설(현황)";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-[70px] pb-[120px]">
|
||||
{/* 1) 빗물이용시설의 건축물별 빗물이용량 현황 */}
|
||||
<section>
|
||||
{/* 인트로 영역 */}
|
||||
<RainIntroSummary />
|
||||
|
||||
<div id="status-rain-building" className="scroll-mt-[120px]">
|
||||
<SectionTitle>빗물이용시설의 건축물별 빗물이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>
|
||||
구분
|
||||
</th>
|
||||
<th colSpan={rainyYears.length || 6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>
|
||||
빗물이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rainyYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByBildTpYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rainyByBildTpYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 빗물이용시설의 용도별 분포 (xxxx년 기준) */}
|
||||
<section
|
||||
id="status-rain-purpose-distribution"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>빗물이용시설의 용도별 분포 ({selectYear}년 기준)</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>용도</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "84px" }}>개소수<br />(개소)</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "84px" }}>저류용량<br />(천m³)</th>
|
||||
<th colSpan={8} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>빗물이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(partBildTpHeader, headerColors8)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByUsgBildTp.map((row, idx) => (
|
||||
<tr key={`${row.USG_NM}-${idx}`} className="bg-white">
|
||||
{renderRainyByUsgBildTpCells(row)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 빗물이용시설의 용도별 빗물이용량 현황 */}
|
||||
<section
|
||||
id="status-rain-purpose-usage"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>빗물이용시설의 용도별 빗물이용량 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>용도</th>
|
||||
<th colSpan={6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>빗물이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rainyYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByUsgYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rainyByUsgYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,591 @@
|
||||
/**
|
||||
* 하수처리수
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import Image from "next/image";
|
||||
import { useEffect } from "react";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { formatUsage } from "@/app/common/transValueFormat";
|
||||
import type {
|
||||
DmStatusLawYearsType,
|
||||
DmStatusBildTpYearsType,
|
||||
DmStatusUsgYearsType,
|
||||
DmStatusSidoUsgType,
|
||||
TableHeaderType,
|
||||
} from "@/app/store/dm-status/type";
|
||||
import { Col2Name } from "@/app/store/dm-status/type";
|
||||
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="h-5 w-[3px] bg-sky-500" />
|
||||
<h3 className="text-[24px] font-bold text-neutral-900">{children}</h3>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
function SewageIntroSummary() {
|
||||
const items = [
|
||||
{
|
||||
icon: "/images/landing/icon_cycle_water.png",
|
||||
title: "안정적인 재이용률 유지",
|
||||
desc: (
|
||||
<>
|
||||
연간 약 1,136백만m³의 하수처리수가 재이용되고 있으며,
|
||||
<br/>
|
||||
약 15%에 달하는 높은 재이용률을 통해
|
||||
<br/>
|
||||
수자원 선순환 체계를 구축하고 있습니다.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_water_flow.png",
|
||||
title: "메마른 하천을 살리는 물",
|
||||
desc: (
|
||||
<>
|
||||
재이용되는 물의 가장 많은 부분이 하천유지용수로 사용되어
|
||||
<br/>
|
||||
도심 속 하천의 생태계를 보호하고 쾌적한 수변 환경을 조성합니다.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
icon: "/images/landing/icon_location.png",
|
||||
title: "지역별 균형 있는 활용",
|
||||
desc: (
|
||||
<>
|
||||
전국적으로 고르게 시설이 확충되고 있으며, 특히 경기 지역을 중심으로
|
||||
<br/>
|
||||
대규모 공업용수 공급망을 구축하여
|
||||
<br/>
|
||||
지역 경제와 환경 보호를 동시에 실현하고 있습니다.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="mb-[70px] px-[160px]">
|
||||
<div className="grid grid-cols-[1fr_400px] items-center gap-0">
|
||||
{/* 좌측 */}
|
||||
<div>
|
||||
<h3 className="text-[40px] font-extrabold leading-[1.35] tracking-[-0.14em] text-neutral-900">
|
||||
버려지는 하수에서 찾는 새로운 가치,
|
||||
<br />
|
||||
<span className="inline-block bg-gradient-to-b from-[#6DBB4F] to-[#01ACE5] bg-clip-text text-transparent">
|
||||
하수처리수 재이용
|
||||
</span>
|
||||
이 도시를 숨쉬게 합니다.
|
||||
</h3>
|
||||
|
||||
<p className="mt-5 text-[16px] leading-[1.8] text-neutral-600">
|
||||
하수처리장에서 정화된 물은 하천의 수량을 조절하고
|
||||
<br />
|
||||
산업 현장에 용수를 공급하는 소중한 자원입니다.
|
||||
</p>
|
||||
|
||||
<div className="mt-10 space-y-8">
|
||||
{items.map((item) => (
|
||||
<div key={item.title} className="flex gap-4 items-center">
|
||||
<div className="mt-1 flex h-12 w-12 shrink-0 items-center justify-center">
|
||||
<Image
|
||||
src={item.icon}
|
||||
alt=""
|
||||
width={48}
|
||||
height={48}
|
||||
className="object-contain"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<strong className="block text-[18px] font-extrabold text-neutral-900">
|
||||
{item.title}
|
||||
</strong>
|
||||
<p className="mt-2 text-[16px] leading-[1.7] text-neutral-600">
|
||||
{item.desc}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우측 이미지 */}
|
||||
<div className="flex items-center justify-center">
|
||||
<Image
|
||||
src="/images/landing/sewage_status_img.png"
|
||||
alt="하수처리수 재이용 개념도"
|
||||
width={420}
|
||||
height={360}
|
||||
className="h-auto w-full max-w-[420px] object-contain"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
const headerColors6 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#17AFC7]",
|
||||
"bg-[#2CB2A9]",
|
||||
"bg-[#42B58B]",
|
||||
"bg-[#57B86D]",
|
||||
"bg-[#81C784]",
|
||||
];
|
||||
|
||||
export default function SewageStatusSection() {
|
||||
const getPbnstByLawYears = useDmStore((state) => state.getPbnstByLawYears);
|
||||
const getPbnstByUsgYears = useDmStore((state) => state.getPbnstByUsgYears);
|
||||
const getPbnstByCpcYears = useDmStore((state) => state.getPbnstByCpcYears);
|
||||
const getPbnstBySido = useDmStore((state) => state.getPbnstBySido);
|
||||
const pbnstYears = useDmStore((state) => state.pbnst.pbnstYears);
|
||||
const selectYear = pbnstYears.slice(-1)[0];
|
||||
const pbnstByLawYearsHeader = useDmStore((state) => state.pbnst.pbnstByLawYearsHeader);
|
||||
const pbnstByLawYears = useDmStore((state) => state.pbnst.pbnstByLawYears);
|
||||
const pbnstByUsgYearsHeader = useDmStore((state) => state.pbnst.pbnstByUsgYearsHeader);
|
||||
const pbnstByUsgYears = useDmStore((state) => state.pbnst.pbnstByUsgYears);
|
||||
const pbnstByCpcYearsHeader = useDmStore((state) => state.pbnst.pbnstByCpcYearsHeader);
|
||||
const pbnstByCpcYears = useDmStore((state) => state.pbnst.pbnstByCpcYears);
|
||||
const pbnstBySidoHeader = useDmStore((state) => state.pbnst.pbnstBySidoHeader);
|
||||
const pbnstBySido = useDmStore((state) => state.pbnst.pbnstBySido);
|
||||
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 하수도정보시스템 {selectYear} 하수도 통계
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void getPbnstByLawYears();
|
||||
void getPbnstByUsgYears();
|
||||
void getPbnstByCpcYears();
|
||||
void getPbnstBySido();
|
||||
}, [getPbnstByLawYears, getPbnstByUsgYears, getPbnstByCpcYears, getPbnstBySido]);
|
||||
|
||||
const renderPartHeaders = (headers: string[], headerColors: string[]) => {
|
||||
return headers.map((header, index) => (
|
||||
<th
|
||||
key={header}
|
||||
className={`${tableStyles.th} ${headerColors[index] ?? "bg-[#81C784]"} text-white`}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
));
|
||||
};
|
||||
|
||||
const renderCells = (
|
||||
rowIdx: number,
|
||||
row: DmStatusLawYearsType,
|
||||
data: DmStatusLawYearsType[],
|
||||
headers: TableHeaderType[],
|
||||
unitSize: number = 1, // 단위 변경 1000 인 경우, 100,000 -> 100 으로 변경
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const currentCol1 = String(rowData["COL1"] ?? "");
|
||||
|
||||
// 이전 값
|
||||
const prevCol1 = rowIdx > 0 ? data[rowIdx - 1]?.COL1 : null;
|
||||
|
||||
// 현재 그룹의 rowspan 계산
|
||||
const getRowSpan = () => {
|
||||
let count = 0;
|
||||
for (let i = rowIdx; i < data.length; i++) {
|
||||
if (data[i]?.COL1 === currentCol1) count++;
|
||||
else break;
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
|
||||
// COL1 처리 (rowspan 핵심)
|
||||
if (header.id === "COL1") {
|
||||
// 이전과 같으면 렌더링 안함
|
||||
if (currentCol1 === prevCol1) return null;
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
rowSpan={getRowSpan()}
|
||||
className={`${tableStyles.td} !font-bold !text-neutral-900`}
|
||||
>
|
||||
{value}
|
||||
</td>
|
||||
);
|
||||
} else if (header.id === "COL2") {
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} !font-bold !text-neutral-900`}
|
||||
dangerouslySetInnerHTML={{ __html: Col2Name[value as string] ?? "" }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} ${[0, 2, 4].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{(String(rowData["COL2"]).indexOf("CNT") > -1)
|
||||
? Number(value).toLocaleString("ko-KR")
|
||||
: ((String(rowData["COL2"]).indexOf("REUSERT") > -1)
|
||||
? formatUsage(value as string | number, 1, 1)
|
||||
: formatUsage(value as string | number, unitSize, 1))
|
||||
}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderPbnstUsgCells = (
|
||||
rowIdx: number,
|
||||
row: DmStatusBildTpYearsType | DmStatusUsgYearsType,
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const isLastInGroup = rowIdx === 8 || rowIdx === 17;
|
||||
|
||||
const getType = (colNm:string) => {
|
||||
const last3 = colNm.slice(-3); // 끝 3자리
|
||||
const prefix = last3[0]; // S 또는 D
|
||||
|
||||
if (prefix === 'S') {
|
||||
const num = parseInt(last3.slice(1), 10); // 숫자
|
||||
if (num < 8) return 'red-item';
|
||||
if (num >= 8) return 'blue-item';
|
||||
}
|
||||
|
||||
return ''; // 조건 안 맞으면 빈값 (필요시 기본값)
|
||||
};
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
const colNm = String(rowData["COL_NM"] ?? "");
|
||||
const type = getType(colNm);
|
||||
const isSubItem = type === "red-item" || type === "blue-item";
|
||||
|
||||
if (index === 0) {
|
||||
console.log(header.id, value);
|
||||
return (
|
||||
<td key={`${rowIdx}-${header.id}`} className={`${tableStyles.td} relative`}>
|
||||
{isSubItem ? (
|
||||
<>
|
||||
<div
|
||||
className={`absolute left-[1px] w-[16px] bg-white z-10 top-[-2px] ${
|
||||
isLastInGroup ? "bottom-[0px]" : "bottom-[-2px]"
|
||||
}`}
|
||||
/>
|
||||
<div
|
||||
className={`absolute left-[16px] w-[4px] z-20 ${
|
||||
type === "red-item" ? "bg-[#FF8B8B]" : "bg-[#4ECBFF]"
|
||||
} top-[-2px] ${
|
||||
isLastInGroup ? "bottom-[0px]" : "bottom-[-2px]"
|
||||
}`}
|
||||
/>
|
||||
<span className="text-[#4f4f4f] font-bold block w-full text-center relative z-30 translate-x-[8px]">
|
||||
{value}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="font-bold text-neutral-900 block w-full text-center">
|
||||
{value}
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[0, 2, 4].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{formatUsage(value as string | number, 1000, 1)}
|
||||
</td>
|
||||
);
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
const renderSidoUsgCells = (
|
||||
rowIdx: number,
|
||||
row: DmStatusSidoUsgType,
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
|
||||
return headers.map((header, index) => {
|
||||
let value = rowData[header.id];
|
||||
value = (index === 0||index ===9) ? value : formatUsage(value as string | number, 1000, 1);
|
||||
value = value === "0.0"? "-" : value;
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowIdx}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 9].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{value}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const title = "하수처리수 재이용시설(현황)";
|
||||
|
||||
return (
|
||||
<div className="bg-white">
|
||||
{/* 상단 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h2 className="relative inline-block px-2 text-[28px] font-extrabold tracking-tight leading-none text-slate-900">
|
||||
<span className="relative z-10">{title}</span>
|
||||
<span className="absolute left-0 bottom-[-4px] z-0 h-3 w-full bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div className="mt-[70px] pb-[120px]">
|
||||
{/* 1) 연도별 하수처리수 재이용 현황 */}
|
||||
<section>
|
||||
{/* 인트로 */}
|
||||
<SewageIntroSummary />
|
||||
|
||||
<div id="status-treated-year" className="scroll-mt-[120px]">
|
||||
<SectionTitle>연도별 하수처리수 재이용 현황</SectionTitle>
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "180px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
{renderPartHeaders(pbnstYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pbnstByLawYears.map((row, idx, arr) => (
|
||||
<tr key={idx}>
|
||||
{renderCells(idx, row, arr, pbnstByLawYearsHeader, 1000)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 용도별 하수처리수 재이용 현황 */}
|
||||
<section id="status-treated-usage" className="scroll-mt-[120px]">
|
||||
<SectionTitle>용도별 하수처리수 재이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<div className="mb-2 text-right text-[13px] text-neutral-600">
|
||||
(단위: 백만m³/일)
|
||||
</div>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "180px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} ${tableStyles.headerBg}`}>
|
||||
구분
|
||||
</th>
|
||||
{renderPartHeaders(pbnstYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pbnstByUsgYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderPbnstUsgCells(idx, row, pbnstByUsgYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 하수처리시설 설치 및 이용 현황 */}
|
||||
<section id="status-treated-facility" className="scroll-mt-[120px]">
|
||||
<SectionTitle>하수처리시설 설치 및 이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "130px" }} />
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
colSpan={2}
|
||||
className={`${tableStyles.th} ${tableStyles.headerBg}`}
|
||||
>
|
||||
구분
|
||||
</th>
|
||||
{renderPartHeaders(pbnstYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pbnstByCpcYears.map((row, idx, arr) => (
|
||||
<tr key={idx}>
|
||||
{renderCells(idx, row, arr, pbnstByCpcYearsHeader, 10000)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 시도별 하수처리수 재이용 현황 */}
|
||||
<section
|
||||
id="status-treated-region"
|
||||
className="scroll-mt-[120px]"
|
||||
>
|
||||
<SectionTitle>시도별 하수처리수 재이용 현황</SectionTitle>
|
||||
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<div className="mb-2 text-right text-[13px] text-neutral-600">
|
||||
(단위: 백만m³/일)
|
||||
</div>
|
||||
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: "160px" }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`}> 시도 </th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`} >
|
||||
연간<br />하수<br />처리량
|
||||
</th>
|
||||
<th colSpan={7} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`} >
|
||||
재이용현황
|
||||
</th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`} >
|
||||
재이용률<br />(%)
|
||||
</th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`} >
|
||||
비고
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} >
|
||||
계
|
||||
</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} >
|
||||
장내
|
||||
</th>
|
||||
<th colSpan={5} className={`${tableStyles.th} ${tableStyles.headerBg} py-1`} >
|
||||
장외
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white py-2 text-[16px]`}>
|
||||
소계
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#1FB1C2] text-white py-2 text-[13px]`}>
|
||||
하천유지용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#3EB59F] text-white py-2 text-[16px]`}>
|
||||
공업용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#5DB97C] text-white py-2 text-[16px]`}>
|
||||
농업용수
|
||||
</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white py-2 text-[16px]`}>
|
||||
기타용수
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{pbnstBySido.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderSidoUsgCells(idx, row, pbnstBySidoHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
// apps/dm/app/(landing)/reuse/cases/greywater/page.tsx
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import greywaterOverall from "@/public/images/landing/cases/greywater_overall.png";
|
||||
import greywaterFactory from "@/public/images/landing/cases/greywater_factory.png";
|
||||
import greywaterBuilding from "@/public/images/landing/cases/greywater_building.png";
|
||||
import greywaterUsage from "@/public/images/landing/cases/greywater_usage.png";
|
||||
import React from "react";
|
||||
|
||||
// 공통 섹션 타이틀 컴포넌트
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -17,86 +13,351 @@ function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type ImgCardProps = {
|
||||
src: StaticImageData;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「2023 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 공통 테이블 스타일
|
||||
* - table-fixed: 열 넓이를 고정하고 균등 분배하기 위해 추가
|
||||
* - 폰트 사이즈: 16px로 수정
|
||||
*/
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="중수도(현황)"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 현황", href: "/reuse/cases/rain" },
|
||||
{ label: "중수도(현황)" },
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 현황", href: "/reuse/cases/rain" },
|
||||
{ label: "중수도(현황)" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외) */}
|
||||
<section className="mt-6">
|
||||
<section className="mt-10">
|
||||
<SectionTitle>전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterOverall} alt="전체 중수도 설치 및 이용 현황 설명" />
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
{/* colgroup으로 열 너비를 최상단에서 완벽히 제어합니다 */}
|
||||
<colgroup>
|
||||
<col style={{ width: '130px' }} />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "576", "633", "682", "740", "819", "842"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "1,494.2", "1,514.4", "1,524.9", "989.1", "1,104.3", "1,015.4"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "39.6", "40.0", "51.1", "47.1", "48.9", "50.7"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "2,224.0", "1,166.0", "4,377.3", "3,196.5", "2,752.5", "898.0"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "196", "214", "253", "273", "287", "325"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "649.7", "668.6", "720.9", "743.2", "760.4", "822.0"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "20.0", "20.5", "21.1", "21.1", "21.2", "24.7"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "614.7", "491.9", "518.1", "362.5", "349.6", "654.1"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "380", "419", "429", "467", "532", "517"],
|
||||
["건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", "844.5", "845.8", "804.0", "245.8", "255.1", "282.3"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "19.6", "19.5", "30.0", "26.0", "27.7", "26.0"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "1,609.3", "674.1", "3,859.2", "2,834.0", "2,402.9", "243.9"],
|
||||
]
|
||||
}
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${isFirstCol ? "!font-bold !text-neutral-900" : ""} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 중수공장 및 발전시설 내 중수도 설치 및 이용 현황 */}
|
||||
{/* 2) 공장 및 발전시설 내 중수도 설치 및 이용 현황 */}
|
||||
<section>
|
||||
<SectionTitle>공장 및 발전시설 내 중수도 설치 및 이용 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterFactory} alt="공장 및 발전시설 내 중수도 설치 및 이용 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '130px' }} />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "125", "127", "139", "146", "151", "176"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "141.3", "141.0", "146.7", "189.0", "190.3", "166.5"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "33,712", "29,300", "33,110", "22,284", "21,593", "21,349"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "59", "63", "78", "78", "82", "86"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "108.6", "109.7", "118.1", "159.5", "142.6", "119.4"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "24,847", "24,021", "28,055", "17,959", "12,239", "12,297"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "66", "64", "61", "68", "69", "90"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "32.7", "31.4", "28.7", "29.5", "47.7", "47.1"],
|
||||
["사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", "8,864.9", "5,278.8", "5,054.7", "4,324.6", "9,353.3", "9,052.4"],
|
||||
]
|
||||
}
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${isFirstCol ? "!font-bold !text-neutral-900" : ""} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 중수도 건축물별 이용량 현황 */}
|
||||
<section className="pb-16">
|
||||
<SectionTitle>중수도 건축물별 이용량 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterBuilding} alt="중수도 건축물별 이용량 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th colSpan={6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["공장·발전시설", "33,712.3", "29,300.6", "33,109.9", "22,284.1", "21,593.3", "21,349.6"],
|
||||
["교정시설", "1.3", "-", "0.4", "0.1", "0.1", "0.1"],
|
||||
["대규모점포", "393.0", "241.5", "259.3", "211.5", "369.2", "119.2"],
|
||||
["방송국·전신국", "9.7", "1.1", "12.7", "7.2", "7.4", "2.3"],
|
||||
["숙박·목욕장", "269.6", "234.4", "250.3", "128.1", "125.8", "104.9"],
|
||||
["업무시설", "338.2", "211.4", "268.0", "138.2", "116.4", "61.4"],
|
||||
["운수·물류시설", "759.2", "34.2", "459.8", "259.8", "252.8", "410.4"],
|
||||
["기타", "453.1", "443.3", "3,126.8", "2,451.5", "1,880.8", "199.7"],
|
||||
["합계", "35,936.3", "30,466.6", "37,487.2", "25,480.6", "24,345.8", "22,247.6"],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className={idx === 8 ? "bg-white font-bold" : "bg-white"}>
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${i === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 중수도 용도별 이용량 현황 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>중수도 용도별 이용량 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={greywaterUsage} alt="중수도 용도별 이용량 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th colSpan={6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["공업용수", "33,082", "28,719", "32,312", "21,707", "20,991", "20,470"],
|
||||
["세척·살수용수", "860.2", "146.5", "133.8", "100.7", "95.1", "149.9"],
|
||||
["조경용수", "313.9", "326.4", "307.6", "67.3", "63.2", "65.9"],
|
||||
["청소·화장실용수", "1,650.7", "1,272", "1,345.7", "1,340", "1,435.8", "1,459.7"],
|
||||
["친수용수", "28.6", "0.5", "2,807.9", "2,258.1", "1,643.6", "9.1"],
|
||||
["미기재", "0.2", "2.0", "580.1", "7.3", "116.2", "92.4"],
|
||||
["합계", "35,935.6", "30,466.4", "37,487.1", "25,480.4", "24,344.9", "22,247"],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className={idx === 6 ? "bg-white font-bold" : "bg-white"}>
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${i === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 5].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
"use client";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import Lnb from "@/components/Lnb";
|
||||
|
||||
export default function CasesLayout({ children }: { children: ReactNode }) {
|
||||
export default function ExampleLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="flex gap-0">
|
||||
<div className="relative">
|
||||
<Lnb />
|
||||
<main className="flex-1 px-[100px] pt-[80px]">{children}</main>
|
||||
<div className="flex max-w-[1400px] gap-[56px] py-[50px]">
|
||||
<main className="min-w-0 flex-1">
|
||||
{children}
|
||||
</main>
|
||||
|
||||
{/* 우측 메뉴 영역 */}
|
||||
{/* <BusinessSubNav /> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// DM 물 재이용 현황 페이지
|
||||
import BusinessSubNav, { type BusinessSubNavMenu } from "@/components/BusinessSubNav";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import RainStatusSection from "./_components/RainStatusSection";
|
||||
import GreywaterStatusSection from "./_components/GreywaterStatusSection";
|
||||
import SewageStatusSection from "./_components/SewageStatusSection";
|
||||
|
||||
const menus: BusinessSubNavMenu[] = [
|
||||
{
|
||||
key: "status-rain",
|
||||
label: "빗물이용시설(현황)",
|
||||
sections: [
|
||||
{
|
||||
id: "status-rain-building",
|
||||
label: "빗물이용시설의 건축물별 빗물이용량 현황",
|
||||
},
|
||||
{
|
||||
id: "status-rain-purpose-distribution",
|
||||
label: "빗물이용시설의 용도별 분포",
|
||||
},
|
||||
{
|
||||
id: "status-rain-purpose-usage",
|
||||
label: "빗물이용시설의 용도별 빗물이용량 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "status-greywater",
|
||||
label: "중수도(현황)",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{
|
||||
id: "status-greywater-overall",
|
||||
label: "전체 중수도 설치 및 이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-factory",
|
||||
label: "공장 및 발전시설 내 중수도 설치 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-building",
|
||||
label: "중수도 건축물별 이용량 현황",
|
||||
},
|
||||
{
|
||||
id: "status-greywater-usage",
|
||||
label: "중수도 용도별 이용량 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "status-sewage",
|
||||
label: "하수처리수 재이용시설(현황)",
|
||||
defaultOpen: false,
|
||||
sections: [
|
||||
{
|
||||
id: "status-treated-year",
|
||||
label: "연도별 하수처리수 재이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-usage",
|
||||
label: "연도별 하수처리수 재이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-facility",
|
||||
label: "하수처리시설 설치 및 이용 현황",
|
||||
},
|
||||
{
|
||||
id: "status-treated-region",
|
||||
label: "시도별 하수처리수 재이용 현황",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function StatusPage() {
|
||||
return (
|
||||
<div className="relative">
|
||||
{/* 상단 헤더 */}
|
||||
<SubPageHeader
|
||||
title="물 재이용 현황"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 현황", href: "/status" },
|
||||
]}
|
||||
/>
|
||||
{/* 우측 앵커 네비 */}
|
||||
<BusinessSubNav menus={menus} offsetTop={120} />
|
||||
|
||||
<div className="max-w-[1320px] pb-24">
|
||||
<RainStatusSection />
|
||||
<GreywaterStatusSection />
|
||||
<SewageStatusSection />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,12 +1,17 @@
|
||||
// apps/dm/app/(landing)/reuse/cases/rain/page.tsx
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import rainwaterBuilding from "@/public/images/landing/cases/rainwater_building.png";
|
||||
import rainwaterUsage from "@/public/images/landing/cases/rainwater_usage.png";
|
||||
import rainwaterUsageAmount from "@/public/images/landing/cases/rainwater_usage_amount.png";
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { formatUsage } from "@/app/common/transValueFormat";
|
||||
import type {
|
||||
DmStatusBildTpYearsType,
|
||||
DmStatusUsgBildTpType,
|
||||
DmStatusUsgYearsType,
|
||||
TableHeaderType,
|
||||
} from "@/app/store/dm-status/type";
|
||||
|
||||
// 공통 섹션 타이틀 컴포넌트
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -16,76 +21,240 @@ function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type ImgCardProps = {
|
||||
src: StaticImageData;
|
||||
alt: string;
|
||||
|
||||
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
function ImgCard({ src, alt }: ImgCardProps) {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const headerColors6 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#17AFC7]",
|
||||
"bg-[#2CB2A9]",
|
||||
"bg-[#42B58B]",
|
||||
"bg-[#57B86D]",
|
||||
"bg-[#81C784]",
|
||||
];
|
||||
const headerColors8 = [
|
||||
"bg-[#01ACE5]",
|
||||
"bg-[#10AED0]",
|
||||
"bg-[#20B0BA]",
|
||||
"bg-[#2FB2A5]",
|
||||
"bg-[#3FB58F]",
|
||||
"bg-[#4EB77A]",
|
||||
"bg-[#5EB964]",
|
||||
"bg-[#6DBB4F]",
|
||||
];
|
||||
|
||||
|
||||
export default function RainwaterStatusPage() {
|
||||
const getRainyByBildTpYears = useDmStore((state) => state.getRainyByBildTpYears);
|
||||
const getRainyByUsgBildTp = useDmStore((state) => state.getRainyByUsgBildTp);
|
||||
const getRainyByUsgYears = useDmStore((state) => state.getRainyByUsgYears);
|
||||
const rainyYears = useDmStore((state) => state.rainy.rainyYears);
|
||||
const selectYear = rainyYears.slice(-1)[0];
|
||||
const rainyByBildTpYearsHeader = useDmStore((state) => state.rainy.rainyByBildTpYearsHeader);
|
||||
const rainyByBildTpYears = useDmStore((state) => state.rainy.rainyByBildTpYears);
|
||||
const rainyByUsgBildTpHeader = useDmStore((state) => state.rainy.rainyByUsgBildTpHeader);
|
||||
const rainyByUsgBildTp = useDmStore((state) => state.rainy.rainyByUsgBildTp);
|
||||
const rainyByUsgYearsHeader = useDmStore((state) => state.rainy.rainyByUsgYearsHeader);
|
||||
const rainyByUsgYears = useDmStore((state) => state.rainy.rainyByUsgYears);
|
||||
const partBildTpHeader = useMemo(
|
||||
() => rainyByUsgBildTpHeader.slice(3).map((header) => header.title),
|
||||
[rainyByUsgBildTpHeader]
|
||||
);
|
||||
|
||||
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「{selectYear} 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
void getRainyByBildTpYears();
|
||||
void getRainyByUsgBildTp();
|
||||
void getRainyByUsgYears();
|
||||
}, [getRainyByBildTpYears, getRainyByUsgBildTp, getRainyByUsgYears]);
|
||||
|
||||
const renderPartHeaders = (headers: string[], headerColors: string[]) => {
|
||||
return headers.map((header, index) => (
|
||||
<th
|
||||
key={header}
|
||||
className={`${tableStyles.th} ${headerColors[index] ?? "bg-[#81C784]"} text-white`}
|
||||
style={{ width: "140px" }}
|
||||
>
|
||||
{header}
|
||||
</th>
|
||||
));
|
||||
};
|
||||
|
||||
const renderBuildingUsageCells = (
|
||||
row: DmStatusBildTpYearsType | DmStatusUsgYearsType | DmStatusUsgYearsType,
|
||||
headers: TableHeaderType[]
|
||||
) => {
|
||||
const rowData = row as Record<string, string | number>;
|
||||
const firstHeaderId = headers[0]?.id;
|
||||
const rowKey = firstHeaderId ? String(rowData[firstHeaderId] ?? "") : "";
|
||||
|
||||
return headers.map((header, index) => {
|
||||
const value = rowData[header.id];
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${rowKey}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 5].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{index === 0 ? value : formatUsage(value as string | number, 1000, 1)}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const renderRainyByUsgBildTpCells = (row: DmStatusUsgBildTpType) => {
|
||||
return rainyByUsgBildTpHeader.map((header, index) => {
|
||||
const value = row[header.id as keyof DmStatusUsgBildTpType];
|
||||
const displayValue =
|
||||
index === 0
|
||||
? value
|
||||
: header.id === "CNT"
|
||||
? Number(value).toLocaleString("ko-KR")
|
||||
: formatUsage(value as string | number, 1000, 1);
|
||||
|
||||
return (
|
||||
<td
|
||||
key={`${row.USG_NM}-${header.id}`}
|
||||
className={`${tableStyles.td} ${index === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 2].includes(index) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{displayValue == 0.0 ? "-" : displayValue}
|
||||
</td>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default function GreywaterPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="bg-white">
|
||||
<SubPageHeader
|
||||
title="빗물이용시설(현황)"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 현황", href: "/reuse/cases/rain" },
|
||||
{ label: "빗물이용시설(현황)" },
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 현황", href: "/reuse/cases/rain" },
|
||||
{ label: "빗물이용시설(현황)" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
<div className="pr-[100px]">
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 빗물이용시설의 건축물별 빗물이용량 현황 */}
|
||||
<section className="mt-6">
|
||||
<SectionTitle>빗물이용시설의 건축물별 빗물이용량 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainwaterBuilding} alt="빗물이용시설의 건축물별 빗물이용량 현황 설명" />
|
||||
</div>
|
||||
</section>
|
||||
{/* 1) 빗물이용시설의 건축물별 빗물이용량 현황 */}
|
||||
<section id="status-rain-building" className="mt-10 scroll-mt-[120px]">
|
||||
<SectionTitle>빗물이용시설의 건축물별 빗물이용량 현황</SectionTitle>
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>
|
||||
구분
|
||||
</th>
|
||||
<th colSpan={rainyYears.length || 6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>
|
||||
빗물이용량 (천m³/년)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rainyYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByBildTpYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rainyByBildTpYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 빗물이용시설의 용도별 분포 (2023년 기준) */}
|
||||
<section>
|
||||
<SectionTitle>빗물이용시설의 용도별 분포 (2023년 기준)</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainwaterUsage} alt="빗물이용시설의 용도별 분포 설명" />
|
||||
</div>
|
||||
</section>
|
||||
{/* 2) 빗물이용시설의 용도별 분포 (2023년 기준) */}
|
||||
<section id="status-rain-purpose-distribution" className="scroll-mt-[120px]">
|
||||
<SectionTitle>빗물이용시설의 용도별 분포 ({selectYear}년 기준)</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>용도</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "84px" }}>개소수<br />(개소)</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "84px" }}>저류용량<br />(천m³)</th>
|
||||
<th colSpan={8} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>빗물이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(partBildTpHeader, headerColors8)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByUsgBildTp.map((row, idx) => (
|
||||
<tr key={`${row.USG_NM}-${idx}`} className="bg-white">
|
||||
{renderRainyByUsgBildTpCells(row)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 빗물이용시설의 용도별 빗물이용량 현황 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>빗물이용시설의 용도별 빗물이용량 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={rainwaterUsageAmount} alt="빗물이용시설의 용도별 빗물이용량 현황 설명" />
|
||||
</div>
|
||||
</section>
|
||||
{/* 3) 빗물이용시설의 용도별 빗물이용량 현황 */}
|
||||
<section id="status-rain-purpose-usage" className="pb-[200px] scroll-mt-[120px]">
|
||||
<SectionTitle>빗물이용시설의 용도별 빗물이용량 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`} style={{ width: "160px" }}>용도</th>
|
||||
<th colSpan={6} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>빗물이용량 (천m³/년)</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{renderPartHeaders(rainyYears, headerColors6)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rainyByUsgYears.map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{renderBuildingUsageCells(row, rainyByUsgYearsHeader)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
// apps/dm/app/(landing)/reuse/cases/treated/page.tsx
|
||||
"use client";
|
||||
|
||||
import Image, { type StaticImageData } from "next/image";
|
||||
import React from "react";
|
||||
import SubPageHeader from "@/components/SubPageHeader";
|
||||
import treatedWaterYear from "@/public/images/landing/cases/treated_water_year.png";
|
||||
import treatedWaterUsage from "@/public/images/landing/cases/treated_water_usage.png";
|
||||
import treatedWaterFacility from "@/public/images/landing/cases/treated_water_facility.png";
|
||||
import treatedWaterRegion from "@/public/images/landing/cases/treated_water_region.png";
|
||||
|
||||
// 공통 섹션 타이틀 컴포넌트
|
||||
function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -17,86 +13,406 @@ function SectionTitle({ children }: { children: React.ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
type HrProps = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
function Hr({ className = "" }: HrProps) {
|
||||
// 구분선
|
||||
function Hr({ className = "" }: { className?: string }) {
|
||||
return <div className={`h-[2px] w-full bg-black/10 ${className}`} />;
|
||||
}
|
||||
|
||||
type ImgCardProps = {
|
||||
src: StaticImageData;
|
||||
alt: string;
|
||||
};
|
||||
|
||||
function ImgCard({ src, alt }: ImgCardProps) {
|
||||
// 출처 텍스트
|
||||
function SourceText() {
|
||||
return (
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Image
|
||||
src={src}
|
||||
alt={alt}
|
||||
className="block mx-auto max-w-none"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
<p className="mt-7 text-center text-[13px] text-neutral-500">
|
||||
자료: 환경부(2024), 「2023 하수도 통계」
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
export default function GreywaterPage() {
|
||||
/**
|
||||
* 공통 테이블 스타일
|
||||
* - table-fixed: 열 넓이를 고정하고 균등 분배하기 위해 추가
|
||||
* - 폰트 사이즈: 16px로 수정
|
||||
*/
|
||||
const tableStyles = {
|
||||
container: "w-[1000px] mx-auto overflow-hidden",
|
||||
table: "w-full table-fixed border-collapse text-center text-[16px]",
|
||||
th: "border border-[#858F98] py-3 font-bold text-neutral-800",
|
||||
td: "border border-[#858F98] py-3 text-neutral-700 font-medium break-keep",
|
||||
headerBg: "bg-[#E3E7EA]",
|
||||
};
|
||||
|
||||
// export default function TreatedWaterPage() {
|
||||
// return (
|
||||
// <section id={id} className={`scroll-mt-[140px] ${className}`}>
|
||||
// <SectionTitle>{title}</SectionTitle>
|
||||
// <div className="mt-8">{children}</div>
|
||||
// </section>
|
||||
// );
|
||||
// }
|
||||
|
||||
export default function TreatedStatusPage() {
|
||||
return (
|
||||
<div className="bg-white">
|
||||
<div className="relative bg-white">
|
||||
{/* <BusinessSubNav /> */}
|
||||
|
||||
<SubPageHeader
|
||||
title="하수처리수 재이용시설(현황)"
|
||||
crumbs={[
|
||||
{ label: "Home", href: "/reuse" },
|
||||
{ label: "물 재이용 현황", href: "/reuse/cases/rain" },
|
||||
{ label: "하수처리수 재이용시설(현황)" },
|
||||
{ label: "Home", href: "/main" },
|
||||
{ label: "물 재이용 현황", href: "/status/rain" },
|
||||
{ label: "하수처리수 재이용시설(현황)" },
|
||||
]}
|
||||
hideFooterLine
|
||||
hideGuideText
|
||||
wrapClassName="!pb-0"
|
||||
/>
|
||||
|
||||
<Hr className="my-7" />
|
||||
<div className="pr-0">
|
||||
<Hr className="my-7" />
|
||||
|
||||
{/* 1) 연도별 하수처리수 재이용 현황 */}
|
||||
<section className="mt-6">
|
||||
<section className="mt-10">
|
||||
<SectionTitle>연도별 하수처리수 재이용 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedWaterYear} alt="연도별 하수처리수 재이용 현황 설명" />
|
||||
<div className="mt-10">
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '130px' }} />
|
||||
<col style={{ width: '180px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "671", "681", "698", "704", "714", "717"],
|
||||
["하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "7,163.7", "7,140.4", "7,375.6", "7,335.8", "7,378.9", "7,537.2"],
|
||||
["재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "1,112.7", "1,148.7", "1,148.7", "1,127.0", "1,136.8", "1,137.5"],
|
||||
["재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>", "17.4", "16.5", "15.5", "14.1", "14.3", "14.4"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "68", "82", "94", "97", "101", "118"],
|
||||
["하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "761.2", "839.0", "935.3", "1,158.7", "1,220.6", "1,473.6"],
|
||||
["재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "162.2", "190.6", "218.5", "244.6", "247.2", "324.9"],
|
||||
["재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>", "24.7", "25.4", "27.8", "25.7", "24.2", "24.1"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
["개소수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "603", "599", "604", "607", "613", "599"],
|
||||
["하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "6,402.5", "6,301.4", "6,440.3", "6,177.1", "6,158.3", "6,063.5"],
|
||||
["재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>", "950.6", "958.1", "926.2", "882.4", "889.5", "812.7"],
|
||||
["재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>", "16.5", "15.3", "13.6", "12.2", "12.6", "12.5"],
|
||||
]
|
||||
}
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${isFirstCol ? "!font-bold !text-neutral-900" : ""} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 2) 용도별 하수처리수 재이용 현황 */}
|
||||
<section>
|
||||
<SectionTitle>용도별 하수처리수 재이용 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedWaterUsage} alt="용도별 하수처리수 재이용 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '180px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{ label: "총 재이용량", type: "total", values: ["1,112.7", "1,148.7", "1,144.7", "1,127.0", "1,136.8", "1,137.5"] },
|
||||
{ label: "장내 재이용", type: "category", values: ["520.6", "519.8", "513.9", "520.3", "502.2", "490.0"] },
|
||||
{ label: "세척수", type: "red-item", values: ["177.9", "174.4", "169.4", "180.9", "196.8", "191.2"] },
|
||||
{ label: "냉각수", type: "red-item", values: ["45.5", "46.8", "50.4", "40.6", "38.3", "41.7"] },
|
||||
{ label: "청소수", type: "red-item", values: ["17.6", "21.2", "21.2", "21.7", "20.8", "20.5"] },
|
||||
{ label: "조경용수", type: "red-item", values: ["0.1", "0.1", "-", "-", "-", "-"] },
|
||||
{ label: "회생용수", type: "red-item", values: ["15.8", "16.7", "17.8", "15.7", "14.4", "14.0"] },
|
||||
{ label: "장내급수도", type: "red-item", values: ["2.4", "-", "-", "-", "-", "-"] },
|
||||
{ label: "장내기타용수", type: "red-item", values: ["261.3", "260.6", "255.0", "261.1", "231.8", "222.6"] },
|
||||
{ label: "장외 재이용", type: "category", values: ["592.1", "628.9", "630.8", "606.7", "634.6", "647.5"] },
|
||||
{ label: "청소·화장실용수", type: "blue-item", values: ["0.6", "0.4", "0.4", "0.4", "0.4", "0.4"] },
|
||||
{ label: "세척·살수용수", type: "blue-item", values: ["5.6", "9.9", "2.9", "2.1", "3.0", "3.6"] },
|
||||
{ label: "조경용수", type: "blue-item", values: ["10.3", "16.4", "5.9", "4.5", "8.9", "9.5"] },
|
||||
{ label: "친수용수", type: "blue-item", values: ["7.8", "3.1", "3.1", "3.0", "10.9", "10.5"] },
|
||||
{ label: "하천유지용수", type: "blue-item", values: ["480.5", "482.5", "496.2", "442.7", "471.7", "484.0"] },
|
||||
{ label: "농업용수", type: "blue-item", values: ["12.2", "11.9", "13.4", "10.7", "10.6", "11.4"] },
|
||||
{ label: "지하수 충전", type: "blue-item", values: ["0.0", "0.0", "0.0", "13.4", "0.0", "0.0"] },
|
||||
{ label: "공업용수", type: "blue-item", values: ["75.2", "104.6", "108.9", "129.9", "129.0", "128.1"] },
|
||||
].map((row, idx) => {
|
||||
const isSubItem = row.type === "red-item" || row.type === "blue-item";
|
||||
const isLastInGroup = idx === 8 || idx === 17;
|
||||
|
||||
return (
|
||||
<tr key={idx} className="bg-white">
|
||||
<td className={`${tableStyles.td} relative`}>
|
||||
{isSubItem ? (
|
||||
<>
|
||||
<div className={`absolute left-[1px] w-[16px] bg-white z-10 top-[-2px] ${isLastInGroup ? 'bottom-[0px]' : 'bottom-[-2px]'}`} />
|
||||
<div className={`absolute left-[16px] w-[4px] z-20 ${row.type === 'red-item' ? 'bg-[#FF8B8B]' : 'bg-[#4ECBFF]'} top-[-2px] ${isLastInGroup ? 'bottom-[0px]' : 'bottom-[-2px]'}`} />
|
||||
<span className="text-[#4f4f4f] font-bold block w-full text-center relative z-30 translate-x-[8px]">{row.label}</span>
|
||||
</>
|
||||
) : (
|
||||
<span className="font-bold text-neutral-900 block w-full text-center">{row.label}</span>
|
||||
)}
|
||||
</td>
|
||||
{row.values.map((val, i) => (
|
||||
<td key={i} className={`${tableStyles.td} ${[0, 2, 4].includes(i) ? "bg-[#EAF5F5]" : ""}`}>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 3) 하수처리시설 설치 및 이용 현황 */}
|
||||
<section>
|
||||
<SectionTitle>하수처리시설 설치 및 이용 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedWaterFacility} alt="하수처리시설 설치 및 이용 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '130px' }} />
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>구분</th>
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white`}>2018</th>
|
||||
<th className={`${tableStyles.th} bg-[#14AFCD] text-white`}>2019</th>
|
||||
<th className={`${tableStyles.th} bg-[#28B2B4] text-white`}>2020</th>
|
||||
<th className={`${tableStyles.th} bg-[#3CB59A] text-white`}>2021</th>
|
||||
<th className={`${tableStyles.th} bg-[#50B880] text-white`}>2022</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white`}>2023</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
{
|
||||
group: "전체",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "671", "681", "698", "704", "714", "717"],
|
||||
["시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "25,846", "26,067", "26,244", "26,492", "26,586", "26,766"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "1,112.7", "1,148.7", "1,144.7", "1,127.0", "1,136.8", "1,137.5"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "68", "82", "94", "97", "101", "118"],
|
||||
["시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "3,088.9", "3,359.2", "3,612.6", "4,412.2", "4,692.3", "5,503.8"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "162.2", "190.6", "218.5", "244.6", "247.2", "324.9"],
|
||||
]
|
||||
},
|
||||
{
|
||||
group: "법적 미대상",
|
||||
rows: [
|
||||
["시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>", "603", "599", "604", "607", "613", "599"],
|
||||
["시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "22,757", "22,708", "22,631", "22,080", "21,894", "21,262"],
|
||||
["처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", "950.6", "958.1", "926.2", "882.4", "889.5", "812.7"],
|
||||
]
|
||||
}
|
||||
].map((section, sIdx) => (
|
||||
<React.Fragment key={sIdx}>
|
||||
{section.rows.map((row, rIdx) => (
|
||||
<tr key={`${sIdx}-${rIdx}`} className="bg-white">
|
||||
{rIdx === 0 && (
|
||||
<td
|
||||
rowSpan={section.rows.length}
|
||||
className={`${tableStyles.td} ${tableStyles.headerBg} !text-[18px] !font-bold !text-neutral-900`}
|
||||
>
|
||||
{section.group}
|
||||
</td>
|
||||
)}
|
||||
{row.map((val, i) => {
|
||||
const isFirstCol = i === 0;
|
||||
const isHighlightedCol = [1, 3, 5].includes(i);
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${isFirstCol ? "!font-bold !text-neutral-900" : ""} ${isHighlightedCol ? "bg-[#EAF5F5]" : ""}`}
|
||||
dangerouslySetInnerHTML={{ __html: val }}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<Hr className="my-11" />
|
||||
<Hr className="my-11" />
|
||||
|
||||
{/* 4) 시도별 하수처리수 재이용 현황 */}
|
||||
{/* 4) 시도별 하수처리수 재이용 현황 */}
|
||||
<section className="pb-[200px]">
|
||||
<SectionTitle>시도별 하수처리수 재이용 현황</SectionTitle>
|
||||
<div className="mt-8">
|
||||
<ImgCard src={treatedWaterRegion} alt="시도별 하수처리수 재이용 현황 설명" />
|
||||
<div className={tableStyles.container}>
|
||||
<div className="text-right text-[13px] mb-2 text-neutral-600">(단위: 백만m³/일)</div>
|
||||
<table className={tableStyles.table}>
|
||||
<colgroup>
|
||||
<col style={{ width: '160px' }} />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
<col />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`}>시도</th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`}>연간<br/>하수<br/>처리량</th>
|
||||
<th colSpan={7} className={`${tableStyles.th} ${tableStyles.headerBg} py-2`}>재이용현황</th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`}>재이용률<br/>(%)</th>
|
||||
<th rowSpan={3} className={`${tableStyles.th} ${tableStyles.headerBg}`}>비고</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>계</th>
|
||||
<th rowSpan={2} className={`${tableStyles.th} ${tableStyles.headerBg}`}>장내</th>
|
||||
<th colSpan={5} className={`${tableStyles.th} ${tableStyles.headerBg} py-1`}>장외</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* 💡 하천유지용수는 14px, 나머지 하위 헤더는 16px로 적용 */}
|
||||
<th className={`${tableStyles.th} bg-[#01ACE5] text-white py-2 text-[16px]`}>소계</th>
|
||||
<th className={`${tableStyles.th} bg-[#1FB1C2] text-white py-2 text-[13px]`}>하천유지용수</th>
|
||||
<th className={`${tableStyles.th} bg-[#3EB59F] text-white py-2 text-[16px]`}>공업용수</th>
|
||||
<th className={`${tableStyles.th} bg-[#5DB97C] text-white py-2 text-[16px]`}>농업용수</th>
|
||||
<th className={`${tableStyles.th} bg-[#6DBB4F] text-white py-2 text-[16px]`}>기타용수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[
|
||||
["합계", "7,537.2", "1,137.5", "490.0", "647.5", "484.0", "128.1", "11.4", "24.0", "15.1%", ""],
|
||||
["서울특별시", "1,567.0", "114.9", "59.3", "55.6", "55.1", "-", "-", "0.5", "7.3%", ""],
|
||||
["부산광역시", "491.5", "146.2", "120.7", "25.5", "25.3", "-", "-", "0.2", "29.7%", ""],
|
||||
["대구광역시", "432.7", "102.4", "38.2", "64.2", "64.1", "-", "-", "0.1", "23.7%", ""],
|
||||
["인천광역시", "302.3", "57.7", "11.6", "46.1", "29.2", "12.1", "0.5", "4.3", "19.1%", ""],
|
||||
["광주광역시", "211.1", "28.7", "6.5", "22.3", "22.2", "-", "-", "0.1", "13.6%", ""],
|
||||
["대전광역시", "244.6", "11.9", "11.9", "0.68", "0.0", "-", "-", "0.1", "4.9%", ""],
|
||||
["울산광역시", "235.1", "36.1", "6.1", "29.9", "29.7", "0.2", "-", "-", "15.3%", ""],
|
||||
["세종특별자치시", "38.5", "2.5", "2.0", "0.5", "0.4", "-", "0.2", "-", "6.6%", ""],
|
||||
["경기도", "1,926.8", "313.3", "122.2", "191.0", "153.0", "33.3", "0.4", "4.3", "16.3%", ""],
|
||||
["강원도", "230.4", "19.6", "15.4", "4.2", "2.1", "-", "-", "2.1", "8.5%", ""],
|
||||
["충청북도", "190.9", "15.6", "6.3", "9.2", "7.4", "0.6", "-", "1.2", "8.2%", ""],
|
||||
["충청남도", "241.5", "41.4", "9.8", "31.5", "17.7", "8.6", "4.5", "0.8", "17.1%", ""],
|
||||
["전라북도", "292.4", "42.7", "7.5", "35.1", "33.5", "0.2", "1.0", "0.5", "14.6%", ""],
|
||||
["전라남도", "254.0", "23.6", "12.3", "11.3", "8.7", "0.0", "1.0", "1.6", "9.3%", ""],
|
||||
["경상북도", "378.4", "129.1", "33.7", "95.3", "20.8", "72.5", "0.8", "1.3", "34.1%", ""],
|
||||
["경상남도", "411.3", "50.1", "25.0", "25.0", "14.7", "0.5", "3.0", "6.9", "12.2%", ""],
|
||||
["제주특별자치도", "88.6", "1.7", "1.5", "0.12", "-", "-", "0.1", "-", "1.9%", ""],
|
||||
].map((row, idx) => (
|
||||
<tr key={idx} className="bg-white">
|
||||
{/* 💡 연간하수처리량(index 1), 장내(index 3), 재이용률(index 9) 컬러 하이라이트 적용 */}
|
||||
{row.map((val, i) => (
|
||||
<td
|
||||
key={i}
|
||||
className={`${tableStyles.td} ${i === 0 ? "!font-bold !text-neutral-900" : ""} ${[1, 3, 9].includes(i) ? "bg-[#EAF5F5]" : ""}`}
|
||||
>
|
||||
{val}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<SourceText />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -23,4 +23,34 @@ export default function TransValueFormat({value='', format=''}: CommonProps) {
|
||||
: String(value ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")}</>
|
||||
: <>{value}</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 천,만 등의 단위 변경 && 반올림 및 소숫점 자리수 지정
|
||||
* @param value
|
||||
* @param unitSize 단위 변경 1000 인 경우, 100,000 -> 100 으로 변경
|
||||
* @param decimalPlaces 어느 자리수에서 반올림할지 지정 1이면 소수 두번째 자리에서 반올림
|
||||
* @returns
|
||||
*/
|
||||
export const formatUsage = (
|
||||
value: string | number,
|
||||
unitSize: number = 1,
|
||||
decimalPlaces: number = 0
|
||||
) => {
|
||||
if (value === null || value === undefined || value === "") return "";
|
||||
const numeric =
|
||||
typeof value === "number" ? value : Number(String(value).replaceAll(",", ""));
|
||||
|
||||
if (Number.isNaN(numeric)) {
|
||||
return String(value);
|
||||
}
|
||||
|
||||
const scaledValue = numeric / unitSize;
|
||||
const roundedValue =
|
||||
Math.round(scaledValue * 10 ** decimalPlaces) / 10 ** decimalPlaces;
|
||||
|
||||
return roundedValue.toLocaleString("ko-KR", {
|
||||
minimumFractionDigits: decimalPlaces,
|
||||
maximumFractionDigits: decimalPlaces,
|
||||
});
|
||||
};
|
||||
@@ -16,7 +16,7 @@ export default function RootLayout({
|
||||
<title>물재이용 대민</title>
|
||||
</head>
|
||||
<body>
|
||||
<div className="min-w-[1800px] overflow-x-auto">
|
||||
<div className="min-w-[1400px] overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function Cases() {
|
||||
|
||||
<div className="mt-14">
|
||||
<a
|
||||
href="/reuse/cases/rain"
|
||||
href="/status"
|
||||
className="
|
||||
inline-flex items-center justify-center gap-3
|
||||
w-[260px] h-[52px]
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function Hero() {
|
||||
<div className="absolute inset-0 z-10 flex items-center justify-center">
|
||||
<div className="flex flex-col items-center justify-items-center gap-[40px] mx-auto max-w-[1520px] px-6 text-center text-white">
|
||||
<h1 className="text-5xl font-bold tracking-tight">물 재이용</h1>
|
||||
<p className="text-xl font-medium leading-6 text-white/85 max-w-[720px] mx-auto">
|
||||
<p className="text-2xl font-medium leading-8 text-white/85 max-w-[720px] mx-auto">
|
||||
빗물, 오수, 하수처리수, 폐수처리수, 발전소 온배수를 재이용처리하여<br />
|
||||
생활, 공업, 농업, 조경, 하천유지 등의 용도로 재이용합니다.
|
||||
</p>
|
||||
|
||||
@@ -20,25 +20,25 @@ const INTRO_CARDS: IntroCardItem[] = [
|
||||
title: "물 재이용 법",
|
||||
desc: "물의 재이용 촉진 및 지원에 관한 법률을 시행하고, 일부 법률은 개정되었습니다.",
|
||||
image: card01,
|
||||
href: "/reuse/intro/law",
|
||||
href: "/intro#law-plan",
|
||||
},
|
||||
{
|
||||
title: "친환경 수자원 빗물",
|
||||
desc: "가뭄을 극복하고 홍수를 예방하기 위해 비가 내릴 때 빗물을 모아두었다가 사용합니다.",
|
||||
image: card02,
|
||||
href: "/reuse/intro/rain",
|
||||
href: "/intro#rain-intro",
|
||||
},
|
||||
{
|
||||
title: "알뜰하게 재이용되는 중수",
|
||||
desc: "한번 사용한 물을 중수도 시설을 통해 재생하여 개별적 또는 지역적으로 재이용할 수 있습니다.",
|
||||
image: card03,
|
||||
href: "/reuse/intro/greywater",
|
||||
href: "/intro#greywater-intro",
|
||||
},
|
||||
{
|
||||
title: "하수처리수 재이용",
|
||||
desc: "사용하고 버려진 공공하수처리장으로 모여진 물을 용도에 맞는 처리 후 재이용 할 수 있습니다.",
|
||||
image: card04,
|
||||
href: "/reuse/intro/treated",
|
||||
href: "/intro#treatedwater-intro",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function IntroCards() {
|
||||
<h3 className="text-lg font-semibold tracking-tight">
|
||||
{card.title}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-white/90 line-clamp-3">
|
||||
<p className="mt-2 text-sm leading-relaxed text-white/60 line-clamp-3">
|
||||
{card.desc}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// apps/dm/app/(landing)/reuse/page.tsx
|
||||
/**
|
||||
* 대민 메인 페이지(첫페이지)
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import TopNav from "@/components/TopNav";
|
||||
import { removeCookie } from "@/src/shared/lib";
|
||||
import SideIndicator from "./SideIndicator";
|
||||
import { useScrollSpy } from "./_hooks/useScrollSpy";
|
||||
|
||||
@@ -11,6 +14,9 @@ import IntroCards from "./_sections/IntroCards";
|
||||
import Cases from "./_sections/Cases";
|
||||
import ProcessFlow from "./_sections/ProcessFlow";
|
||||
|
||||
const CLAIM_NAME = "Authorization";
|
||||
const AUTH_USER_ID = "token-id";
|
||||
|
||||
const LANDING_SECTIONS = [
|
||||
{ id: "main", label: "Main" },
|
||||
{
|
||||
@@ -34,6 +40,11 @@ export default function ReuseLandingPage() {
|
||||
const sectionIds = useMemo(() => LANDING_SECTIONS.map((s) => s.id), []);
|
||||
const activeId = useScrollSpy(sectionIds);
|
||||
|
||||
useEffect(() => {
|
||||
removeCookie(CLAIM_NAME);
|
||||
removeCookie(AUTH_USER_ID);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="bg-white">
|
||||
|
||||
|
||||
@@ -496,7 +496,7 @@ export const CommonReducer:(set:any)=>CommonAction=(set: any) => {
|
||||
}
|
||||
})
|
||||
)},
|
||||
fetchAnonymousMenu: async (siteId = REUSE_SITE_ID) => fetchAnonymousMenu(siteId)
|
||||
fetchAnonymousMenu: async (siteId = REUSE_SITE_ID) => fetchAnonymousMenu(siteId),
|
||||
}
|
||||
}
|
||||
export type CommonStore = CommonType & CommonAction;
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import { fetchType, getFetch } from "@/components/ui/api";
|
||||
import type {
|
||||
TableHeaderType, DmStatusBildTpYearsType, DmStatusUsgBildTpType, DmStatusUsgYearsType,
|
||||
DmStatusLawYearsType, DmStatusSidoUsgType,
|
||||
} from "./type";
|
||||
import { rainyByBildTpYearsHeader, rainyByUsgBildTpHeader, rainyByUsgYearsHeader } from "./type";
|
||||
import { rswtrByLawYearsHeader, rswtrByBildTpYearsHeader, rswtrByUsgYearsHeader } from "./type";
|
||||
import { pbnstByLawYearsHeader, pbnstByUsgYearsHeader, pbnstByCpcYearsHeader, pbnstBySidoHeader } from "./type";
|
||||
|
||||
|
||||
export type DmStatusActions = {
|
||||
getRainyByBildTpYears: () => Promise<void>; // 1.대민 빗물재이용현황 건축물유형별 최근 6년간
|
||||
getRainyByUsgBildTp: () => Promise<void>; // 2.대민 빗물재이용현황 이용용도별 건축물별
|
||||
getRainyByUsgYears: () => Promise<void>; // 3.대민 빗물재이용현황 이용용도별 최근 6년간
|
||||
getRswtrByLawYears: (bildTpCond: string) => Promise<void>; // 4.대민 중수도재이용현황 법적대상별 최근 6년간
|
||||
getRswtrByBildTpYears: () => Promise<void>; // 5.대민 중수도재이용현황 건축물별 최근 6년간
|
||||
getRswtrByUsgYears: () => Promise<void>; // 6.대민 중수도재이용현황 이용용도별 최근 6년간
|
||||
getPbnstByLawYears: () => Promise<void>; // 7.대민 하수재이용현황 법적대상별 최근 6년간
|
||||
getPbnstByUsgYears: () => Promise<void>; // 8.대민 하수재이용현황 이용용도별 최근 6년간
|
||||
getPbnstByCpcYears: () => Promise<void>; // 9.대민 하수재이용현황 용량 최근 6년간
|
||||
getPbnstBySido: () => Promise<void>; // 10.대민 하수재이용현황 시도별
|
||||
};
|
||||
|
||||
export type DmStatusType = {
|
||||
rainy: {
|
||||
rainyYears: string[];
|
||||
rainyByBildTpYearsHeader: TableHeaderType[];
|
||||
rainyByUsgBildTpHeader: TableHeaderType[];
|
||||
rainyByUsgYearsHeader: TableHeaderType[];
|
||||
rainyByBildTpYears: DmStatusBildTpYearsType[];
|
||||
rainyByUsgBildTp: DmStatusUsgBildTpType[];
|
||||
rainyByUsgYears: DmStatusUsgYearsType[];
|
||||
},
|
||||
rswtr: {
|
||||
rswtrYears: string[];
|
||||
rswtrByLawYearsHeader: TableHeaderType[];
|
||||
rswtrByBildTpYearsHeader: TableHeaderType[];
|
||||
rswtrByUsgYearsHeader: TableHeaderType[];
|
||||
rswtrByLawYearsFactory: DmStatusLawYearsType[];
|
||||
rswtrByLawYears: DmStatusLawYearsType[];
|
||||
rswtrByBildTpYears: DmStatusBildTpYearsType[];
|
||||
rswtrByUsgYears: DmStatusUsgYearsType[];
|
||||
},
|
||||
pbnst: {
|
||||
pbnstYears: string[];
|
||||
pbnstByLawYearsHeader: TableHeaderType[];
|
||||
pbnstByUsgYearsHeader: TableHeaderType[];
|
||||
pbnstByCpcYearsHeader: TableHeaderType[];
|
||||
pbnstBySidoHeader: TableHeaderType[];
|
||||
pbnstByLawYears: DmStatusLawYearsType[];
|
||||
pbnstByUsgYears: DmStatusUsgYearsType[];
|
||||
pbnstByCpcYears: DmStatusLawYearsType[];
|
||||
pbnstBySido: DmStatusSidoUsgType[];
|
||||
}
|
||||
};
|
||||
|
||||
export const dmStatusState: DmStatusType = {
|
||||
rainy: {
|
||||
rainyYears: ["2019", "2020", "2021", "2022", "2023", "2024"],
|
||||
rainyByBildTpYearsHeader: rainyByBildTpYearsHeader,
|
||||
rainyByUsgBildTpHeader: rainyByUsgBildTpHeader,
|
||||
rainyByUsgYearsHeader: rainyByUsgYearsHeader,
|
||||
rainyByBildTpYears: [],
|
||||
rainyByUsgBildTp: [],
|
||||
rainyByUsgYears: [],
|
||||
},
|
||||
rswtr: {
|
||||
rswtrYears: ["2019", "2020", "2021", "2022", "2023", "2024"],
|
||||
rswtrByLawYearsHeader: rswtrByLawYearsHeader,
|
||||
rswtrByBildTpYearsHeader: rswtrByBildTpYearsHeader,
|
||||
rswtrByUsgYearsHeader: rswtrByUsgYearsHeader,
|
||||
rswtrByLawYearsFactory: [], // 공장 및 발전시설 내 중수도 설치 현황
|
||||
rswtrByLawYears: [], // 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)
|
||||
rswtrByBildTpYears: [],
|
||||
rswtrByUsgYears: [],
|
||||
},
|
||||
pbnst: {
|
||||
pbnstYears: ["2019", "2020", "2021", "2022", "2023", "2024"],
|
||||
pbnstByLawYearsHeader: pbnstByLawYearsHeader,
|
||||
pbnstByUsgYearsHeader: pbnstByUsgYearsHeader,
|
||||
pbnstByCpcYearsHeader: pbnstByCpcYearsHeader,
|
||||
pbnstBySidoHeader: pbnstBySidoHeader,
|
||||
pbnstByLawYears: [],
|
||||
pbnstByUsgYears: [],
|
||||
pbnstByCpcYears: [],
|
||||
pbnstBySido: [],
|
||||
}
|
||||
};
|
||||
|
||||
export type DmStatusStore = DmStatusType & DmStatusActions;
|
||||
|
||||
export const DmStatusReducer = (set: any): DmStatusActions => {
|
||||
return {
|
||||
// 1. 대민 빗물재이용현황 건축물유형별 최근 6년간
|
||||
getRainyByBildTpYears: async () => {
|
||||
const res = await getFetch(fetchType.WR, "/dmStatus/rainyByBildTpYears");
|
||||
const rainyByBildTpYears = Array.isArray(res?.data) ? res.data : [];
|
||||
const rainyYears = (rainyByBildTpYears[0]?.YEAR ?? "")
|
||||
.split(",")
|
||||
.map((year) => year.trim())
|
||||
.filter(Boolean);
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rainy: {
|
||||
...state.rainy,
|
||||
rainyByBildTpYears,
|
||||
rainyYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 2.대민 빗물재이용현황 이용용도별 건축물별
|
||||
getRainyByUsgBildTp: async () => {
|
||||
const res = await getFetch(fetchType.WR, "/dmStatus/rainyByUsgBildTp");
|
||||
const rainyByUsgBildTp = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rainy: {
|
||||
...state.rainy,
|
||||
rainyByUsgBildTp,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 3.대민 빗물재이용현황 이용용도별 최근 6년간
|
||||
getRainyByUsgYears: async () => {
|
||||
const res = await getFetch(fetchType.WR, "/dmStatus/rainyByUsgYears");
|
||||
const rainyByUsgYears = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rainy: {
|
||||
...state.rainy,
|
||||
rainyByUsgYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 4.대민 중수도재이용현황 법적대상별 최근 6년간
|
||||
getRswtrByLawYears: async (bildTpCond: string) => {
|
||||
const res = await getFetch(fetchType.WR,`/dmStatus/rswtrByLawYears/${bildTpCond}`);
|
||||
const rswtrByLawYears = Array.isArray(res?.data) ? res.data : [];
|
||||
const maxYear = Number(rswtrByLawYears[0]?.MAXYR);
|
||||
const rswtrYears = Number.isFinite(maxYear)
|
||||
? Array.from({ length: 6 }, (_, index) => String(maxYear - 5 + index))
|
||||
: dmStatusState.rswtr.rswtrYears;
|
||||
|
||||
if (bildTpCond === "factory") {
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rswtr: {
|
||||
...state.rswtr,
|
||||
rswtrYears,
|
||||
rswtrByLawYearsFactory: rswtrByLawYears,
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rswtr: {
|
||||
...state.rswtr,
|
||||
rswtrYears,
|
||||
rswtrByLawYears,
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
// 5.대민 중수도재이용현황 건축물별 최근 6년간
|
||||
getRswtrByBildTpYears: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/rswtrByBildTpYears");
|
||||
const rswtrByBildTpYears = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rswtr: {
|
||||
...state.rswtr,
|
||||
rswtrByBildTpYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 6.대민 중수도재이용현황 이용용도별 최근 6년간
|
||||
getRswtrByUsgYears: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/rswtrByUsgYears");
|
||||
const rswtrByUsgYears = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
rswtr: {
|
||||
...state.rswtr,
|
||||
rswtrByUsgYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 7.대민 하수재이용현황 법적대상별 최근 6년간
|
||||
getPbnstByLawYears: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/pbnstByLawYears");
|
||||
const pbnstByLawYears = Array.isArray(res?.data) ? res.data : [];
|
||||
const maxYear = Number(pbnstByLawYears[0]?.MAXYR);
|
||||
const pbnstYears = Number.isFinite(maxYear)
|
||||
? Array.from({ length: 6 }, (_, index) => String(maxYear - 5 + index))
|
||||
: dmStatusState.rswtr.rswtrYears;
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
pbnst: {
|
||||
...state.pbnst,
|
||||
pbnstYears,
|
||||
pbnstByLawYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 8.대민 하수재이용현황 이용용도별 최근 6년간
|
||||
getPbnstByUsgYears: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/pbnstByUsgYears");
|
||||
const pbnstByUsgYears = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
pbnst: {
|
||||
...state.pbnst,
|
||||
pbnstByUsgYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 9.대민 하수재이용현황 용량 최근 6년간
|
||||
getPbnstByCpcYears: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/pbnstByCpcYears");
|
||||
const pbnstByCpcYears = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
pbnst: {
|
||||
...state.pbnst,
|
||||
pbnstByCpcYears,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
// 10.대민 하수재이용현황 시도별
|
||||
getPbnstBySido: async () => {
|
||||
const res = await getFetch(fetchType.WR,"/dmStatus/pbnstBySido");
|
||||
const pbnstBySido = Array.isArray(res?.data) ? res.data : [];
|
||||
|
||||
set((state: DmStatusType) => ({
|
||||
...state,
|
||||
pbnst: {
|
||||
...state.pbnst,
|
||||
pbnstBySido,
|
||||
}
|
||||
}));
|
||||
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,285 @@
|
||||
export type TableHeaderType = {
|
||||
id: string;
|
||||
title: string;
|
||||
upName?: string;
|
||||
upSequnce?: number;
|
||||
align?: string;
|
||||
row?: number;
|
||||
rowSpan?: number;
|
||||
colSpan?: number;
|
||||
widthPercent?: number;
|
||||
}
|
||||
|
||||
export type DmStatusBildTpYearsType = {
|
||||
BILD_TP: string; // 건물구분
|
||||
YEAR?: string; // 가로 연도 목록
|
||||
Y1: string | number;
|
||||
Y2: string | number;
|
||||
Y3: string | number;
|
||||
Y4: string | number;
|
||||
Y5: string | number;
|
||||
Y6: string | number;
|
||||
MAXYR?: string; // 최종 확정 연도
|
||||
};
|
||||
|
||||
export type DmStatusUsgBildTpType = {
|
||||
USG_NM: string;
|
||||
CNT: string | number;
|
||||
USEQTY: string | number;
|
||||
SGBS_CPCTY: string | number;
|
||||
PUB_FCT: string | number;
|
||||
COMM_HOUSE: string | number;
|
||||
MEGA_STORE: string | number;
|
||||
GYM_FCT: string | number;
|
||||
SCHOOL: string | number;
|
||||
GOLF: string | number;
|
||||
ETC: string | number;
|
||||
};
|
||||
|
||||
export type DmStatusUsgYearsType = {
|
||||
USG_NM: string; // 용도
|
||||
YEAR?: string; // 가로 연도 목록
|
||||
Y1: string | number;
|
||||
Y2: string | number;
|
||||
Y3: string | number;
|
||||
Y4: string | number;
|
||||
Y5: string | number;
|
||||
Y6: string | number;
|
||||
COL_NM?: string; // 컬럼명
|
||||
MAXYR?: string; // 최종 확정 연도
|
||||
};
|
||||
|
||||
export type DmStatusLawYearsType = {
|
||||
COL1: string; // 전체, 법적 대상, 법적 미대상
|
||||
COL2: string; // 구분
|
||||
MAXYR?: string; // 최종 확정 연도
|
||||
Y1: string | number;
|
||||
Y2: string | number;
|
||||
Y3: string | number;
|
||||
Y4: string | number;
|
||||
Y5: string | number;
|
||||
Y6: string | number;
|
||||
};
|
||||
|
||||
export type DmStatusSidoUsgType = {
|
||||
ADST_CD: string; // 시도 코드
|
||||
SIDONM: string; // 시도명
|
||||
TPUT_TOTAL: string; // 연간하수처리량(천톤/년)
|
||||
USE_AMNT_TOTAL: number; // 재이용현황
|
||||
USE_AMNT_D01: number;
|
||||
USE_AMNT_D02: number;
|
||||
USE_AMNT_S11: number; // 청소·화장실용수
|
||||
USE_AMNT_S12: number; // 세척·살수용수
|
||||
USE_AMNT_S13: number; // 조경용수
|
||||
USE_AMNT_S14: number; // 친수용수
|
||||
USE_AMNT_S15: number; // 하천유지용수
|
||||
USE_AMNT_S16: number; // 농업용수
|
||||
USE_AMNT_S17: number; // 지하수충전
|
||||
USE_AMNT_S18: number; // 공업용수
|
||||
USE_AMNT_ETC: number; // 기타 (청소·화장실용수+세척·살수용수+조경용수+친수용수+지하수충전)
|
||||
REUSE_RT: number;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// 빗물이용시설의 건축물별 빗물이용량 현황
|
||||
export const rainyByBildTpYearsHeader: TableHeaderType[] = [
|
||||
{ id: "BILD_TP", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
export const rainyByBildTpYearsInit: DmStatusBildTpYearsType[] = [
|
||||
{ BILD_TP: "골프장", Y1: "6,771.8", Y2: "6,417.4", Y3: "8,371.6", Y4: "10,215.4", Y5: "11,360", Y6: "10,191.6" },
|
||||
{ BILD_TP: "공공시설", Y1: "364", Y2: "325.2", Y3: "201.9", Y4: "244.6", Y5: "209.9", Y6: "299.6" },
|
||||
{ BILD_TP: "공동주택", Y1: "291", Y2: "840.5", Y3: "804.2", Y4: "2,393.0", Y5: "2,386", Y6: "2,428.4" },
|
||||
{ BILD_TP: "대규모점포", Y1: "169.9", Y2: "214.4", Y3: "58.4", Y4: "427.2", Y5: "371", Y6: "293.3" },
|
||||
{ BILD_TP: "체육시설", Y1: "52.6", Y2: "44.7", Y3: "48.6", Y4: "75.3", Y5: "71.8", Y6: "77.6" },
|
||||
{ BILD_TP: "학교", Y1: "33", Y2: "25.5", Y3: "22", Y4: "155.8", Y5: "165.4", Y6: "32.6" },
|
||||
{ BILD_TP: "기타", Y1: "311.6", Y2: "901.2", Y3: "344.5", Y4: "537.8", Y5: "564", Y6: "621.2" },
|
||||
];
|
||||
// 빗물이용시설의 용도별 분포
|
||||
export const rainyByUsgBildTpHeader: TableHeaderType[] = [
|
||||
{ id: "USG_NM", title: "용도", row: 0, widthPercent: 15 },
|
||||
{ id: "CNT", title: "개소수", row: 1, widthPercent: 5 },
|
||||
{ id: "SGBS_CPCTY", title: "저류용량", row: 1, widthPercent: 5 },
|
||||
{ id: "USEQTY", title: "총사용량", row: 1, widthPercent: 5 },
|
||||
{ id: "PUB_FCT", title: "공공시설", row: 1, widthPercent: 5 },
|
||||
{ id: "COMM_HOUSE", title: "공동주택", row: 0, widthPercent: 7 },
|
||||
{ id: "MEGA_STORE", title: "대규모점포", row: 0, widthPercent: 10 },
|
||||
{ id: "GYM_FCT", title: "체육시설", row: 1, widthPercent: 5 },
|
||||
{ id: "SCHOOL", title: "학교", row: 1, widthPercent: 5 },
|
||||
{ id: "GOLF", title: "골프장", row: 1, widthPercent: 5 },
|
||||
{ id: "ETC", title: "기타", row: 1, widthPercent: 5 },
|
||||
];
|
||||
export const rainyByUsgBildTpInit: DmStatusUsgBildTpType[] = [
|
||||
{ USG_NM: "청소·환경정화용수", CNT: "195", SGBS_CPCTY: "32.7", USEQTY: "108.8", PUB_FCT: "24.9", COMM_HOUSE: "5.8", MEGA_STORE: "2.6", GYM_FCT: "4.4", SCHOOL: "2.9", GOLF: "-", ETC: "68.1" },
|
||||
{ USG_NM: "세척·살수용수", CNT: "12", SGBS_CPCTY: "1.4", USEQTY: "37", PUB_FCT: "35", COMM_HOUSE: "-", MEGA_STORE: "-", GYM_FCT: "1.9", SCHOOL: "-", GOLF: "-", ETC: "0.2" },
|
||||
{ USG_NM: "조경용수", CNT: "3,165", SGBS_CPCTY: "7,440.8", USEQTY: "13,554.1", PUB_FCT: "168.1", COMM_HOUSE: "2,421.9", MEGA_STORE: "290.7", GYM_FCT: "66.2", SCHOOL: "29", GOLF: "10,044", ETC: "534.3" },
|
||||
{ USG_NM: "냉·난방수", CNT: "8", SGBS_CPCTY: "1.6", USEQTY: "0.1", PUB_FCT: "-", COMM_HOUSE: "-", MEGA_STORE: "-", GYM_FCT: "-", SCHOOL: "0.1", GOLF: "-", ETC: "-" },
|
||||
{ USG_NM: "공업용수", CNT: "6", SGBS_CPCTY: "14.9", USEQTY: "53.2", PUB_FCT: "52.8", COMM_HOUSE: "-", MEGA_STORE: "-", GYM_FCT: "-", SCHOOL: "-", GOLF: "-", ETC: "0.4" },
|
||||
{ USG_NM: "농업용수", CNT: "6.0", SGBS_CPCTY: "0.5", USEQTY: "0.6", PUB_FCT: "-", COMM_HOUSE: "-", MEGA_STORE: "-", GYM_FCT: "-", SCHOOL: "-", GOLF: "-", ETC: "0.6" },
|
||||
{ USG_NM: "기타", CNT: "48", SGBS_CPCTY: "306.1", USEQTY: "155.6", PUB_FCT: "2.8", COMM_HOUSE: "-", MEGA_STORE: "-", GYM_FCT: "0.6", SCHOOL: "0.5", GOLF: "147.5", ETC: "4.2" },
|
||||
{ USG_NM: "미기재", CNT: "103", SGBS_CPCTY: "22.9", USEQTY: "34.8", PUB_FCT: "16.0", COMM_HOUSE: "0.6", MEGA_STORE: "-", GYM_FCT: "4.6", SCHOOL: "0.2", GOLF: "-", ETC: "13.5" },
|
||||
{ USG_NM: "합계", CNT: "3,543", SGBS_CPCTY: "7,820.8", USEQTY: "13,944.7", PUB_FCT: "299.6", COMM_HOUSE: "2,428.4", MEGA_STORE: "293.3", GYM_FCT: "77.6", SCHOOL: "32.6", GOLF: "10,191.6", ETC: "621.2" },
|
||||
];
|
||||
|
||||
// 빗물이용시설의 용도별 빗물이용량 현황
|
||||
export const rainyByUsgYearsHeader: TableHeaderType[] = [
|
||||
{ id: "USG_NM", title: "용도", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
export const rainyByUsgYearsInit: DmStatusUsgYearsType[] = [
|
||||
{ USG_NM: "청소·화장실용수", Y1: "126.5", Y2: "134.1", Y3: "96.5", Y4: "153.9", Y5: "140.5", Y6: "108.8" },
|
||||
{ USG_NM: "세척·살수용수", Y1: "14.8", Y2: "14.6", Y3: "9.5", Y4: "49.8", Y5: "49.7", Y6: "37.0" },
|
||||
{ USG_NM: "조경용수", Y1: "7,629.8", Y2: "8,496.2", Y3: "9,415.4", Y4: "13,360.4", Y5: "14,466.3", Y6: "13,554.1" },
|
||||
{ USG_NM: "친수용수", Y1: "1.0", Y2: "0.1", Y3: "0.1", Y4: "0.1", Y5: "0.1", Y6: "0.1" },
|
||||
{ USG_NM: "공업용수", Y1: "152.0", Y2: "77.0", Y3: "77.0", Y4: "75.4", Y5: "0.4", Y6: "53.2" },
|
||||
{ USG_NM: "농업용수", Y1: "4.4", Y2: "4.3", Y3: "4.3", Y4: "4.3", Y5: "0.6", Y6: "0.6" },
|
||||
{ USG_NM: "기타", Y1: "37.7", Y2: "42.6", Y3: "230.7", Y4: "394.3", Y5: "463.2", Y6: "155.6" },
|
||||
];
|
||||
|
||||
// 전체 중수도 설치 및 이용 현황 (공장 및 발전시설 제외)
|
||||
export const rswtrByLawYearsHeader: TableHeaderType[] = [
|
||||
{ id: "COL1", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "COL2", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
export const rswtrByLawYearsInit: DmStatusLawYearsType[] = [
|
||||
{ COL1: "전체", COL2: "CNT", Y1: "576", Y2: "633", Y3: "682", Y4: "740", Y5: "819", Y6: "842" },
|
||||
{ COL1: "전체", COL2: "건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", Y1: "1,494.2", Y2: "1,514.4", Y3: "1,524.9", Y4: "989.1", Y5: "1,104.3", Y6: "1,015.4" },
|
||||
{ COL1: "전체", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "39.6", Y2: "40.0", Y3: "51.1", Y4: "47.1", Y5: "48.9", Y6: "50.7" },
|
||||
{ COL1: "전체", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "2,223.4", Y2: "1,165.2", Y3: "6,713.0", Y4: "4,681.7", Y5: "3,918.2", Y6: "898.4" },
|
||||
{ COL1: "법적 대상", COL2: "CNT", Y1: "146", Y2: "154", Y3: "156", Y4: "170", Y5: "166", Y6: "169" },
|
||||
{ COL1: "법적 대상", COL2: "건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", Y1: "674.9", Y2: "682.4", Y3: "736.1", Y4: "421.1", Y5: "434.9", Y6: "465.5" },
|
||||
{ COL1: "법적 대상", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "20.0", Y2: "20.1", Y3: "31.2", Y4: "22.8", Y5: "21.2", Y6: "24.7" },
|
||||
{ COL1: "법적 대상", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "1,819.4", Y2: "712.8", Y3: "6,479.9", Y4: "4,416.8", Y5: "3,663.1", Y6: "616.1" },
|
||||
{ COL1: "법적 미대상", COL2: "CNT", Y1: "430", Y2: "479", Y3: "526", Y4: "570", Y5: "653", Y6: "673" },
|
||||
{ COL1: "법적 미대상", COL2: "건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>", Y1: "819.3", Y2: "832.0", Y3: "788.8", Y4: "568.0", Y5: "669.4", Y6: "549.9" },
|
||||
{ COL1: "법적 미대상", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "19.6", Y2: "19.5", Y3: "19.9", Y4: "24.3", Y5: "27.7", Y6: "26.0" },
|
||||
{ COL1: "법적 미대상", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "404.0", Y2: "452.4", Y3: "233.1", Y4: "264.9", Y5: "255.1", Y6: "282.3" },
|
||||
];
|
||||
|
||||
export const rswtrByLawYearsFactoryInit: DmStatusLawYearsType[] = [
|
||||
{ COL1: "전체", COL2: "CNT", Y1: "125", Y2: "127", Y3: "139", Y4: "146", Y5: "151", Y6: "176" },
|
||||
{ COL1: "전체", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "141.3", Y2: "141.0", Y3: "146.7", Y4: "189.0", Y5: "190.3", Y6: "166.5" },
|
||||
{ COL1: "전체", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "33,712", Y2: "29,300", Y3: "33,110", Y4: "22,284", Y5: "21,593", Y6: "21,349" },
|
||||
{ COL1: "법적 대상", COL2: "CNT", Y1: "59", Y2: "63", Y3: "78", Y4: "78", Y5: "82", Y6: "86" },
|
||||
{ COL1: "법적 대상", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "108.6", Y2: "109.7", Y3: "118.1", Y4: "159.5", Y5: "142.6", Y6: "119.4" },
|
||||
{ COL1: "법적 대상", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "24,847", Y2: "24,021", Y3: "28,055", Y4: "17,959", Y5: "12,241", Y6: "12,296" },
|
||||
{ COL1: "법적 비대상", COL2: "CNT", Y1: "66", Y2: "64", Y3: "61", Y4: "68", Y5: "69", Y6: "90" },
|
||||
{ COL1: "법적 비대상", COL2: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>", Y1: "32.7", Y2: "31.4", Y3: "28.7", Y4: "29.5", Y5: "47.7", Y6: "47.1" },
|
||||
{ COL1: "법적 비대상", COL2: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>", Y1: "8,864.9", Y2: "5,278.8", Y3: "5,054.7", Y4: "4,324.6", Y5: "9,353.3", Y6: "9,052.4" },
|
||||
];
|
||||
|
||||
export const Col2Name: Record<string, string> =
|
||||
{ CNT: "시설수 <span class='text-[13px] font-normal text-[#767676]'>(개소)</span>",
|
||||
TOTAR: "건축연면적 <span class='text-[13px] font-normal text-[#767676]'>(만m²)</span>",
|
||||
PROCCPCTY: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
USECPCTY: "사용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/년)</span>",
|
||||
TPUTTOTAL: "하수처리량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
USEAMNT: "재이용량 <span class='text-[13px] font-normal text-[#767676]'>(백만m³/년)</span>",
|
||||
REUSERT: "재이용률 <span class='text-[13px] font-normal text-[#767676]'>(%)</span>",
|
||||
FCT_CPC: "시설용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
TOT_TPUT_AVE: "처리용량 <span class='text-[13px] font-normal text-[#767676]'>(만m³/일)</span>",
|
||||
};
|
||||
|
||||
|
||||
export const rswtrByBildTpYearsHeader: TableHeaderType[] = [
|
||||
{ id: "BILD_TP", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
|
||||
export const rswtrByBildTpYearsInit: DmStatusBildTpYearsType[] = [
|
||||
{ BILD_TP: "공동주택", Y1: "804.0", Y2: "245.8", Y3: "255.1", Y4: "282.3", Y5: "255.1", Y6: "282.3" },
|
||||
{ BILD_TP: "판매시설", Y1: "1,447.5", Y2: "477.1", Y3: "802.1", Y4: "713.0", Y5: "972.3", Y6: "742.4" },
|
||||
{ BILD_TP: "숙박시설", Y1: "458.5", Y2: "0.4", Y3: "177.0", Y4: "102.0", Y5: "123.3", Y6: "140.9" },
|
||||
{ BILD_TP: "업무시설", Y1: "20.7", Y2: "0.3", Y3: "14.3", Y4: "44.9", Y5: "31.5", Y6: "37.4" },
|
||||
{ BILD_TP: "의료시설", Y1: "97.7", Y2: "7.2", Y3: "63.7", Y4: "55.7", Y5: "85.1", Y6: "70.4" },
|
||||
{ BILD_TP: "교육연구시설", Y1: "536.6", Y2: "1.5", Y3: "1,558.4", Y4: "772.2", Y5: "315.1", Y6: "66.7" },
|
||||
{ BILD_TP: "운수·물류시설", Y1: "759.2", Y2: "34.2", Y3: "459.8", Y4: "259.8", Y5: "252.8", Y6: "410.4" },
|
||||
{ BILD_TP: "기타", Y1: "453.1", Y2: "443.3", Y3: "3,126.8", Y4: "2,451.5", Y5: "1,880.8", Y6: "199.7" },
|
||||
{ BILD_TP: "합계", Y1: "35,936.3", Y2: "30,466.6", Y3: "37,487.2", Y4: "25,480.6", Y5: "24,345.8", Y6: "22,247.6" },
|
||||
];
|
||||
|
||||
export const rswtrByUsgYearsHeader: TableHeaderType[] = [
|
||||
{ id: "USE_NM", title: "용도", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
// 연도별 하수처리수 재이용 현황
|
||||
export const pbnstByLawYearsHeader: TableHeaderType[] = [
|
||||
{ id: "COL1", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "COL2", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
// 용도별 하수처리수 재이용 현황
|
||||
export const pbnstByUsgYearsHeader: TableHeaderType[] = [
|
||||
{ id: "USG_NM", title: "용도", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
// 하수처리시설 설치 및 이용 현황
|
||||
export const pbnstByCpcYearsHeader: TableHeaderType[] = [
|
||||
{ id: "COL1", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "COL2", title: "구분", row: 0, widthPercent: 15 },
|
||||
{ id: "Y1", title: "2019", row: 1, widthPercent: 5 },
|
||||
{ id: "Y2", title: "2020", row: 1, widthPercent: 5 },
|
||||
{ id: "Y3", title: "2021", row: 1, widthPercent: 5 },
|
||||
{ id: "Y4", title: "2022", row: 0, widthPercent: 7 },
|
||||
{ id: "Y5", title: "2023", row: 0, widthPercent: 10 },
|
||||
{ id: "Y6", title: "2024", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
// 시도별 하수처리수 재이용 현황
|
||||
export const pbnstBySidoHeader: TableHeaderType[] = [
|
||||
{ id: "SIDONM", title: "시도", row: 0, widthPercent: 15 },
|
||||
{ id: "TPUT_TOTAL", title: "연간하수처리량", row: 1, widthPercent: 5 },
|
||||
{ id: "USE_AMNT_TOTAL", title: "재이용현황계", row: 1, widthPercent: 5 },
|
||||
{ id: "USE_AMNT_D01", title: "재이용현황 장내", row: 1, widthPercent: 5 },
|
||||
{ id: "USE_AMNT_D02", title: "재이용현황 장외소계", row: 0, widthPercent: 7 },
|
||||
{ id: "USE_AMNT_S15", title: "장외 하천유지용수", row: 0, widthPercent: 10 },
|
||||
{ id: "USE_AMNT_S16", title: "장외 농업용수", row: 1, widthPercent: 5 },
|
||||
{ id: "USE_AMNT_S18", title: "장외 공업용수", row: 1, widthPercent: 5 },
|
||||
{ id: "USE_AMNT_ETC", title: "장외 기타용수", row: 1, widthPercent: 5 },
|
||||
{ id: "REUSE_RT", title: "재이용률", row: 1, widthPercent: 5 },
|
||||
{ id: "REMARK", title: "비고", row: 1, widthPercent: 5 },
|
||||
];
|
||||
|
||||
@@ -537,6 +537,8 @@ const getSdmInfoStatusList = async (dmdInfoId: string) => {
|
||||
// 유사사례 조회
|
||||
const fetchGetSimilarCaseList = async (part: string, value: number | string) => {
|
||||
try {
|
||||
// part : 'S' 요구수량 , 'U' 사용용도
|
||||
// default : part 'S' , value 10000 즉, part와 value값이 없으면 backend에서 default값으로 조회함.
|
||||
const url = '/reuseFc/pubFcRuseCase/dm/' + part + '/' + (value ? value : 0);
|
||||
const res = await getFetch(fetchType.WR, url);
|
||||
return res?.data || [];
|
||||
@@ -759,7 +761,7 @@ export const SdmReducer = (set: any): SdmActions => {
|
||||
}
|
||||
}))
|
||||
},
|
||||
|
||||
// 용도 코드 조회 (WR018 : 하수재이용장외용도)
|
||||
getUseTypesData: async () => {
|
||||
//http://dev.onasys.co.kr:3000/server/cms-service/api/v1/lwpo-cds?lwpoCdNm=&sort=regDt,desc&upCd=WR018&page=0&size=10
|
||||
|
||||
|
||||
@@ -257,9 +257,9 @@ export function extractSearchInputFromRoadAddr(roadAddr: string): string {
|
||||
* @returns 최대 5개의 "도로명 건물본번" 자동완성 문자열 목록
|
||||
*/
|
||||
export async function fetchPostcodeSuggestions(input: string): Promise<string[]> {
|
||||
const normalized = input.replace(/\s+/g, " ").trim();
|
||||
const normalized = input.replace(/\s+/g, " ").trim(); // space가 여러개일 때, 하나만 남김
|
||||
const parts = normalized.split(" ");
|
||||
const tail = parts.at(-1) ?? "";
|
||||
const tail = parts.at(-1) ?? ""; // 마지막 요소
|
||||
const roadNm =
|
||||
/^\d+(?:-\d+)?$/.test(tail) && parts.length > 1
|
||||
? parts.slice(0, -1).join(" ").trim()
|
||||
|
||||
@@ -18,8 +18,7 @@ import {
|
||||
dmStatusState,
|
||||
type DmStatusStore,
|
||||
type DmStatusType,
|
||||
} from "./wrStatus";
|
||||
|
||||
} from "./dm-status";
|
||||
export type DmStore = CommonStore & SdmStore & DmMapStore & DmStatusStore;
|
||||
export type DmState = CommonType & SdmType & DmMapType & DmStatusType;
|
||||
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { ChevronDown, ChevronUp } from "lucide-react";
|
||||
|
||||
export type AnchorSection = {
|
||||
id: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
export type BusinessSubNavMenu = {
|
||||
key: string;
|
||||
label: string;
|
||||
href?: string;
|
||||
current?: boolean;
|
||||
defaultOpen?: boolean;
|
||||
sections?: AnchorSection[];
|
||||
};
|
||||
|
||||
type BusinessSubNavProps = {
|
||||
menus?: BusinessSubNavMenu[];
|
||||
offsetTop?: number;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export default function BusinessSubNav({
|
||||
menus = [],
|
||||
offsetTop = 120,
|
||||
className = "",
|
||||
}: BusinessSubNavProps) {
|
||||
const [activeId, setActiveId] = useState("");
|
||||
const [openMenus, setOpenMenus] = useState<Record<string, boolean>>({});
|
||||
|
||||
const allSections = useMemo(
|
||||
() => menus.flatMap((menu) => menu.sections ?? []),
|
||||
[menus]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const initialOpenState: Record<string, boolean> = {};
|
||||
|
||||
menus.forEach((menu) => {
|
||||
initialOpenState[menu.key] = menu.defaultOpen ?? menu.current ?? false;
|
||||
});
|
||||
|
||||
setOpenMenus(initialOpenState);
|
||||
}, [menus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!allSections.length) return;
|
||||
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
const visible = entries
|
||||
.filter((entry) => entry.isIntersecting)
|
||||
.sort((a, b) => b.intersectionRatio - a.intersectionRatio);
|
||||
|
||||
if (visible[0]) {
|
||||
setActiveId(visible[0].target.id);
|
||||
}
|
||||
},
|
||||
{
|
||||
rootMargin: `-${offsetTop}px 0px -55% 0px`,
|
||||
threshold: [0.2, 0.35, 0.5, 0.7],
|
||||
}
|
||||
);
|
||||
|
||||
const targets = allSections
|
||||
.map((section) => document.getElementById(section.id))
|
||||
.filter(Boolean) as HTMLElement[];
|
||||
|
||||
targets.forEach((el) => observer.observe(el));
|
||||
|
||||
const hash = window.location.hash.replace("#", "");
|
||||
const initialActive =
|
||||
allSections.find((section) => section.id === hash)?.id ??
|
||||
allSections[0]?.id ??
|
||||
"";
|
||||
|
||||
setActiveId(initialActive);
|
||||
|
||||
return () => observer.disconnect();
|
||||
}, [allSections, offsetTop]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activeId) return;
|
||||
|
||||
const parentMenu = menus.find((menu) =>
|
||||
(menu.sections ?? []).some((section) => section.id === activeId)
|
||||
);
|
||||
|
||||
if (!parentMenu) return;
|
||||
|
||||
setOpenMenus(() => {
|
||||
const nextOpenState: Record<string, boolean> = {};
|
||||
|
||||
menus.forEach((menu) => {
|
||||
nextOpenState[menu.key] = menu.key === parentMenu.key;
|
||||
});
|
||||
|
||||
return nextOpenState;
|
||||
});
|
||||
}, [activeId, menus]);
|
||||
|
||||
const handleMove = (id: string) => {
|
||||
setActiveId(id);
|
||||
|
||||
const target = document.getElementById(id);
|
||||
if (!target) return;
|
||||
|
||||
window.history.replaceState(null, "", `#${id}`);
|
||||
|
||||
const top = target.getBoundingClientRect().top + window.scrollY - offsetTop;
|
||||
|
||||
window.scrollTo({
|
||||
top,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
const toggleMenu = (key: string) => {
|
||||
setOpenMenus((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
if (!menus.length) return null;
|
||||
|
||||
return (
|
||||
<aside className={`fixed right-2 top-[110px] z-50 w-[260px] ${className}`}>
|
||||
<div className="overflow-hidden bg-white">
|
||||
{menus.map((menu) => {
|
||||
const isOpen = openMenus[menu.key] ?? false;
|
||||
const hasChildren = (menu.sections?.length ?? 0) > 0;
|
||||
const isCurrentGroup = (menu.sections ?? []).some(
|
||||
(section) => section.id === activeId
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={menu.key}
|
||||
>
|
||||
<div className="flex items-center justify-between px-5 py-2">
|
||||
{menu.href ? (
|
||||
<Link
|
||||
href={menu.href}
|
||||
className={[
|
||||
"text-[16px] font-medium tracking-[-0.02em]",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
>
|
||||
{menu.label}
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (hasChildren) toggleMenu(menu.key);
|
||||
}}
|
||||
className={[
|
||||
"text-left text-[16px] font-medium tracking-[-0.02em]",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
>
|
||||
{menu.label}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{hasChildren && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => toggleMenu(menu.key)}
|
||||
className={[
|
||||
"ml-2 shrink-0",
|
||||
isCurrentGroup || menu.current
|
||||
? "text-sky-500"
|
||||
: "text-[#767676]",
|
||||
].join(" ")}
|
||||
aria-label={`${menu.label} 드롭다운 토글`}
|
||||
>
|
||||
{isOpen ? (
|
||||
<ChevronUp className="h-5 w-5" />
|
||||
) : (
|
||||
<ChevronDown className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{hasChildren && isOpen && (
|
||||
<div className="relative px-5 pb-5">
|
||||
<div className="absolute left-[31px] top-[6px] bottom-[14px] w-[2px] bg-[#CDD5DB]" />
|
||||
|
||||
<ul className="space-y-4 pl-2">
|
||||
{menu.sections!.map((section) => {
|
||||
const active = activeId === section.id;
|
||||
|
||||
return (
|
||||
<li key={section.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleMove(section.id)}
|
||||
className="flex items-start gap-3 text-left"
|
||||
>
|
||||
<span
|
||||
className={[
|
||||
"relative z-[1] mt-[6px] -ml-[2px] h-[11px] w-[11px] shrink-0 rounded-full border",
|
||||
active
|
||||
? "border-sky-500 bg-sky-500"
|
||||
: "border-[#BFC7CF] bg-white",
|
||||
].join(" ")}
|
||||
/>
|
||||
<span
|
||||
className={[
|
||||
"break-keep text-[14px] leading-[1.45] transition-colors",
|
||||
active
|
||||
? "text-[#4A4A4A]"
|
||||
: "text-[#D1D7DE] hover:text-[#7B8794]",
|
||||
].join(" ")}
|
||||
>
|
||||
{section.label}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
// apps/dm/app/(landing)/reuse/_components/ImageSlider.tsx
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
@@ -9,167 +8,151 @@ type Slide = { src: string; alt?: string };
|
||||
type Props = {
|
||||
slides: Slide[];
|
||||
className?: string;
|
||||
maxWidthClassName?: string; // 예: "max-w-[760px]"
|
||||
maxWidthClassName?: string;
|
||||
autoPlayMs?: number;
|
||||
imageHeightClassName?: string;
|
||||
};
|
||||
|
||||
const PER_VIEW = 2;
|
||||
|
||||
export default function CaseImageSlider({
|
||||
slides,
|
||||
className = "",
|
||||
maxWidthClassName = "max-w-[760px]",
|
||||
maxWidthClassName = "max-w-[700px]",
|
||||
autoPlayMs = 4500,
|
||||
imageHeightClassName = "h-[340px]",
|
||||
}: Props) {
|
||||
const safeSlides = useMemo(() => slides.filter(Boolean), [slides]);
|
||||
const [idx, setIdx] = useState(0);
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const [dragging, setDragging] = useState(false);
|
||||
const [isHover, setIsHover] = useState(false);
|
||||
|
||||
const startX = useRef(0);
|
||||
const deltaX = useRef(0);
|
||||
|
||||
const total = safeSlides.length;
|
||||
const canSlide = total > 1;
|
||||
const pageCount = Math.ceil(safeSlides.length / PER_VIEW);
|
||||
const canSlide = pageCount > 1;
|
||||
|
||||
const go = (dir: number) => {
|
||||
const goTo = (nextPage: number) => {
|
||||
if (!canSlide) return;
|
||||
setIdx((prev) => {
|
||||
const next = prev + dir;
|
||||
if (next < 0) return total - 1;
|
||||
if (next >= total) return 0;
|
||||
return next;
|
||||
});
|
||||
|
||||
if (nextPage < 0) setPage(pageCount - 1);
|
||||
else if (nextPage >= pageCount) setPage(0);
|
||||
else setPage(nextPage);
|
||||
};
|
||||
|
||||
// ← → 키보드 지원(선택)
|
||||
useEffect(() => {
|
||||
const onKey = (e: KeyboardEvent) => {
|
||||
if (!canSlide) return;
|
||||
if (e.key === "ArrowLeft") go(-1);
|
||||
if (e.key === "ArrowRight") go(1);
|
||||
};
|
||||
window.addEventListener("keydown", onKey);
|
||||
return () => window.removeEventListener("keydown", onKey);
|
||||
}, [canSlide, total]);
|
||||
const goNext = () => goTo(page + 1);
|
||||
const goPrev = () => goTo(page - 1);
|
||||
|
||||
// 드래그(마우스/터치)로 넘기기
|
||||
const onPointerDown = (e: React.PointerEvent) => {
|
||||
// ✅ 자동롤링 (hover 시 정지 포함)
|
||||
useEffect(() => {
|
||||
if (!canSlide || dragging || isHover) return;
|
||||
|
||||
const timer = window.setInterval(() => {
|
||||
setPage((prev) => (prev + 1 >= pageCount ? 0 : prev + 1));
|
||||
}, autoPlayMs);
|
||||
|
||||
return () => window.clearInterval(timer);
|
||||
}, [canSlide, dragging, isHover, pageCount, autoPlayMs]);
|
||||
|
||||
// ✅ swipe
|
||||
const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!canSlide) return;
|
||||
|
||||
setDragging(true);
|
||||
startX.current = e.clientX;
|
||||
deltaX.current = 0;
|
||||
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
||||
e.currentTarget.setPointerCapture(e.pointerId);
|
||||
};
|
||||
|
||||
const onPointerMove = (e: React.PointerEvent) => {
|
||||
const onPointerMove = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
if (!dragging) return;
|
||||
deltaX.current = e.clientX - startX.current;
|
||||
};
|
||||
|
||||
const onPointerUp = () => {
|
||||
if (!dragging) return;
|
||||
|
||||
setDragging(false);
|
||||
|
||||
const threshold = 60; // 넘김 감도
|
||||
if (deltaX.current > threshold) go(-1);
|
||||
else if (deltaX.current < -threshold) go(1);
|
||||
const threshold = 50;
|
||||
|
||||
if (deltaX.current > threshold) goPrev();
|
||||
if (deltaX.current < -threshold) goNext();
|
||||
|
||||
deltaX.current = 0;
|
||||
};
|
||||
|
||||
if (!total) return null;
|
||||
if (!safeSlides.length) return null;
|
||||
|
||||
return (
|
||||
<section className={className}>
|
||||
<div className={`mx-auto ${maxWidthClassName}`}>
|
||||
<div className="relative">
|
||||
{/* 좌측 버튼 */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => go(-1)}
|
||||
className="absolute left-[-60px] top-1/2 -translate-y-1/2 z-10
|
||||
w-[80px] h-[80px] rounded-full border border-black/20 bg-white
|
||||
grid place-items-center shadow-sm hover:bg-black/[0.03]"
|
||||
>
|
||||
<Image
|
||||
src="/images/landing/icon_left_arrow.png"
|
||||
alt="이전"
|
||||
width={12}
|
||||
height={22}
|
||||
className="w-[12px] h-[22px]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
|
||||
{/* 우측 버튼 */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => go(1)}
|
||||
className="absolute right-[-60px] top-1/2 -translate-y-1/2 z-10
|
||||
w-[80px] h-[80px] rounded-full border border-black/20 bg-white
|
||||
grid place-items-center shadow-sm hover:bg-black/[0.03]"
|
||||
>
|
||||
<Image
|
||||
src="/images/landing/icon_right_arrow.png"
|
||||
alt="다음"
|
||||
width={12}
|
||||
height={22}
|
||||
className="w-[12px] h-[22px]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
|
||||
{/* 뷰포트 */}
|
||||
<div
|
||||
className="overflow-hidden select-none touch-pan-y cursor-grab active:cursor-grabbing"
|
||||
onMouseEnter={() => setIsHover(true)} // ✅ hover 시작 → 정지
|
||||
onMouseLeave={() => setIsHover(false)} // ✅ hover 끝 → 재시작
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
>
|
||||
<div
|
||||
className={[
|
||||
"overflow-hidden",
|
||||
"select-none touch-pan-y",
|
||||
"cursor-grab active:cursor-grabbing",
|
||||
"w-[583px] h-[400px] mx-auto",
|
||||
].join(" ")}
|
||||
onPointerDown={onPointerDown}
|
||||
onPointerMove={onPointerMove}
|
||||
onPointerUp={onPointerUp}
|
||||
onPointerCancel={onPointerUp}
|
||||
className="flex transition-transform duration-500 ease-out"
|
||||
style={{
|
||||
transform: `translateX(-${page * 100}%)`,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="flex transition-transform duration-300 ease-out"
|
||||
style={{ transform: `translateX(-${idx * 100}%)` }}
|
||||
>
|
||||
{safeSlides.map((s, i) => (
|
||||
<div key={`${s.src}-${i}`} className="min-w-full">
|
||||
<Image
|
||||
src={s.src}
|
||||
alt={s.alt ?? `slide-${i + 1}`}
|
||||
width={583}
|
||||
height={400}
|
||||
className="w-full h-full object-contain"
|
||||
style={{ width: "100%", height: "100%" }}
|
||||
draggable={false}
|
||||
/>
|
||||
{Array.from({ length: pageCount }).map((_, pageIndex) => {
|
||||
const pageSlides = safeSlides.slice(
|
||||
pageIndex * PER_VIEW,
|
||||
pageIndex * PER_VIEW + PER_VIEW
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
key={pageIndex}
|
||||
className="grid min-w-full grid-cols-2 gap-5"
|
||||
>
|
||||
{pageSlides.map((slide, i) => (
|
||||
<div
|
||||
key={`${slide.src}-${i}`}
|
||||
className={`relative ${imageHeightClassName} overflow-hidden rounded-[20px] bg-neutral-100`}
|
||||
>
|
||||
<Image
|
||||
src={slide.src}
|
||||
alt={slide.alt ?? `slide-${pageIndex * PER_VIEW + i + 1}`}
|
||||
fill
|
||||
className="object-cover"
|
||||
sizes="(max-width: 768px) 50vw, 340px"
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 도트 */}
|
||||
{canSlide && (
|
||||
<div className="mt-3 flex items-center justify-center gap-2">
|
||||
{safeSlides.map((_, i) => {
|
||||
const active = i === idx;
|
||||
return (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => setIdx(i)}
|
||||
aria-label={`${i + 1}번 슬라이드`}
|
||||
className={[
|
||||
"h-2 w-2 rounded-full",
|
||||
active ? "bg-black/70" : "bg-black/20 hover:bg-black/35",
|
||||
].join(" ")}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
<div className="mt-5 flex items-center justify-center gap-[6px]">
|
||||
{Array.from({ length: pageCount }).map((_, i) => (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
onClick={() => setPage(i)}
|
||||
className={[
|
||||
"h-[8px] w-[8px] rounded-full transition-all",
|
||||
page === i ? "bg-[#18BCEB]" : "bg-[#D8DDE3]",
|
||||
].join(" ")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import Image from "next/image";
|
||||
* - BUGFIX 2) 슬라이드 중(busy) clipWidth 변화로 트랙 스냅(-W) 덮어쓰지 않게 분리
|
||||
*/
|
||||
|
||||
type TabKey = "rain" | "reuse" | "sewage" | "global";
|
||||
type TabKey = "rain" | "greywater" | "sewage" | "global";
|
||||
|
||||
type CaseItem = {
|
||||
id: string;
|
||||
@@ -34,7 +34,7 @@ type CaseItem = {
|
||||
|
||||
const TABS: { key: TabKey; label: string }[] = [
|
||||
{ key: "rain", label: "빗물 재이용 사례" },
|
||||
{ key: "reuse", label: "중수 재이용 사례" },
|
||||
{ key: "greywater", label: "중수 재이용 사례" },
|
||||
{ key: "sewage", label: "하수 재이용 사례" },
|
||||
{ key: "global", label: "해외 재이용 사례" },
|
||||
];
|
||||
@@ -43,113 +43,143 @@ const TABS: { key: TabKey; label: string }[] = [
|
||||
const MOCK: Record<TabKey, CaseItem[]> = {
|
||||
rain: [
|
||||
{
|
||||
id: "rain-0",
|
||||
title: "경기도 수원시 빗물이용과 중수도",
|
||||
desc: "(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기…",
|
||||
imageSrc: "/images/landing/rain_01.png",
|
||||
href: "/reuse/business/rain/suwon",
|
||||
id: "rain-1",
|
||||
title: "경기도 수원시 빗물 재이용",
|
||||
desc: "월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물공급기를 설치하여 축서기 도시 열섬효과 및 도로 비산먼지 저감",
|
||||
imageSrc: "/images/landing/business/rain/suwon_01.png",
|
||||
href: "/example#example-rain-suwon",
|
||||
},
|
||||
{
|
||||
id: "rain-2",
|
||||
title: "제주특별자치도 빗물 재이용",
|
||||
desc: "강우시 비닐하우스에서 발생하는 대체수자원인 빗물을 순환 활용하고자 하는 사업으로, 지하수 사용 저감과 집중호우시 도로와 농경지 침수피해 예방",
|
||||
imageSrc: "/images/landing/business/rain/jeju_01.png",
|
||||
href: "/example#example-rain-jeju",
|
||||
},
|
||||
],
|
||||
greywater: [
|
||||
{
|
||||
id: "greywater-1",
|
||||
title: "경기도 안산시 중수 재이용",
|
||||
desc: "수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현을 위해 안산시 중수 재이용 사업 추진, 공공기관 및 산업체에 중수 공급",
|
||||
imageSrc: "/images/landing/business/greywater/no_img.jpg",
|
||||
href: "/example#example-greywater-ansan",
|
||||
},
|
||||
{
|
||||
id: "greywater-2",
|
||||
title: "경기도 용인시 중수 재이용",
|
||||
desc: "물 부족 등의 비상상황에 대한 대비, 하수라도 사용할 집결 등 수자원의 효율적 활용과 지속가능성 도모를 위해 용인시 중수 재이용 사업 추진, 공공기관 및 산업체에 중수 공급",
|
||||
imageSrc: "/images/landing/business/greywater/no_img.jpg",
|
||||
href: "/example#example-greywater-yongin",
|
||||
},
|
||||
{
|
||||
id: "greywater-3",
|
||||
title: "경기도 평택시 중수 재이용",
|
||||
desc: "수자원을 효율적으로 활용하여 지속가능성 도모 및 물 절약 실현을 위해 평택시 중수 재이용 사업 추진, 공공기관 및 산업체에 중수 공급",
|
||||
imageSrc: "/images/landing/business/greywater/no_img.jpg",
|
||||
href: "/example#example-greywater-pyeongtaek",
|
||||
},
|
||||
// {
|
||||
// id: "rain-2",
|
||||
// title: "경기도 수원시 빗물이용과 중수도",
|
||||
// desc: "(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기…",
|
||||
// imageSrc: "/images/landing/rain_01.png",
|
||||
// href: "#",
|
||||
// },
|
||||
],
|
||||
reuse: Array.from({ length: 6 }).map((_, i) => ({
|
||||
id: `reuse-${i}`,
|
||||
title: `중수 재이용 사례 ${i + 1}`,
|
||||
desc: "설명 문구가 들어갑니다. (최대 2~3줄) …",
|
||||
imageSrc: "/images/landing/sample_case.png",
|
||||
href: "#",
|
||||
})),
|
||||
sewage: [
|
||||
{
|
||||
id: "sewage-1",
|
||||
title: "경기도 파주시 하수 재이용",
|
||||
desc: "운정·금촌하수처리시설의 하수처리수를 막공법(U/F, R/O)으로 재처리하여 파주 LCD 산업단지내 3개 업체*에...",
|
||||
imageSrc: "/images/landing/sewage_01.png",
|
||||
href: "/reuse/business/sewage/paju",
|
||||
title: "구리시 하수 재이용",
|
||||
desc: "고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용하여 미세먼지 저감 및 폭염 취약성 완화",
|
||||
imageSrc: "/images/landing/business/sewage/guri_01.png",
|
||||
href: "/example#example-sewage-guri",
|
||||
},
|
||||
{
|
||||
id: "sewage-2",
|
||||
title: "충남보령 하수재이용",
|
||||
desc: "보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업...",
|
||||
imageSrc: "/images/landing/sewage_02.png",
|
||||
href: "/reuse/business/sewage/bolyeong",
|
||||
title: "부천시 하수재이용",
|
||||
desc: "굴포하수처리시설 하수처리수 재이용수 생산시설 설치 운영을 통해 여월천 등 부천 관내 하천의 건천화 방지, 하천 생태계 보존 및 주민들에게 친수 공간 제공",
|
||||
imageSrc: "/images/landing/business/sewage/bucheon_01.png",
|
||||
href: "/example#example-sewage-bucheon",
|
||||
},
|
||||
{
|
||||
id: "sewage-3",
|
||||
title: "여수시 하수처리수 재이용",
|
||||
desc: "(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기 도시 열섬효과 및 도로...",
|
||||
imageSrc: "/images/landing/sewage_03.png",
|
||||
href: "/reuse/business/sewage/yeosu",
|
||||
title: "수원시 하수처리수 재이용",
|
||||
desc: "황구지천 공공하수처리시설 하수처리수 재이용사업으로 황구 호매실천 및 금곡천 건천화 방지로 주민들에게 친수 공간 제공 및 하천 생태계 보전",
|
||||
imageSrc: "/images/landing/business/sewage/suwon_01.png",
|
||||
href: "/example#example-sewage-suwon",
|
||||
},
|
||||
{
|
||||
id: "sewage-4",
|
||||
title: "청주시 하수 재이용 시설",
|
||||
desc: "청주시 하수처리수 재이용 민간투자사업(BTO) 청주시 하수처리수 재이용 민간투자사업(BTO) 청주시 하수처리수 재이용 민간투자사업(BTO)...",
|
||||
imageSrc: "/images/landing/sewage_04.png",
|
||||
href: "/reuse/business/sewage/cheongju",
|
||||
title: "파주시 하수 재이용 시설",
|
||||
desc: "운정 및 금촌하수처리시설의 하수처리수를 막공법으로 재처리하여 파주LCD산업단지 내 3개 업체에 공업용수를 공급(LG디스플레이, 희성전자, 엔비텍)",
|
||||
imageSrc: "/images/landing/business/sewage/paju_01.png",
|
||||
href: "/example#example-sewage-paju",
|
||||
},
|
||||
{
|
||||
id: "sewage-5",
|
||||
title: "수원시 황구지천 하천유지용수",
|
||||
desc: "황구지천 공공하수처리시설 하수처리수 재이용사업으로 향후 호매실천 및 금곡천 건천화 방지로...",
|
||||
imageSrc: "/images/landing/sewage_05.png",
|
||||
href: "/reuse/business/sewage/hwanggu",
|
||||
title: "포항시 하수 재이용 시설",
|
||||
desc: "포항시 내 공단 개발 확대로 지속적이고 안정적인 공업용수 공급 추진(포스코, 동국산업 등), 포항하수처리장 하수처리수를 재처리하여 공업용수로 공급",
|
||||
imageSrc: "/images/landing/business/sewage/pohang_01.png",
|
||||
href: "/example#example-sewage-pohang",
|
||||
},
|
||||
{
|
||||
id: "sewage-6",
|
||||
title: "구리시 도로자동청소시스템",
|
||||
desc: "고농도 미세먼지 발생 및 폭염의 급증에 따라 공공하수처리시설 발생 재이용수 일부를 도로자동청소시스템에 활용...",
|
||||
imageSrc: "/images/landing/sewage_06.png",
|
||||
href: "/reuse/business/sewage/guri",
|
||||
title: "울산광역시 하수 재이용 시설",
|
||||
desc: "18년 갈수기시 낙동강 수질 악화로 울산석유화학단지 용수 공급 부족을 해소하기 위해 미포산단 내 한화케미칼 등 8개사 공업용수 공급",
|
||||
imageSrc: "/images/landing/business/sewage/ulsan_01.png",
|
||||
href: "/example#example-sewage-ulsan",
|
||||
},
|
||||
// ...Array.from({ length: 6 }).map((_, i) => ({
|
||||
// id: `sewage-x-${i}`,
|
||||
// title: `하수 재이용 사례 ${i + 4}`,
|
||||
// desc: "설명 문구가 들어갑니다. (최대 2~3줄) …",
|
||||
// imageSrc: "/images/landing/sample_case.png",
|
||||
// href: "#",
|
||||
// })),
|
||||
{
|
||||
id: "sewage-7",
|
||||
title: "여수시 하수 재이용 시설",
|
||||
desc: "수요자가 원하는 양질의 공업용수를 저렴하게 공급하여 한정된 수자원의 효율적 이용 및 지역적 물 부족 해소, 하수재이용을 통한 오염총량 저감 및 방류수역 수질개선을 위한 여수국가산단 내 공업용수 공급(GS칼텍스, YNCC 등)",
|
||||
imageSrc: "/images/landing/business/sewage/yeosu_01.png",
|
||||
href: "/example#example-sewage-yeosu",
|
||||
},
|
||||
{
|
||||
id: "sewage-8",
|
||||
title: "보령시 하수 재이용 시설",
|
||||
desc: "한국중부발전(주)의 보령 및 신보령화력발전소에 10,000㎥/일의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진, 하수처리수 재이용을 통한 수자원 절약 및 지역 물 부족 해소",
|
||||
imageSrc: "/images/landing/business/sewage/boryeong_01.png",
|
||||
href: "/example#example-sewage-boryeong",
|
||||
},
|
||||
{
|
||||
id: "sewage-9",
|
||||
title: "아산시 하수 재이용 시설",
|
||||
desc: "신도시 개발에 따른 인구 급증으로 발생하는 하수를 재처리하여 탕정산업단지(삼성디스플레이)에 공업용수를 안정적으로 공급함으로써 지역기반 및 활성화를 도모",
|
||||
imageSrc: "/images/landing/business/sewage/asan_01.png",
|
||||
href: "/example#example-sewage-asan",
|
||||
}
|
||||
],
|
||||
global: [
|
||||
{
|
||||
id: "global-1",
|
||||
title: "해외 재이용 사례 개요",
|
||||
desc: "2024년 하수도 통계 기준 15.6%이며, 제2차 물 재이용 기본 계획을 고려한 2030년 목표는 17%, 우리나라의 하수 처리 재이용률은 15% 수준...",
|
||||
imageSrc: "/images/landing/overview_01.png",
|
||||
href: "reuse/business/globalCase/overview",
|
||||
desc: "전 세계적으로 물 재이용은 호주, 유럽, 아프리카, 미국 등 가뭄이 잦거나 심각한 가뭄이 발생한 국가에서 많이 이루어짐. 이스라엘, 쿠웨이트 등 중동 및 북아프리카 지역은 가용 수자원이 제한되어 있어 재이용량 비중도 매우 높은 편",
|
||||
imageSrc: "/images/landing/business/globalCase/overview_01.png",
|
||||
href: "/example#example-global-overview",
|
||||
},
|
||||
{
|
||||
id: "global-2",
|
||||
title: "[대만] 국가 주도의 공업용수 공급",
|
||||
desc: "대만은 물 재이용에 적극적이며 국가주도로 하수재이용 사업 시행(안핑), 안핑을 포함한 16건 공사 완료시 628,100 CMD 공급 가능...",
|
||||
imageSrc: "/images/landing/taiwan_01.png",
|
||||
href: "/reuse/business/globalCase/taiwan",
|
||||
desc: "수자원 지속 가능성 향상, 재생 수자원 활용을 통해 물부족 문제 해결. 재생 수자원 정의·분류, 이용 의무, 이용 제한, 수질 기준 등",
|
||||
imageSrc: "/images/landing/business/globalCase/taiwan_01.png",
|
||||
href: "/example#example-global-taiwan",
|
||||
},
|
||||
{
|
||||
id: "global-3",
|
||||
title: "[호주] 생활 속 물 재이용 / 광역 물 재이용수 공급",
|
||||
desc: "SEQ Water Grid는 브리즈번 남동부지역의 식수 공급을 위해 설정한 광역 수로망, 이 광역수로망을 활용하여 물이 부족한 지역으로 물을 공급...",
|
||||
imageSrc: "/images/landing/australia_01.png",
|
||||
href: "/reuse/business/globalCase/australia",
|
||||
title: "[싱가포르] 수요가 높은 산업의 물 재이용 의무화",
|
||||
desc: "2003년 공식 NEWater 공장을 가동했으며 신뢰확보를 위해 NEWater Visitor Center를 함께 건설. NEWater 전체파이프 공급 → 반도체 공장에서 가장 많이 사용",
|
||||
imageSrc: "/images/landing/business/globalCase/singapore_01.png",
|
||||
href: "/example#example-global-singapore",
|
||||
},
|
||||
{
|
||||
id: "global-4",
|
||||
title: "[싱가포르] 수요가 높은 산업의 물 재이용 의무화",
|
||||
desc: "2003년 공식 NEWater 공장을 가동했으며 신뢰확보를 위해 NEWater Visitor Center를 함께 건설, NEWater 전체파이프 공급 → 반도체 공장에서 가장 많이 사용...",
|
||||
imageSrc: "/images/landing/singapore_01.png",
|
||||
href: "/reuse/business/globalCase/singapore",
|
||||
title: "[호주] 생활 속 물 재이용 / 광역 물 재이용수 공급",
|
||||
desc: "SEQ Water Grid는 브리즈번 남동부지역의 식수 공급을 위해 설정한 광역 수로망. 이 광역수로망을 활용하여 물이 부족한 지역으로 물을 공급",
|
||||
imageSrc: "/images/landing/business/globalCase/australia_01.png",
|
||||
href: "/example#example-global-australia",
|
||||
},
|
||||
{
|
||||
id: "global-5",
|
||||
title: "[EU] 법률을 제정, EU국가에서 물 재이용 유도",
|
||||
desc: "유럽 지역의 물 부족 문제를 인식하고 ’20년 물 재이용 법률을 제정 (2020/741), 2023년부터 모든 EU국가에서 시행...",
|
||||
imageSrc: "/images/landing/eu_01.png",
|
||||
href: "/reuse/business/globalCase/eu",
|
||||
desc: "유럽 지역의 물 부족 문제를 인식하고 ’20년 물 재이용 법률을 제정 (2020/741), 2023년부터 모든 EU국가에서 시행",
|
||||
imageSrc: "/images/landing/business/globalCase/eu_01.png",
|
||||
href: "/example#example-global-eu",
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -249,7 +279,7 @@ export default function CaseTabsCarousel() {
|
||||
// 탭별 마지막 그룹 인덱스 기억
|
||||
const tabIndexRef = useRef<Record<TabKey, number>>({
|
||||
rain: 0,
|
||||
reuse: 0,
|
||||
greywater: 0,
|
||||
sewage: 0,
|
||||
global: 0,
|
||||
});
|
||||
|
||||
@@ -1,181 +1,55 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import {
|
||||
fetchAnonymousMenu,
|
||||
findActiveTopMenu,
|
||||
hasAnyChild,
|
||||
isMenuActive,
|
||||
type MenuItem,
|
||||
} from "@/app/store/common";
|
||||
|
||||
export type LnbItem = {
|
||||
export type LnbMenu = {
|
||||
label: string;
|
||||
href?: string;
|
||||
children?: LnbItem[];
|
||||
href: string;
|
||||
};
|
||||
|
||||
function findOpenMenuCode(items: MenuItem[], pathname: string): number | null {
|
||||
const matched = items.find((item) => {
|
||||
if (isMenuActive(pathname, item)) return true;
|
||||
return (item.children ?? []).some((child) => isMenuActive(pathname, child));
|
||||
});
|
||||
type Props = {
|
||||
menus?: LnbMenu[];
|
||||
className?: string;
|
||||
};
|
||||
|
||||
return matched?.menuCd ?? null;
|
||||
}
|
||||
const defaultMenus: LnbMenu[] = [
|
||||
{ label: "수요처", href: "/apply/dmdco" },
|
||||
{ label: "수요정보등록", href: "/apply/dmdinfo" },
|
||||
{ label: "수요신청현황", href: "/apply/status" },
|
||||
];
|
||||
|
||||
export default function Lnb() {
|
||||
export default function Lnb({ menus, className = "" }: Props) {
|
||||
const pathname = usePathname();
|
||||
const [menus, setMenus] = useState<MenuItem[]>([]);
|
||||
const [openMenuCode, setOpenMenuCode] = useState<number | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let isMounted = true;
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
const loaded = await fetchAnonymousMenu();
|
||||
if (!isMounted) return;
|
||||
console.log("Fetched LNB menus:", loaded);
|
||||
setMenus(loaded);
|
||||
} catch (error) {
|
||||
console.error("Failed to load DN LNB menu", error);
|
||||
if (!isMounted) return;
|
||||
setMenus([]);
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
isMounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const currentTopMenu = useMemo(
|
||||
() => findActiveTopMenu(menus, pathname),
|
||||
[menus, pathname],
|
||||
);
|
||||
|
||||
const secondLevelMenus = useMemo(
|
||||
() => currentTopMenu?.children ?? [],
|
||||
[currentTopMenu],
|
||||
);
|
||||
// console.log("LNB render", { pathname, currentTopMenu, secondLevelMenus, menus });
|
||||
useEffect(() => {
|
||||
const defaultOpen = findOpenMenuCode(secondLevelMenus, pathname);
|
||||
setOpenMenuCode(defaultOpen);
|
||||
}, [pathname, secondLevelMenus]);
|
||||
const navMenus = menus ?? defaultMenus;
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={[
|
||||
"fixed left-0 top-[83px]", // ✅ 화면 고정 + TopNav 아래로
|
||||
"w-[260px] min-w-[260px] shrink-0",
|
||||
"h-[calc(100vh-83px)]", // ✅ 남은 높이만큼
|
||||
"overflow-y-auto", // ✅ LNB 내부 스크롤
|
||||
"bg-white z-50", // ✅ 겹침 방지
|
||||
].join(" ")}
|
||||
>
|
||||
{/* 타이틀 */}
|
||||
<div className="flex h-[120px] items-center justify-center bg-gradient-to-r from-cyan-500 to-green-500 text-[40px] font-bold text-white">
|
||||
{currentTopMenu?.menuKornNm?.trim().split(/\s+/).pop() ?? ""}
|
||||
</div>
|
||||
<aside className={`fixed right-2 top-[110px] z-50 w-[260px] ${className}`}>
|
||||
<ul className="space-y-1">
|
||||
{navMenus.map((menu) => {
|
||||
const isActive =
|
||||
pathname === menu.href ||
|
||||
pathname.startsWith(`${menu.href}/`);
|
||||
|
||||
{/* 메뉴 */}
|
||||
<ul>
|
||||
{secondLevelMenus.map((item) => {
|
||||
const hasChildren = hasAnyChild(item);
|
||||
const active = isMenuActive(pathname, item);
|
||||
|
||||
if (!hasChildren) {
|
||||
return (
|
||||
<li key={item.menuCd}>
|
||||
<li key={menu.href}>
|
||||
<Link
|
||||
href={item.urlPath || "#"}
|
||||
className={[
|
||||
"block border-b border-slate-300 px-5 py-4 text-center text-base font-semibold transition-colors",
|
||||
active
|
||||
? "cursor-default bg-white text-sky-500"
|
||||
: "bg-[#F0F0F0] text-[#767676] hover:bg-white hover:text-sky-500",
|
||||
].join(" ")}
|
||||
href={menu.href}
|
||||
className={`
|
||||
block text-[16px] font-medium transition px-5 py-2
|
||||
${
|
||||
isActive
|
||||
? "text-[#00AEEF]"
|
||||
: "text-[#767676] hover:text-[#00AEEF]"
|
||||
}
|
||||
`}
|
||||
>
|
||||
{item.menuKornNm}
|
||||
{menu.label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
const isOpen = openMenuCode === item.menuCd;
|
||||
const childMenus = item.children ?? [];
|
||||
|
||||
return (
|
||||
<li key={item.menuCd}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
setOpenMenuCode((prev) => (prev === item.menuCd ? null : item.menuCd))
|
||||
}
|
||||
className={[
|
||||
"flex w-full items-center justify-between border-b border-slate-300 px-5 py-6 text-base font-semibold",
|
||||
active ? "bg-slate-100 text-slate-900" : "bg-slate-100 text-slate-500",
|
||||
].join(" ")}
|
||||
aria-expanded={isOpen}
|
||||
>
|
||||
<span className="w-full text-center">{item.menuKornNm}</span>
|
||||
<span
|
||||
aria-hidden
|
||||
className={[
|
||||
"ml-3 shrink-0 transition-transform duration-200",
|
||||
isOpen ? "rotate-180" : "rotate-0",
|
||||
].join(" ")}
|
||||
>
|
||||
<Image
|
||||
src="/images/landing/drop_arrow.svg"
|
||||
alt="menu toggle icon"
|
||||
width={28}
|
||||
height={28}
|
||||
className="h-7 w-7"
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
className={[
|
||||
"grid transition-[grid-template-rows] duration-200 ease-out",
|
||||
isOpen ? "grid-rows-[1fr]" : "grid-rows-[0fr]",
|
||||
].join(" ")}
|
||||
>
|
||||
<div className="overflow-hidden bg-white">
|
||||
<ul>
|
||||
{childMenus.map((child) => {
|
||||
const childActive = isMenuActive(pathname, child);
|
||||
|
||||
return (
|
||||
<li key={child.menuCd}>
|
||||
<Link
|
||||
href={child.urlPath || "#"}
|
||||
className={[
|
||||
"block border-b border-slate-200 bg-slate-50 px-5 py-4 pl-[60px] text-[18px] font-medium transition-colors",
|
||||
childActive
|
||||
? "cursor-default bg-white text-sky-500"
|
||||
: "text-slate-500 hover:bg-white hover:text-sky-500",
|
||||
].join(" ")}
|
||||
>
|
||||
<span className="mr-2 text-current">-</span>
|
||||
{child.menuKornNm}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
})}
|
||||
</ul>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
/**
|
||||
* 로그인 modal
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { useEffect, useId, useState } from "react";
|
||||
@@ -50,6 +53,16 @@ export default function LoginModal({
|
||||
);
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
|
||||
const resetForm = () => {
|
||||
setBizNo("");
|
||||
setWorkerNo("");
|
||||
setNoWorker(true);
|
||||
setPw("");
|
||||
setErrors({});
|
||||
setTouched({});
|
||||
setSubmitting(false);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
@@ -61,13 +74,10 @@ export default function LoginModal({
|
||||
const prev = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden"; // 모달 오픈 시 배경 스크롤 방지
|
||||
|
||||
// 모달 오픈 시 에러/터치 초기화
|
||||
setErrors({});
|
||||
setTouched({});
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", onKeyDown);
|
||||
document.body.style.overflow = prev;
|
||||
resetForm();
|
||||
};
|
||||
}, [open, onClose]);
|
||||
|
||||
@@ -151,7 +161,7 @@ export default function LoginModal({
|
||||
const parsed = JSON.parse(sessionUser) as { dmdCoNm?: string };
|
||||
onSuccess?.({ userName: parsed.dmdCoNm ?? "수요처" });
|
||||
onClose();
|
||||
router.refresh();
|
||||
router.push("/apply/status");
|
||||
}
|
||||
} finally {
|
||||
setSubmitting(false);
|
||||
@@ -342,7 +352,7 @@ export default function LoginModal({
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.push(REGISTER_HREF)}
|
||||
onClick={() => {onClose(); router.push(REGISTER_HREF);}}
|
||||
className="shrink-0 rounded-[4px] h-[48px] px-10 bg-[#004A94] text-white text-[18px] hover:opacity-95"
|
||||
>
|
||||
수요처등록
|
||||
|
||||
@@ -30,14 +30,16 @@ function useDebouncedValue<T>(value: T, delay = 250) {
|
||||
export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
const [q, setQ] = useState("");
|
||||
const [q, setQ] = useState(""); // 주소 입력값
|
||||
const dq = useDebouncedValue(q, 250);
|
||||
|
||||
const [items, setItems] = useState<AddrItem[]>([]);
|
||||
const [suggestions, setSuggestions] = useState<string[]>([]);
|
||||
const [items, setItems] = useState<AddrItem[]>([]); // 검색 결과
|
||||
const [suggestions, setSuggestions] = useState<string[]>([]); // 추천 주소 목록
|
||||
const [total, setTotal] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchMessage, setSearchMessage] = useState("");
|
||||
const [searchMessage, setSearchMessage] = useState(""); // 검색 실패/형식 오류/보조 안내 문구를 담는 상태값
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [searchedInput, setSearchedInput] = useState(""); // 주소 검색 입력란 저장
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
@@ -51,6 +53,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
setTotal(0);
|
||||
setLoading(false);
|
||||
setSearchMessage("");
|
||||
setIsInputFocused(false);
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -59,7 +62,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
const prevOverflow = document.body.style.overflow;
|
||||
document.body.style.overflow = "hidden";
|
||||
|
||||
setTimeout(() => inputRef.current?.focus(), 50);
|
||||
setTimeout(() => inputRef.current?.focus(), 50); // 주소입력란에 focus()
|
||||
|
||||
const onKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape") onClose();
|
||||
@@ -131,6 +134,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
|
||||
setItems(result.items);
|
||||
setTotal(result.totalCount);
|
||||
setSearchedInput(keyword);
|
||||
} catch {
|
||||
if (!alive) return;
|
||||
setItems([]);
|
||||
@@ -149,7 +153,7 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
};
|
||||
}, [dq, open]);
|
||||
|
||||
const showSuggest = open && q.trim().length >= 1 && suggestions.length > 0;
|
||||
const showSuggest = open && isInputFocused && q.trim().length >= 1 && suggestions.length > 0 && dq.trim() !== searchedInput;
|
||||
|
||||
if (!mounted || !open) return null;
|
||||
|
||||
@@ -190,6 +194,10 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
ref={inputRef}
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
onFocus={() => setIsInputFocused(true)}
|
||||
onBlur={() => {
|
||||
setTimeout(() => setIsInputFocused(false), 100);
|
||||
}}
|
||||
placeholder="예) 테헤란로 152-1"
|
||||
className={[
|
||||
"w-full rounded-md border px-4 py-3 pr-12 text-[14px] outline-none",
|
||||
@@ -226,8 +234,9 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
key={s}
|
||||
type="button"
|
||||
className="flex w-full items-center justify-between px-4 py-3 text-left text-[14px] hover:bg-slate-50"
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
onClick={() => {
|
||||
// ✅ 추천 클릭 → 입력값 채우고 드롭다운 닫기 + 포커스 유지
|
||||
// 추천 클릭 → 입력값 초기화 드롭다운 닫기 + 포커스 유지
|
||||
setQ(s);
|
||||
setSuggestions([]);
|
||||
requestAnimationFrame(() => inputRef.current?.focus());
|
||||
@@ -263,7 +272,9 @@ export default function PostcodeModal({ open, onClose, onSelect }: Props) {
|
||||
<button
|
||||
key={`${it.zipNo}-${it.roadAddr}`}
|
||||
type="button"
|
||||
onClick={() => onSelect(it)}
|
||||
onClick={() => {setQ("");
|
||||
setSuggestions([]);
|
||||
onSelect(it)}}
|
||||
className={[
|
||||
"w-full rounded-lg border p-4 text-left transition",
|
||||
"border-slate-200 hover:border-sky-300 hover:bg-sky-50/30",
|
||||
|
||||
@@ -16,27 +16,21 @@ type Props = {
|
||||
};
|
||||
|
||||
export default function SubPageHeader({
|
||||
title,
|
||||
// title,
|
||||
crumbs,
|
||||
note = "* 는 필수 값입니다.",
|
||||
// note = "* 는 필수 값입니다.",
|
||||
// underlineWidthClass = "w-40",
|
||||
className = "",
|
||||
wrapClassName = "",
|
||||
// hideFooterLine = false,
|
||||
hideGuideText = false,
|
||||
// hideGuideText = false,
|
||||
}: Props) {
|
||||
return (
|
||||
<section className={`text-black ${className}`}>
|
||||
<section className={`text-black pr-[100px] ${className}`}>
|
||||
<div className={`pb-5 ${wrapClassName}`}>
|
||||
<div className="flex items-start justify-between gap-6">
|
||||
{/* 좌측 타이틀 */}
|
||||
<div className="inline-block">
|
||||
<h1 className="relative z-10 px-2 text-[28px] font-extrabold tracking-tight text-slate-900 leading-none">{title}</h1>
|
||||
<div className="relative top-[-7px] -z-1 h-4 bg-gradient-to-r from-sky-300 to-green-300" />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-5">
|
||||
{/* 우측 브레드크럼 */}
|
||||
<nav className="pt-2 text-sm text-black/70" aria-label="breadcrumb">
|
||||
<nav className="text-sm text-black/70" aria-label="breadcrumb">
|
||||
{crumbs.map((c, i) => (
|
||||
<span key={`${c.label}-${i}`}>
|
||||
{c.href ? (
|
||||
@@ -49,15 +43,15 @@ export default function SubPageHeader({
|
||||
{i < crumbs.length - 1 && <span className="mx-2">›</span>}
|
||||
</span>
|
||||
))}
|
||||
</nav>
|
||||
</nav>
|
||||
</div>
|
||||
{/* 하단 라인 + 안내문 */}
|
||||
{!hideGuideText && (
|
||||
{/* {!hideGuideText && (
|
||||
<div className="mt-6 border-t border-black/10 pt-4 text-sm text-black/70">
|
||||
<span className="text-red-400">*</span>
|
||||
{note.replace("*", "")}
|
||||
</div>
|
||||
)}
|
||||
)} */}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -12,10 +12,10 @@ import { useDmStore } from "@/app/store";
|
||||
const LS_USER_KEY = "user";
|
||||
|
||||
const DEFAULT_NAV_ITEMS: Pick<MenuItem, "menuCd" | "menuKornNm" | "urlPath" | "newWndwYn">[] = [
|
||||
{ menuCd: 1, menuKornNm: "물 재이용 소개", urlPath: "/reuse/intro/law", newWndwYn: "N" },
|
||||
{ menuCd: 2, menuKornNm: "물 재이용 현황", urlPath: "/reuse/cases/rain", newWndwYn: "N" },
|
||||
{ menuCd: 3, menuKornNm: "물 재이용 사례", urlPath: "/reuse/business/rain/suwon", newWndwYn: "N" },
|
||||
{ menuCd: 4, menuKornNm: "수요신청", urlPath: "/reuse/apply/place", newWndwYn: "N" },
|
||||
{ menuCd: 1, menuKornNm: "물 재이용 소개", urlPath: "/intro", newWndwYn: "N" },
|
||||
{ menuCd: 2, menuKornNm: "물 재이용 현황", urlPath: "/status", newWndwYn: "N" },
|
||||
{ menuCd: 3, menuKornNm: "물 재이용 사례", urlPath: "/example", newWndwYn: "N" },
|
||||
{ menuCd: 4, menuKornNm: "수요신청", urlPath: "/apply/dmdco", newWndwYn: "N" },
|
||||
];
|
||||
|
||||
export default function TopNav() {
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function SearchInputText({
|
||||
value={textValue || ""}
|
||||
onChange={handleInputChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
className="w-full h-8 text-xs pl-2 pr-2 text-gray-600 border border-gray-300 rounded focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||
className="w-full h-[3.2rem] !text-xl pl-3 text-gray-600 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
placeholder={`${label}을(를) 입력하세요`}
|
||||
onClick={() => {
|
||||
setTextValue("");
|
||||
@@ -109,10 +109,10 @@ export default function SearchInputText({
|
||||
searchList.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`w-full cursor-pointer px-3 py-2 text-left text-xs ${
|
||||
className={`w-full cursor-pointer overflow-auto px-5 py-3 text-left !text-xl ${
|
||||
highlightIndex === index
|
||||
? "bg-blue-50"
|
||||
: "hover:bg-gray-50"
|
||||
? "bg-blue-100"
|
||||
: "hover:bg-gray-100"
|
||||
}`}
|
||||
onClick={() => handleSuggestionClick(item.bscFctNm)}
|
||||
role="button"
|
||||
@@ -127,7 +127,7 @@ export default function SearchInputText({
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-xs w-full px-3 py-2 hover:bg-gray-50 cursor-pointer text-gray-500">
|
||||
<div className="!text-xl w-full px-5 py-3 hover:bg-gray-100 cursor-pointer overflow-auto">
|
||||
검색 결과가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -62,51 +62,50 @@ export function SearchBar({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center w-full px-4 py-2">
|
||||
<div className="flex items-center w-auto shadow-md px-4 py-2 space-x-2 bg-white/95 backdrop-blur-sm rounded-lg text-sm border border-gray-200">
|
||||
<div className="flex justify-center w-full px-4 py-3">
|
||||
<div className="flex items-center w-full max-w-screen-lg rounded-xl bg-white shadow-md px-6 py-4 space-x-3">
|
||||
<SelectBox
|
||||
labelClass="w-[50px] text-xs font-medium text-gray-700"
|
||||
className="w-[110px]"
|
||||
labelClass="w-[40px]"
|
||||
className="w-[200px]"
|
||||
label="시도"
|
||||
selectClass="rounded bg-white text-xs h-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={Common.selectSidoData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "ctpvCd")}
|
||||
value={sidoValue || Common.selectSidoData[0]?.val || ""}
|
||||
/>
|
||||
<SelectBox
|
||||
labelClass="w-[70px] text-xs font-medium text-gray-700"
|
||||
className="w-[110px]"
|
||||
labelClass="w-[65px]"
|
||||
className="w-[200px]"
|
||||
label="시군구"
|
||||
selectClass="rounded bg-white text-xs h-8"
|
||||
selectClass="rounded-lg bg-white"
|
||||
selectArray={filteredSigunData || []}
|
||||
onSelectValue={(val) => onSelectValue(val.toString(), "sggCd")}
|
||||
value={sigunValue}
|
||||
/>
|
||||
<Label className="text-gray-700 text-xs font-medium pl-2 pr-1">하수재이용시설</Label>
|
||||
<div className="w-[180px]">
|
||||
<SearchInputText
|
||||
label="시설명 입력"
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
<Label className="text-gray-600">하수재이용시설</Label>
|
||||
<SearchInputText
|
||||
label="시설명"
|
||||
initValue={undefined}
|
||||
setValue={(searchNm?: string) => {
|
||||
let sggOrCity = "";
|
||||
if (searchNm) {
|
||||
if (sigunValue === "0" || sigunValue === "") {
|
||||
sggOrCity = sidoValue;
|
||||
} else {
|
||||
sggOrCity = sigunValue;
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
</div>
|
||||
findRuseFclt(sggOrCity, searchNm);
|
||||
}
|
||||
}}
|
||||
setRtnData={(rtnData: any) => {
|
||||
setHasuPk(rtnData);
|
||||
}}
|
||||
searchList={map.ruseFcltList || []}
|
||||
/>
|
||||
<Button
|
||||
color="sky"
|
||||
className="ml-2 text-white !h-8 !py-0 !px-3 !text-xs font-semibold rounded"
|
||||
className="ml-2 text-white"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
onApply();
|
||||
}}
|
||||
|
||||
@@ -8,7 +8,7 @@ const serverApiUrl = process.env.NEXT_PUBLIC_CS_SERVER_API_URL;
|
||||
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
|
||||
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
|
||||
const GATEWAY_URL = process.env.NEXT_PUBLIC_GATEWAY_URL || "";
|
||||
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||
// const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
|
||||
const BUSINESS_JUSO_API_URL = process.env.NEXT_PUBLIC_FINDADRESS_URL || "";
|
||||
const AdressKey = process.env.NEXT_PRIVATE_WR_ADDRESS_KEY || "";
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 340 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 243 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 916 B |
|
After Width: | Height: | Size: 562 B |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 364 KiB |