Compare commits
7
Commits
6ec9e51fb4
...
40cbd827e2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40cbd827e2 | ||
|
|
ecc0b7a459 | ||
|
|
56d8f7445e | ||
|
|
b25e89099d | ||
|
|
6a3aa5750d | ||
|
|
b04c215503 | ||
|
|
5ff12b9cbb |
@@ -34,7 +34,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"next-qrcode": "^2.5.1",
|
"next-qrcode": "^2.5.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ import {
|
|||||||
type MyPostList,
|
type MyPostList,
|
||||||
type PostParams,
|
type PostParams,
|
||||||
type MyPostParams,
|
type MyPostParams,
|
||||||
|
type NoticePostParams,
|
||||||
type PostFormData,
|
type PostFormData,
|
||||||
type PostUpdateFormData,
|
type PostUpdateFormData,
|
||||||
type PostService,
|
type PostService,
|
||||||
|
type NoticePostList,
|
||||||
} from "../model/post-interface";
|
} from "../model/post-interface";
|
||||||
|
|
||||||
export class PostApiService implements PostService {
|
export class PostApiService implements PostService {
|
||||||
@@ -28,6 +30,17 @@ export class PostApiService implements PostService {
|
|||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async getNoticePostList(
|
||||||
|
params?: NoticePostParams,
|
||||||
|
): Promise<NoticePostList> {
|
||||||
|
const response = await client.get<NoticePostList>(
|
||||||
|
`${ENDPOINT.BOARD_SERVICE.POSTS}/recentPosts`,
|
||||||
|
{ params },
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
public async getMyPostList(
|
public async getMyPostList(
|
||||||
params?: MyPostParams,
|
params?: MyPostParams,
|
||||||
): Promise<MyPostList> {
|
): Promise<MyPostList> {
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ export {
|
|||||||
postUpdateFormSchema,
|
postUpdateFormSchema,
|
||||||
type Post,
|
type Post,
|
||||||
type MyPost,
|
type MyPost,
|
||||||
|
type NoticePost,
|
||||||
type MovePost,
|
type MovePost,
|
||||||
type DeletePost,
|
type DeletePost,
|
||||||
type PostList,
|
type PostList,
|
||||||
type PostParams,
|
type PostParams,
|
||||||
type MyPostParams,
|
type MyPostParams,
|
||||||
|
type NoticePostParams,
|
||||||
type PostFormData,
|
type PostFormData,
|
||||||
type PostUpdateFormData,
|
type PostUpdateFormData,
|
||||||
type PostService,
|
type PostService,
|
||||||
|
|||||||
@@ -12,6 +12,20 @@ export interface PostParams extends PageableParams {
|
|||||||
ansUseYn?: string;
|
ansUseYn?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NoticePostParams extends PageableParams {
|
||||||
|
keywordType?: string;
|
||||||
|
keyword?: string;
|
||||||
|
bbsId?: number | null;
|
||||||
|
delYn?: string;
|
||||||
|
frDateYmd?: string;
|
||||||
|
toDateYmd?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NoticePost extends Post {
|
||||||
|
bbsNm: string;
|
||||||
|
bbsTypeCd: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MyPostParams extends PageableParams {
|
export interface MyPostParams extends PageableParams {
|
||||||
keywordType?: string;
|
keywordType?: string;
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
@@ -99,6 +113,7 @@ export const postUpdateFormSchema = postFormSchema.omit({
|
|||||||
|
|
||||||
export type PostList = PageableList<Post>;
|
export type PostList = PageableList<Post>;
|
||||||
export type MyPostList = PageableList<MyPost>;
|
export type MyPostList = PageableList<MyPost>;
|
||||||
|
export type NoticePostList = PageableList<NoticePost>;
|
||||||
export type PostFormData = z.infer<typeof postFormSchema>;
|
export type PostFormData = z.infer<typeof postFormSchema>;
|
||||||
export type PostUpdateFormData = z.infer<typeof postUpdateFormSchema>;
|
export type PostUpdateFormData = z.infer<typeof postUpdateFormSchema>;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { client } from "@/shared/api";
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
|
||||||
|
export interface Banner {
|
||||||
|
bnrSn: number;
|
||||||
|
bnrTypeCd: string;
|
||||||
|
bnrTypeNm: string;
|
||||||
|
bnrTtl: string;
|
||||||
|
lnkgUrlAddr: string;
|
||||||
|
useYn: string;
|
||||||
|
atflCd: string;
|
||||||
|
mdfcnDt: string;
|
||||||
|
mdfr: string;
|
||||||
|
mdfrNm: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBannerList = async (siteId: string) => {
|
||||||
|
const response = await client.get<Banner[]>(
|
||||||
|
`${ENDPOINT.CMS_SERVICE.BANNERS}/${siteId}`,
|
||||||
|
);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getImageUrlByAtflCd = async (atflCd: string): Promise<string> => {
|
||||||
|
if (!atflCd) return "";
|
||||||
|
const response = await client.get<Blob>(
|
||||||
|
`${ENDPOINT.ATTACH_SERVICE.IMAGES}/atflCd/${atflCd}`,
|
||||||
|
{
|
||||||
|
responseType: "blob",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const blob = response.data;
|
||||||
|
return URL.createObjectURL(blob);
|
||||||
|
};
|
||||||
@@ -348,85 +348,79 @@ export function KoreaBasinMap() {
|
|||||||
|
|
||||||
// 스타일은 필요에 따라 tailwind, css 등으로 커스터마이즈하세요.
|
// 스타일은 필요에 따라 tailwind, css 등으로 커스터마이즈하세요.
|
||||||
return (
|
return (
|
||||||
<div className="col-span-3 h-200 !p-0 relative z-10">
|
<div
|
||||||
<div className="card card-border">
|
ref={stageRef}
|
||||||
<div className="card-header text-foreground">
|
id="stage"
|
||||||
<h3 className="text-2xl font-medium">유역별 정보</h3>
|
style={
|
||||||
</div>
|
{
|
||||||
<div
|
aspectRatio: 0.72/1,
|
||||||
ref={stageRef}
|
width: "100%",
|
||||||
id="stage"
|
maxHeight: "100%",
|
||||||
style={
|
position: "relative",
|
||||||
{
|
margin: "auto",
|
||||||
width: "100%",
|
"--map-inset": "0%",
|
||||||
height: "560px",
|
} as React.CSSProperties
|
||||||
position: "relative",
|
}
|
||||||
zIndex: 20,
|
>
|
||||||
"--map-inset": "0",
|
<div
|
||||||
} as React.CSSProperties
|
className={styles.bgLayer}
|
||||||
}
|
id="bgLayer"
|
||||||
>
|
/>
|
||||||
<div
|
<div
|
||||||
className={styles.bgLayer}
|
ref={svgLayerRef}
|
||||||
id="bgLayer"
|
id="svgLayer"
|
||||||
/>
|
className={`${styles.svgLayer} ${styles.svgContainer}`}
|
||||||
<div
|
style={{ width: "100%", height: "100%" }}
|
||||||
ref={svgLayerRef}
|
/>
|
||||||
id="svgLayer"
|
<div
|
||||||
className={`${styles.svgLayer} ${styles.svgContainer}`}
|
ref={labelsLayerRef}
|
||||||
style={{ width: "100%", height: "100%" }}
|
id="labelsLayer"
|
||||||
/>
|
className={styles.labelsLayer}
|
||||||
<div
|
style={{
|
||||||
ref={labelsLayerRef}
|
position: "absolute",
|
||||||
id="labelsLayer"
|
left: 0,
|
||||||
className={styles.labelsLayer}
|
top: 0,
|
||||||
style={{
|
width: "100%",
|
||||||
position: "absolute",
|
height: "100%",
|
||||||
left: 0,
|
pointerEvents: "none",
|
||||||
top: 0,
|
}}
|
||||||
width: "100%",
|
/>
|
||||||
height: "100%",
|
<div
|
||||||
pointerEvents: "none",
|
ref={badgeRef}
|
||||||
}}
|
id="badge"
|
||||||
/>
|
className={styles.badge}
|
||||||
<div
|
style={{ position: "absolute", zIndex: 10, display: "block" }}
|
||||||
ref={badgeRef}
|
/>
|
||||||
id="badge"
|
<div
|
||||||
className={styles.badge}
|
ref={popupRef}
|
||||||
style={{ position: "absolute", zIndex: 10, display: "block" }}
|
id="popup"
|
||||||
/>
|
className={styles.popup}
|
||||||
<div
|
style={{
|
||||||
ref={popupRef}
|
position: "absolute",
|
||||||
id="popup"
|
zIndex: 20,
|
||||||
className={styles.popup}
|
background: "#fff",
|
||||||
style={{
|
border: "1px solid #ccc",
|
||||||
position: "absolute",
|
borderRadius: 8,
|
||||||
zIndex: 20,
|
padding: 12,
|
||||||
background: "#fff",
|
minWidth: 160,
|
||||||
border: "1px solid #ccc",
|
boxShadow: "0 2px 8px #0002",
|
||||||
borderRadius: 8,
|
display: "block",
|
||||||
padding: 12,
|
opacity: 0,
|
||||||
minWidth: 160,
|
visibility: "hidden",
|
||||||
boxShadow: "0 2px 8px #0002",
|
}}
|
||||||
display: "block",
|
>
|
||||||
opacity: 0,
|
<table style={{ width: "100%" }}>
|
||||||
visibility: "hidden",
|
<thead>
|
||||||
}}
|
<tr>
|
||||||
>
|
<th>구분</th>
|
||||||
<table style={{ width: "100%" }}>
|
<th ref={colTitleRef} id="colTitle">
|
||||||
<thead>
|
시설수
|
||||||
<tr>
|
</th>
|
||||||
<th>구분</th>
|
</tr>
|
||||||
<th ref={colTitleRef} id="colTitle">
|
</thead>
|
||||||
시설수
|
<tbody ref={rowsRef} id="rows"></tbody>
|
||||||
</th>
|
</table>
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody ref={rowsRef} id="rows"></tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import type { ColumnDef } from "@tanstack/react-table";
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
import { Badge } from "@common/components";
|
import { Badge } from "@common/components";
|
||||||
import type { Post, MyPost } from "@/entities/post";
|
import type { MyPost, NoticePost } from "@/entities/post";
|
||||||
|
|
||||||
const noticePostTableColumns: ColumnDef<Post>[] = [
|
const noticePostTableColumns: ColumnDef<NoticePost>[] = [
|
||||||
{
|
{
|
||||||
header: "번호",
|
header: "번호",
|
||||||
cell: ({ row, table }) => {
|
cell: ({ row, table }) => {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRef } from "react";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import Autoplay from "embla-carousel-autoplay";
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import { getImageUrlByAtflCd, getBannerList } from "../api/banner";
|
||||||
|
import { getCookie } from "@/shared/lib";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Carousel,
|
||||||
|
CarouselContent,
|
||||||
|
CarouselItem,
|
||||||
|
CarouselNext,
|
||||||
|
CarouselPrevious,
|
||||||
|
} from "@common/components";
|
||||||
|
|
||||||
|
|
||||||
|
type BannerImageProps = {
|
||||||
|
atflCd: string;
|
||||||
|
bnrSn: number;
|
||||||
|
lnkgUrlAddr: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const BannerList: React.FC = () => {
|
||||||
|
const siteId = getCookie("site-id-key") ?? "";
|
||||||
|
|
||||||
|
const BannerImage = ({ atflCd, bnrSn, lnkgUrlAddr }: BannerImageProps) => {
|
||||||
|
const { data: imageUrl, isLoading } = useQuery({
|
||||||
|
queryKey: [ENDPOINT.ATTACH_SERVICE.IMAGES, atflCd],
|
||||||
|
queryFn: () => getImageUrlByAtflCd(atflCd),
|
||||||
|
enabled: !!atflCd,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
if (lnkgUrlAddr) {
|
||||||
|
window.open(lnkgUrlAddr, '_blank');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="relative flex h-24 items-center justify-center bg-accent mx-auto">
|
||||||
|
<div className="text-sm text-muted-foreground">로딩 중...</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!imageUrl) {
|
||||||
|
return (
|
||||||
|
<div className="relative flex h-24 items-center justify-center bg-accent mx-auto">
|
||||||
|
<div className="text-sm text-muted-foreground">이미지 없음</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="relative flex h-24 items-center justify-center cursor-pointer hover:opacity-80 transition-opacity mx-auto"
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<img src={imageUrl} className="object-contain w-full h-full" alt="배너 이미지" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
const { data: bannerList } = useQuery({
|
||||||
|
queryKey: [ENDPOINT.CMS_SERVICE.BANNERS, siteId],
|
||||||
|
queryFn: () => getBannerList(siteId),
|
||||||
|
});
|
||||||
|
|
||||||
|
const plugin = useRef(Autoplay({ delay: 2000, stopOnInteraction: true }));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card card-border">
|
||||||
|
<Carousel
|
||||||
|
plugins={[plugin.current]}
|
||||||
|
opts={{ loop: true }}
|
||||||
|
className="relative my-8 h-24"
|
||||||
|
>
|
||||||
|
<CarouselContent>
|
||||||
|
{bannerList && bannerList.length > 0 ? (
|
||||||
|
bannerList.map((banner) => (
|
||||||
|
<CarouselItem key={`banner-${banner.bnrSn}`} className="basis-1/3">
|
||||||
|
<BannerImage
|
||||||
|
atflCd={banner.atflCd}
|
||||||
|
bnrSn={banner.bnrSn}
|
||||||
|
lnkgUrlAddr={banner.lnkgUrlAddr}
|
||||||
|
/>
|
||||||
|
</CarouselItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<CarouselItem className="basis-1/3">
|
||||||
|
<div className="relative flex h-24 w-full items-center justify-center bg-accent">
|
||||||
|
<div className="text-sm text-muted-foreground">배너가 없습니다</div>
|
||||||
|
</div>
|
||||||
|
</CarouselItem>
|
||||||
|
)}
|
||||||
|
</CarouselContent>
|
||||||
|
<CarouselPrevious className="absolute left-2 top-1/2 !bg-[rgba(255,255,255,0.8)] text-accent-foreground z-10" />
|
||||||
|
<CarouselNext className="absolute right-2 top-1/2 !bg-[rgba(255,255,255,0.8)] text-accent-foreground z-10" />
|
||||||
|
</Carousel>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BannerList;
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
|
cn,
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
@@ -12,14 +13,19 @@ import {
|
|||||||
interface BasinSelectProps {
|
interface BasinSelectProps {
|
||||||
selectedBasin: string;
|
selectedBasin: string;
|
||||||
onValueChange: (value: string) => void;
|
onValueChange: (value: string) => void;
|
||||||
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasinSelect: React.FC<BasinSelectProps> = ({ selectedBasin, onValueChange }) => {
|
const BasinSelect: React.FC<BasinSelectProps> = ({
|
||||||
|
selectedBasin,
|
||||||
|
onValueChange,
|
||||||
|
className
|
||||||
|
}) => {
|
||||||
const basins = ["한강", "금강", "낙동강", "영산강"];
|
const basins = ["한강", "금강", "낙동강", "영산강"];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Select value={selectedBasin} onValueChange={onValueChange}>
|
<Select value={selectedBasin} onValueChange={onValueChange}>
|
||||||
<SelectTrigger className="w-[100px] h-8 bg-blue-500 text-white font-bold text-md border-none hover:bg-blue-600">
|
<SelectTrigger className={cn("w-[12rem]", className)}>
|
||||||
<SelectValue placeholder="유역 선택" />
|
<SelectValue placeholder="유역 선택" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import FoFcltCntRt from "./fo-fclt-cnt-rt";
|
|||||||
import FoWqltInflEffl from "./fo-wqlt-Infl-effl";
|
import FoWqltInflEffl from "./fo-wqlt-Infl-effl";
|
||||||
import ReuseChart from "./reuse-chart";
|
import ReuseChart from "./reuse-chart";
|
||||||
import ReuseStatus from "./reuse-status";
|
import ReuseStatus from "./reuse-status";
|
||||||
|
import NoticeList from "./notice-list";
|
||||||
|
import BannerList from "./banner-list";
|
||||||
|
|
||||||
// Chart.js 등록
|
// Chart.js 등록
|
||||||
ChartJS.register(
|
ChartJS.register(
|
||||||
@@ -137,9 +139,16 @@ function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 지도영역 */}
|
{/* 지도영역 */}
|
||||||
<KoreaBasinMap />
|
<div className="col-span-3 !p-0 relative z-10" style={{ height: "765px", background: "rgba(255, 255, 255, 0.6)" }}>
|
||||||
|
<div className="card card-border" style={{ height: "100%" }}>
|
||||||
|
<div className="card-header text-foreground">
|
||||||
|
<h3 className="text-2xl font-medium">유역별 정보</h3>
|
||||||
|
</div>
|
||||||
|
<div id="mapArea" style={{height: "calc(100% - 40px)"}}>
|
||||||
|
<KoreaBasinMap />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* 사용자 정보 카드 */}
|
{/* 사용자 정보 카드 */}
|
||||||
<div className="card card-border col-span-1 h-80 p-10">
|
<div className="card card-border col-span-1 h-80 p-10">
|
||||||
@@ -191,16 +200,12 @@ function DashboardPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card card-border">
|
<div className="card card-border">
|
||||||
<div className="card-header text-foreground">
|
<NoticeList />
|
||||||
<h3 className="text-2xl font-medium">공지사항</h3>
|
|
||||||
</div>
|
|
||||||
<div className="h-70 p-4">
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 배너 영역 */}
|
{/* 배너 영역 */}
|
||||||
<div className="card card-border">
|
<div className="card card-border">
|
||||||
|
<BannerList />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ const FoFcltCntRt: React.FC = () => {
|
|||||||
}, [operFcltStatus, selectedBasin]);
|
}, [operFcltStatus, selectedBasin]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">수계별 시설운영 개소수 및 가동률</h3>
|
<h3 className="text-2xl font-medium">수계별 시설운영 개소수 및 가동률</h3>
|
||||||
<BasinSelect selectedBasin={selectedBasin} onValueChange={setSelectedBasin} />
|
<BasinSelect selectedBasin={selectedBasin} onValueChange={setSelectedBasin} />
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ const FoWqltInflEffl: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">수계별 유입ㆍ방류수질 분석</h3>
|
<h3 className="text-2xl font-medium">수계별 유입ㆍ방류수질 분석</h3>
|
||||||
<BasinSelect selectedBasin={selectedBasin} onValueChange={setSelectedBasin} />
|
<BasinSelect selectedBasin={selectedBasin} onValueChange={setSelectedBasin} />
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import { getCoreRowModel } from "@tanstack/react-table";
|
||||||
|
import { PostApi, type NoticePost } from "@/entities/post";
|
||||||
|
import { ENDPOINT } from "@/shared/config";
|
||||||
|
import { useDataTable } from "@/shared/lib";
|
||||||
|
import {
|
||||||
|
noticePostTableColumns,
|
||||||
|
} from "../model/table-columns";
|
||||||
|
|
||||||
|
|
||||||
|
const NoticeList: React.FC = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const noticePostQuery = useQuery({
|
||||||
|
queryKey: [ENDPOINT.BOARD_SERVICE.POSTS],
|
||||||
|
queryFn: () =>
|
||||||
|
PostApi.getNoticePostList({
|
||||||
|
page: 0,
|
||||||
|
size: 5,
|
||||||
|
keywordType: "bbsTypeCd",
|
||||||
|
keyword: "BBSNOTICE",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNoticePostRowClick = (row: NoticePost) => {
|
||||||
|
router.push(`/bbs/bbsnotice/${row.bbsId}/${row.pstNo}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { tableComponent: noticePostTableComponent } = useDataTable({
|
||||||
|
query: noticePostQuery,
|
||||||
|
tableOptions: {
|
||||||
|
columns: noticePostTableColumns,
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
},
|
||||||
|
dataTableOptions: {
|
||||||
|
variant: "line",
|
||||||
|
onRowClick: handleNoticePostRowClick,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="card card-border !p-0">
|
||||||
|
<div className="card-header !m-0 !border-none text-foreground">
|
||||||
|
<h3 className="text-2xl font-medium">공지사항</h3>
|
||||||
|
</div>
|
||||||
|
{noticePostTableComponent}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default NoticeList;
|
||||||
@@ -176,10 +176,10 @@ const ReuseChart: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">{chartItems[activeIndex]?.title}</h3>
|
<h3 className="text-2xl font-medium">{chartItems[activeIndex]?.title}</h3>
|
||||||
<Button variant="outline" size="sm" className="text-white h-8 py-0 ml-auto">더보기</Button>
|
<Button className="h-[3.2rem]">더보기</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-60 p-4 relative overflow-hidden">
|
<div className="h-60 p-4 relative overflow-hidden">
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ const ReuseStatus: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">물재이용 현황 (재이용량, m³)</h3>
|
<h3 className="text-2xl font-medium">물재이용 현황 (재이용량, m³)</h3>
|
||||||
<Button variant="outline" size="sm" className="text-white h-8 py-0 ml-auto">더보기</Button>
|
<Button className="h-[3.2rem]">더보기</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-70 p-4">
|
<div className="h-70 p-4">
|
||||||
<div className="w-full h-full flex items-center justify-center">
|
<div className="w-full h-full flex items-center justify-center">
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ export default function SnOldPpl() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">노후 하수관로 현황</h3>
|
<h3 className="text-2xl font-medium">노후 하수관로 현황</h3>
|
||||||
<Button variant="outline" size="sm" className="text-white h-8 py-0 ml-auto">더보기</Button>
|
<Button className="h-[3.2rem]">더보기</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-70 p-0 flex items-center justify-center">
|
<div className="h-70 p-0 flex items-center justify-center">
|
||||||
<div style={{ width: "100%", maxWidth: 400, height: 131 }}>
|
<div style={{ width: "100%", maxWidth: 400, height: 131 }}>
|
||||||
|
|||||||
@@ -187,10 +187,11 @@ export default function SnPplStatus() {
|
|||||||
//const data = { labels, datasets };
|
//const data = { labels, datasets };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="card card-border">
|
<div className="card card-border" style={{ height: "250px" }}>
|
||||||
<div className="card-header text-foreground">
|
<div className="card-header text-foreground">
|
||||||
<h3 className="text-2xl font-medium">오수관로 및 우수관로 현황</h3>
|
<h3 className="text-2xl font-medium">오수관로 및 우수관로 현황</h3>
|
||||||
<Button variant="outline" size="sm" className="text-white h-8 py-0 ml-auto">더보기</Button>
|
|
||||||
|
<Button className="h-[3.2rem]">더보기</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="h-60 p-4">
|
<div className="h-60 p-4">
|
||||||
<div style={{ width: "100%", maxWidth: 400, height: 150 }}>
|
<div style={{ width: "100%", maxWidth: 400, height: 150 }}>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { PageTitle } from "@common/components";
|
||||||
|
import { useMenuStore } from "../lib/use-menu-store";
|
||||||
|
|
||||||
|
function PageLayout({ children }: { children: React.ReactNode }) {
|
||||||
|
const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo);
|
||||||
|
|
||||||
|
const menuPaths = currentMenuInfo?.menuPath
|
||||||
|
?.split(">")
|
||||||
|
.map((path) => path.trim());
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-auto min-h-full bg-container pb-2">
|
||||||
|
<PageTitle
|
||||||
|
title={currentMenuInfo?.menuKornNm ?? ""}
|
||||||
|
menuPaths={menuPaths ?? []}
|
||||||
|
/>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { PageLayout };
|
||||||
@@ -10,10 +10,6 @@ function PageLayout({ children }: { children: React.ReactNode }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-auto min-h-full bg-container pb-2">
|
<div className="h-auto min-h-full bg-container pb-2">
|
||||||
<PageTitle
|
|
||||||
title={currentMenuInfo?.menuKornNm ?? ""}
|
|
||||||
menuPaths={menuPaths ?? []}
|
|
||||||
/>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"chart.js": "^4.4.8",
|
"chart.js": "^4.4.8",
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"next-qrcode": "^2.5.1",
|
"next-qrcode": "^2.5.1",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
"react-dropzone": "^14.3.8",
|
"react-dropzone": "^14.3.8",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"js-sha512": "^0.9.0",
|
"js-sha512": "^0.9.0",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"react-hook-form": "^7.54.2",
|
"react-hook-form": "^7.54.2",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.5.2",
|
||||||
"tailwind-merge": "^3.0.1",
|
"tailwind-merge": "^3.0.1",
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"@tailwindcss/vite": "^4.0.10",
|
"@tailwindcss/vite": "^4.0.10",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"select": "^1.1.2",
|
"select": "^1.1.2",
|
||||||
"webpack": "^5.98.0",
|
"webpack": "^5.98.0",
|
||||||
"zustand": "^5.0.3",
|
"zustand": "^5.0.3",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"react-chartjs-2": "^5.3.0",
|
"react-chartjs-2": "^5.3.0",
|
||||||
"react-hot-toast": "^2.5.2",
|
"react-hot-toast": "^2.5.2",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"js-cookie": "^3.0.5",
|
"js-cookie": "^3.0.5",
|
||||||
"lucide-react": "^0.474.0",
|
"lucide-react": "^0.474.0",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"next": "15.3.6",
|
"next": "15.3.8",
|
||||||
"ol": "^7.3.0",
|
"ol": "^7.3.0",
|
||||||
"ol-ext": "^4.0.11",
|
"ol-ext": "^4.0.11",
|
||||||
"proj4": "^2.9.0",
|
"proj4": "^2.9.0",
|
||||||
|
|||||||
+2
-2
@@ -49,5 +49,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"packageManager": "pnpm@10.25.0"
|
"packageManager": "pnpm@10.25.0+sha512.5e82639027af37cf832061bcc6d639c219634488e0f2baebe785028a793de7b525ffcd3f7ff574f5e9860654e098fe852ba8ac5dd5cefe1767d23a020a92f501"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+23
-23
@@ -121,8 +121,8 @@ importers:
|
|||||||
specifier: ^0.474.0
|
specifier: ^0.474.0
|
||||||
version: 0.474.0(react@19.2.1)
|
version: 0.474.0(react@19.2.1)
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
next-qrcode:
|
next-qrcode:
|
||||||
specifier: ^2.5.1
|
specifier: ^2.5.1
|
||||||
version: 2.5.1(react@19.2.1)
|
version: 2.5.1(react@19.2.1)
|
||||||
@@ -251,8 +251,8 @@ importers:
|
|||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
next-themes:
|
next-themes:
|
||||||
specifier: ^0.4.6
|
specifier: ^0.4.6
|
||||||
version: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 0.4.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
@@ -399,8 +399,8 @@ importers:
|
|||||||
specifier: ^0.474.0
|
specifier: ^0.474.0
|
||||||
version: 0.474.0(react@19.2.1)
|
version: 0.474.0(react@19.2.1)
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
next-qrcode:
|
next-qrcode:
|
||||||
specifier: ^2.5.1
|
specifier: ^2.5.1
|
||||||
version: 2.5.1(react@19.2.1)
|
version: 2.5.1(react@19.2.1)
|
||||||
@@ -514,8 +514,8 @@ importers:
|
|||||||
specifier: ^0.474.0
|
specifier: ^0.474.0
|
||||||
version: 0.474.0(react@19.2.1)
|
version: 0.474.0(react@19.2.1)
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
react-chartjs-2:
|
react-chartjs-2:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.1(chart.js@4.5.1)(react@19.2.1)
|
version: 5.3.1(chart.js@4.5.1)(react@19.2.1)
|
||||||
@@ -611,8 +611,8 @@ importers:
|
|||||||
specifier: ^0.474.0
|
specifier: ^0.474.0
|
||||||
version: 0.474.0(react@19.2.1)
|
version: 0.474.0(react@19.2.1)
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
react-hook-form:
|
react-hook-form:
|
||||||
specifier: ^7.54.2
|
specifier: ^7.54.2
|
||||||
version: 7.68.0(react@19.2.1)
|
version: 7.68.0(react@19.2.1)
|
||||||
@@ -696,8 +696,8 @@ importers:
|
|||||||
specifier: ^3.0.5
|
specifier: ^3.0.5
|
||||||
version: 3.0.5
|
version: 3.0.5
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
ol:
|
ol:
|
||||||
specifier: ^7.3.0
|
specifier: ^7.3.0
|
||||||
version: 7.5.2
|
version: 7.5.2
|
||||||
@@ -799,8 +799,8 @@ importers:
|
|||||||
specifier: ^2.30.1
|
specifier: ^2.30.1
|
||||||
version: 2.30.1
|
version: 2.30.1
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
ol:
|
ol:
|
||||||
specifier: ^7.3.0
|
specifier: ^7.3.0
|
||||||
version: 7.5.2
|
version: 7.5.2
|
||||||
@@ -941,8 +941,8 @@ importers:
|
|||||||
specifier: ^2.30.1
|
specifier: ^2.30.1
|
||||||
version: 2.30.1
|
version: 2.30.1
|
||||||
next:
|
next:
|
||||||
specifier: 15.3.6
|
specifier: 15.3.8
|
||||||
version: 15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
version: 15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1)
|
||||||
ol:
|
ol:
|
||||||
specifier: ^7.3.0
|
specifier: ^7.3.0
|
||||||
version: 7.5.2
|
version: 7.5.2
|
||||||
@@ -2341,8 +2341,8 @@ packages:
|
|||||||
'@napi-rs/wasm-runtime@0.2.12':
|
'@napi-rs/wasm-runtime@0.2.12':
|
||||||
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
|
||||||
|
|
||||||
'@next/env@15.3.6':
|
'@next/env@15.3.8':
|
||||||
resolution: {integrity: sha512-/cK+QPcfRbDZxmI/uckT4lu9pHCfRIPBLqy88MhE+7Vg5hKrEYc333Ae76dn/cw2FBP2bR/GoK/4DU+U7by/Nw==}
|
resolution: {integrity: sha512-SAfHg0g91MQVMPioeFeDjE+8UPF3j3BvHjs8ZKJAUz1BG7eMPvfCKOAgNWJ6s1MLNeP6O2InKQRTNblxPWuq+Q==}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.1.7':
|
'@next/eslint-plugin-next@15.1.7':
|
||||||
resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==}
|
resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==}
|
||||||
@@ -5478,8 +5478,8 @@ packages:
|
|||||||
react: ^19.2.1
|
react: ^19.2.1
|
||||||
react-dom: ^19.2.1
|
react-dom: ^19.2.1
|
||||||
|
|
||||||
next@15.3.6:
|
next@15.3.8:
|
||||||
resolution: {integrity: sha512-oI6D1zbbsh6JzzZFDCSHnnx6Qpvd1fSkVJu/5d8uluqnxzuoqtodVZjYvNovooznUq8udSAiKp7MbwlfZ8Gm6w==}
|
resolution: {integrity: sha512-L+4c5Hlr84fuaNADZbB9+ceRX9/CzwxJ+obXIGHupboB/Q1OLbSUapFs4bO8hnS/E6zV/JDX7sG1QpKVR2bguA==}
|
||||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -7660,7 +7660,7 @@ snapshots:
|
|||||||
'@tybys/wasm-util': 0.10.1
|
'@tybys/wasm-util': 0.10.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@next/env@15.3.6': {}
|
'@next/env@15.3.8': {}
|
||||||
|
|
||||||
'@next/eslint-plugin-next@15.1.7':
|
'@next/eslint-plugin-next@15.1.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -11250,9 +11250,9 @@ snapshots:
|
|||||||
react: 19.2.1
|
react: 19.2.1
|
||||||
react-dom: 19.2.1(react@19.2.1)
|
react-dom: 19.2.1(react@19.2.1)
|
||||||
|
|
||||||
next@15.3.6(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
|
next@15.3.8(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@next/env': 15.3.6
|
'@next/env': 15.3.8
|
||||||
'@swc/counter': 0.1.3
|
'@swc/counter': 0.1.3
|
||||||
'@swc/helpers': 0.5.15
|
'@swc/helpers': 0.5.15
|
||||||
busboy: 1.6.0
|
busboy: 1.6.0
|
||||||
|
|||||||
Reference in New Issue
Block a user