Compare commits

...

47 Commits

Author SHA1 Message Date
thkim 432cad27ab feat: 관리자 - 환경설정 - 위원회 코드관리 목록 구현 - 총괄위원회 목록 가져오기 구현 건 2024-02-23 14:20:23 +09:00
유민형 a357df161d 게시물관리 작업중 2024-02-20 15:06:42 +09:00
유민형 ff036473d7 게시판관리 버튼 수정 2024-02-14 15:32:38 +09:00
강석 최 694f0ef918 코드 정리 2024-02-14 09:24:19 +09:00
강석 최 bf3a460610 Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-02-13 17:59:34 +09:00
강석 최 f210629907 북마크 모달 오류 수정. 2024-02-13 17:59:31 +09:00
유민형 1fd7ba0fb1 게시판관리 조회, 삭제, 날짜포맷 수정 2024-02-13 17:25:29 +09:00
Lim\jun cbfb9b2ca3 기본 포멧 업뎃 2024-02-13 09:18:41 +09:00
Lim\jun 6ca96a65ad 기본 포멧 업뎃 2024-02-13 09:15:48 +09:00
Lim\jun a2fcdfe569 기본 포멧 업뎃 2024-02-13 09:14:09 +09:00
강석 최 60521e4995 관리자 로그인 아이피 제한 해제. 2024-02-13 09:07:21 +09:00
유민형 e825207231 게시판관리 추가, 수정 2024-02-08 15:52:22 +09:00
강석 최 a63a47a7b8 기준코드 뷰어 수정중. 2024-02-08 15:46:10 +09:00
Lim\jun b84bb9a7a9 Dashboard - 다운로드수 업뎃 2024-02-07 19:02:07 +09:00
Lim\jun 8e22399ec4 Dashboard - 다운로드수 2024-02-07 18:52:29 +09:00
강석 최 f14ee3b03d Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-02-07 18:01:26 +09:00
강석 최 701e813fbb 기준코드 뷰어 수정중. 2024-02-07 18:01:23 +09:00
Lim\jun 2ec64ceac0 Dashboard - 메뉴접속수 / 방문자수 2024-02-07 14:57:15 +09:00
Lim\jun 9e3a856d23 확장자 변경 2024-02-07 09:22:04 +09:00
강석 최 b5003f30d2 Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-02-06 18:02:36 +09:00
강석 최 7c5a94ed38 기준코드 목록 화면 구성 완료.
기준코드 뷰어 수정중.
2024-02-06 18:02:33 +09:00
Lim\jun 3bf8cb065a 관리자 메인 디자인 2024-02-06 17:35:59 +09:00
강석 최 7a141fff89 Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-02-05 17:53:21 +09:00
강석 최 a476f3e00c 통합 다운로드 모달 내용 추가.
기준코드 목록 페이지 css 조정.
2024-02-05 17:53:18 +09:00
유민형 1f5485249b Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-02-05 14:51:10 +09:00
유민형 1cd9cf8202 게시판관리 추가/수정 모달 2024-02-05 14:51:06 +09:00
Lim\jun 94963507c4 배경색 제거 2024-02-05 13:08:48 +09:00
Lim\jun 76ce7a9a36 소스정리 2024-02-05 11:56:29 +09:00
강석 최 64e4b07953 만료 토큰 재발급 절차 수정.
기준코드 검색 페이지 즐겨찾기 기능 추가.
2024-02-02 16:50:54 +09:00
강석 최 cdd6f726c2 기준코드 목록 작업중. 2024-02-01 17:58:23 +09:00
강석 최 a7bdd5093f StandardCodeList 분리 2024-02-01 11:42:49 +09:00
강석 최 720033ece6 건설기준코드 검색 기능 추가 2024-02-01 11:30:25 +09:00
강석 최 52e6aed96e 중간저장 2024-01-31 18:00:33 +09:00
Lim\jun dc0046a3c1 Dashboard 중간저장 2024-01-31 17:41:00 +09:00
Lim\jun 7936704abd Dashboard 중간저장 2024-01-31 16:37:36 +09:00
Lim\jun 2bbd80d40b Dashboard 중간저장 2024-01-31 16:28:08 +09:00
Lim\jun ebe40f2f3e Dashboard 중간저장 2024-01-31 16:26:36 +09:00
Lim\jun 33f3654222 Dashboard 중간저장 2024-01-31 16:20:36 +09:00
강석 최 c7440cbc0d 관리자 허용 아이피 추가. 2024-01-31 13:55:29 +09:00
Lim\jun f0fdfbfcbb 필요없는 파일 삭제 2024-01-31 10:04:05 +09:00
강석 최 67852a87d0 .nav 충돌 수정. 2024-01-31 09:51:55 +09:00
강석 최 2ac1241ef1 Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-01-31 09:45:35 +09:00
강석 최 7136da0529 css 주석 해제
sql 오류 수정.
2024-01-31 09:43:35 +09:00
유민형 d413c605cd css 사이드바 밀림 수정 2024-01-31 09:35:52 +09:00
강석 최 c308ec3691 중간저장. 2024-01-30 17:59:30 +09:00
강석 최 3657c16a15 Merge branch 'master' of http://118.219.150.34:50501/DBNT/kcscDev 2024-01-30 13:10:03 +09:00
강석 최 47e4fda988 관리자페이지 검증 로직 수정. 2024-01-30 13:10:00 +09:00
106 changed files with 5743 additions and 2037 deletions

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,8 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@ant-design/colors": "^6.0.0",
"@ant-design/icons": "^4.7.0",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@material-ui/core": "^4.12.4",
@ -10,19 +12,25 @@
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.14.19",
"@mui/styles": "^5.15.3",
"apexcharts": "^3.45.2",
"bootstrap": "^5.3.2",
"date-fns": "^3.2.0",
"prop-types": "^15.8.1",
"qs": "^6.11.0",
"react": "^18.2.0",
"react-apexcharts": "^1.4.0",
"react-bootstrap": "^2.9.0",
"react-copy-to-clipboard": "^5.1.0",
"react-csv": "^2.2.2",
"react-datepicker": "^4.8.0",
"react-dom": "^18.2.0",
"react-element-to-jsx-string": "^15.0.0",
"react-icons": "^4.11.0",
"react-loader-spinner": "^5.4.5",
"react-quill": "^2.0.0",
"react-router-dom": "^6.4.0",
"react-scripts": "5.0.1",
"react-syntax-highlighter": "^15.5.0",
"recharts": "^2.10.3",
"styled-components": "^6.0.9",
"web-vitals": "^2.1.4"

View File

@ -1,4 +1,6 @@
import RootRoutes from './routes';
import ThemeCustomization from 'themes';
import ScrollTop from 'components/ScrollTop';
import React from 'react';
import 'bootstrap/dist/css/bootstrap.min.css';
@ -12,9 +14,11 @@ import './css/Custom/customMain.css'
function App() {
return (
<div className="wrap">
<RootRoutes />
</div>
<ThemeCustomization>
<ScrollTop>
<RootRoutes />
</ScrollTop>
</ThemeCustomization>
)
}

View File

@ -29,65 +29,67 @@ export function requestFetch(url, requestOptions, handler, errorHandler) {
const accessToken = getLocalItem('accessToken');
const sessionUser = parseJwt(accessToken);
const sessionUserId = sessionUser?.id || null;
const refreshToken = getLocalItem('refreshToken');
if(sessionUserId != null){
if( !requestOptions['headers'] ) requestOptions['headers']={}
if( !requestOptions['headers']['Authorization'] ) requestOptions['headers']['Authorization']=null;
requestOptions['headers']['Authorization'] = accessToken;
if(accessToken && new Date(sessionUser.exp*1000) < new Date()){
//만료된 토큰 재발급 절차 진행.
accessTokenRefresh(url, requestOptions, handler, errorHandler);
}else{
if(sessionUserId != null){
if( !requestOptions['headers'] ) requestOptions['headers']={}
if( !requestOptions['headers']['Authorization'] ) requestOptions['headers']['Authorization']=null;
requestOptions['headers']['Authorization'] = accessToken;
}
//CORS ISSUE 로 인한 조치 - origin 및 credentials 추가
// origin 추가
if (!requestOptions['origin']) {
requestOptions = { ...requestOptions, origin: SERVER_URL };
}
// credentials 추가
if (!requestOptions['credentials']) {
requestOptions = { ...requestOptions, credentials: 'include' };
}
fetch(SERVER_URL + url, requestOptions)
.then(response => {// response Stream. Not completion object
return response.json();
})
.then((resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)) {
alert("로그인이 해제되었습니다.")
window.location.href = "/login"
}else{
return resp;
}
})
.then((resp) => {
console.groupCollapsed("requestFetch.then()");
console.log("requestFetch [response] ", resp);
if (typeof handler === 'function') {
handler(resp);
} else {
console.log('egov fetch handler not assigned!');
}
console.groupEnd("requestFetch.then()");
})
.catch(error => {
console.error('There was an error!', error);
if (error === 'TypeError: Failed to fetch') {
alert("서버와의 연결이 원활하지 않습니다. 서버를 확인하세요.");
}
if (typeof errorHandler === 'function') {
errorHandler(error);
} else {
console.error('egov error handler not assigned!');
alert("ERR : " + error.message);
}
})
.finally(() => {
console.log("requestFetch finally end");
console.groupEnd("requestFetch");
});
}
//CORS ISSUE 로 인한 조치 - origin 및 credentials 추가
// origin 추가
if (!requestOptions['origin']) {
requestOptions = { ...requestOptions, origin: SERVER_URL };
}
// credentials 추가
if (!requestOptions['credentials']) {
requestOptions = { ...requestOptions, credentials: 'include' };
}
fetch(SERVER_URL + url, requestOptions)
.then(response => {// response Stream. Not completion object
//console.log("requestFetch [Response Stream] ", response);
return response.json();
})
.then((resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)) {
//accessToken 갱신 요청
accessTokenRefresh(url, requestOptions, handler, errorHandler);
return resp;
} else {
return resp;
}
})
.then((resp) => {
console.groupCollapsed("requestFetch.then()");
console.log("requestFetch [response] ", resp);
if (typeof handler === 'function') {
handler(resp);
} else {
console.log('egov fetch handler not assigned!');
}
console.groupEnd("requestFetch.then()");
})
.catch(error => {
console.error('There was an error!', error);
if (error === 'TypeError: Failed to fetch') {
alert("서버와의 연결이 원활하지 않습니다. 서버를 확인하세요.");
}
if (typeof errorHandler === 'function') {
errorHandler(error);
} else {
console.error('egov error handler not assigned!');
alert("ERR : " + error.message);
}
})
.finally(() => {
console.log("requestFetch finally end");
console.groupEnd("requestFetch");
});
}
function accessTokenRefresh(url, requestOptions, handler, errorHandler){

View File

@ -115,7 +115,7 @@ function EgovHeader({ loginUser, onChangeLogin }) {
{/* <!-- All menu : web --> */}
<div className={`all_menu WEB ${menuDiv?"open":"closed"}`}>
<h2 className="blind">전체메뉴</h2>
<div className="inner">
<div className="inner row">
<div className="col">
<h3>건설기준코드</h3>
<ul>

View File

@ -1,10 +1,16 @@
import React from 'react';
import React, {useEffect, useState} from 'react';
import {Blocks} from "react-loader-spinner";
import {LoadingDiv} from "./Loading.style";
function Loading () {
function Loading ({loadingState}) {
const [visible, setVisible] = useState(loadingState);
useEffect(() => {
setVisible(loadingState)
}, [loadingState]);
return (
<LoadingDiv>
<LoadingDiv $visible={visible}>
<Blocks
height="150"
width="150"

View File

@ -4,4 +4,5 @@ export const LoadingDiv = styled.div`
min-height: 83vh;
padding-top: calc(40vh - 150px);
padding-left: calc(54vw - 150px);
display: ${props=>props.$visible?"block":"none"};
`

View File

@ -0,0 +1,26 @@
import PropTypes from 'prop-types';
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
// ==============================|| NAVIGATION - SCROLL TO TOP ||============================== //
const ScrollTop = ({ children }) => {
const location = useLocation();
const { pathname } = location;
useEffect(() => {
window.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
});
}, [pathname]);
return children || null;
};
ScrollTop.propTypes = {
children: PropTypes.node
};
export default ScrollTop;

View File

@ -0,0 +1,70 @@
import PropTypes from 'prop-types';
// material-ui
import { Box, Chip, Grid, Stack, Typography } from '@mui/material';
// project import
import MainCard from 'components/cards/MainCard';
// assets
import { RiseOutlined, FallOutlined } from '@ant-design/icons';
// ==============================|| STATISTICS - ECOMMERCE CARD ||============================== //
const AnalyticEcommerce = ({ color, title, count, percentage, isLoss, extra }) => (
<MainCard contentSX={{ p: 2.25 }}>
<Stack spacing={0.5}>
<Typography variant="h6" color="textSecondary">
{title}
</Typography>
<Grid container alignItems="center">
<Grid item>
<Typography variant="h4" color="inherit">
{count}
</Typography>
</Grid>
{percentage && (
<Grid item>
<Chip
variant="combined"
color={color}
icon={
<>
{!isLoss && <RiseOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
{isLoss && <FallOutlined style={{ fontSize: '0.75rem', color: 'inherit' }} />}
</>
}
label={`${percentage}%`}
sx={{ ml: 1.25, pl: 1 }}
size="small"
/>
</Grid>
)}
</Grid>
</Stack>
<Box sx={{ pt: 2.25 }}>
<Typography variant="caption" color="textSecondary">
지난달{' '}
<Typography component="span" variant="caption" sx={{ color: `${color || 'primary'}.main` }}>
{extra}
</Typography>{' '}
건이 기록되었습니다.
</Typography>
</Box>
</MainCard>
);
AnalyticEcommerce.propTypes = {
color: PropTypes.string,
title: PropTypes.string,
count: PropTypes.string,
percentage: PropTypes.number,
isLoss: PropTypes.bool,
extra: PropTypes.oneOfType([PropTypes.node, PropTypes.string])
};
AnalyticEcommerce.defaultProps = {
color: 'primary'
};
export default AnalyticEcommerce;

View File

@ -0,0 +1,103 @@
import PropTypes from 'prop-types';
import { forwardRef } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Card, CardContent, CardHeader, Divider, Typography } from '@mui/material';
// project import
import Highlighter from '../third-party/Highlighter';
// header style
const headerSX = {
p: 2.5,
'& .MuiCardHeader-action': { m: '0px auto', alignSelf: 'center' }
};
// ==============================|| CUSTOM - MAIN CARD ||============================== //
const MainCard = forwardRef(
(
{
border = true,
boxShadow,
children,
content = true,
contentSX = {},
darkTitle,
elevation,
secondary,
shadow,
sx = {},
title,
codeHighlight,
...others
},
ref
) => {
const theme = useTheme();
boxShadow = theme.palette.mode === 'dark' ? boxShadow || true : boxShadow;
return (
<Card
elevation={elevation || 0}
ref={ref}
{...others}
sx={{
border: border ? '1px solid' : 'none',
borderRadius: 2,
borderColor: theme.palette.mode === 'dark' ? theme.palette.divider : theme.palette.grey.A800,
boxShadow: boxShadow && (!border || theme.palette.mode === 'dark') ? shadow || theme.customShadows.z1 : 'inherit',
':hover': {
boxShadow: boxShadow ? shadow || theme.customShadows.z1 : 'inherit'
},
'& pre': {
m: 0,
p: '16px !important',
fontFamily: theme.typography.fontFamily,
fontSize: '0.75rem'
},
...sx
}}
>
{/* card header and action */}
{!darkTitle && title && (
<CardHeader sx={headerSX} titleTypographyProps={{ variant: 'subtitle1' }} title={title} action={secondary} />
)}
{darkTitle && title && <CardHeader sx={headerSX} title={<Typography variant="h3">{title}</Typography>} action={secondary} />}
{/* card content */}
{content && <CardContent sx={contentSX}>{children}</CardContent>}
{!content && children}
{/* card footer - clipboard & highlighter */}
{codeHighlight && (
<>
<Divider sx={{ borderStyle: 'dashed' }} />
<Highlighter codeHighlight={codeHighlight} main>
{children}
</Highlighter>
</>
)}
</Card>
);
}
);
MainCard.propTypes = {
border: PropTypes.bool,
boxShadow: PropTypes.bool,
contentSX: PropTypes.object,
darkTitle: PropTypes.bool,
divider: PropTypes.bool,
elevation: PropTypes.number,
secondary: PropTypes.node,
shadow: PropTypes.string,
sx: PropTypes.object,
title: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
codeHighlight: PropTypes.bool,
content: PropTypes.bool,
children: PropTypes.node
};
export default MainCard;

View File

@ -18,15 +18,16 @@ import FormDialog from '../../components/alert/FormDialog';
function generate(items, element, onClickListner) {
function generate(items, element, onClickListner,nameKey,
idKey) {
return items.map((value, index) =>
React.cloneElement(element, {
key: value,
key: value[nameKey],
},
<Card fullWidth sx={{ '&': { boxShadow: 'none' } }}>
<CardActionArea fullWidth sx={{ px: 1 }}>
<ListItemText
primary={value}
primary={value[nameKey]}
key={index}
data-index={index}
onClick={(e) => {onClickListner(e, index);}}
@ -99,7 +100,9 @@ function ListCreateUpdateDelete(props) {
}
>
</ListItem>,
onClickItem
onClickItem,
props.nameKey,
props.idKey
)}
</List>
</Demo>

View File

@ -28,12 +28,10 @@ function ListLabelInputs(props) {
useEffect(function () {
setItems(props.items);
console.log( props.items );
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props]);
useEffect(function () {
console.log( items );
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [items]);

View File

@ -0,0 +1,65 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
// material-ui
import { Box, CardActions, Collapse, Divider, IconButton, Tooltip } from '@mui/material';
// third-party
import { CopyToClipboard } from 'react-copy-to-clipboard';
import reactElementToJSXString from 'react-element-to-jsx-string';
// project import
import SyntaxHighlight from 'utils/SyntaxHighlight';
// assets
import { CodeOutlined, CopyOutlined } from '@ant-design/icons';
// ==============================|| CLIPBOARD & HIGHLIGHTER ||============================== //
const Highlighter = ({ children }) => {
const [highlight, setHighlight] = useState(false);
return (
<Box sx={{ position: 'relative' }}>
<CardActions sx={{ justifyContent: 'flex-end', p: 1, mb: highlight ? 1 : 0 }}>
<Box sx={{ display: 'flex', position: 'inherit', right: 0, top: 6 }}>
<CopyToClipboard text={reactElementToJSXString(children, { showFunctions: true, maxInlineAttributesLineLength: 100 })}>
<Tooltip title="Copy the source" placement="top-end">
<IconButton color="secondary" size="small" sx={{ fontSize: '0.875rem' }}>
<CopyOutlined />
</IconButton>
</Tooltip>
</CopyToClipboard>
<Divider orientation="vertical" variant="middle" flexItem sx={{ mx: 1 }} />
<Tooltip title="Show the source" placement="top-end">
<IconButton
sx={{ fontSize: '0.875rem' }}
size="small"
color={highlight ? 'primary' : 'secondary'}
onClick={() => setHighlight(!highlight)}
>
<CodeOutlined />
</IconButton>
</Tooltip>
</Box>
</CardActions>
<Collapse in={highlight}>
{highlight && (
<SyntaxHighlight>
{reactElementToJSXString(children, {
showFunctions: true,
showDefaultProps: false,
maxInlineAttributesLineLength: 100
})}
</SyntaxHighlight>
)}
</Collapse>
</Box>
);
};
Highlighter.propTypes = {
children: PropTypes.node
};
export default Highlighter;

View File

@ -46,14 +46,27 @@
.code_list .result .List_Codes >div:nth-child(7){
font-size: 14px;
}
.codelistcontent{
padding: 0 0;
width: 80%;
.standard_code_modal .head >span:nth-child(1),.standard_code_modal .result .list_item >div:nth-child(1){width: 12%;}
.standard_code_modal .head >span:nth-child(2),.standard_code_modal .result .list_item >div:nth-child(2){width: 15%;}
.standard_code_modal .head >span:nth-child(3),.standard_code_modal .result .list_item >div:nth-child(3){width: 10%;}
.standard_code_result{
max-height: 520px;
overflow-y: auto;
}
.download_list{
max-height: 550px;
overflow-y: auto;
}
.codeListContent{
padding: 10px 0;
width: 100%;
}
.StandardCodeList{
max-width: 100%;
}
.listtablediv{
.listTableDiv{
padding: 0 !important;
}
.vieweratag{

View File

@ -50,7 +50,7 @@ body {min-width: 1400px;}
.c_wrap .layout {display: table; width: 100%; table-layout: fixed; padding-bottom: 20px;} /* added by lim padding-bottom: 20px; */
/* sub navigation */
.c_wrap .layout .nav {display: table-cell; width: 220px; vertical-align: top;} /* changed by lim width: 260px; */
.c_wrap .layout .nav:not(.tabs) {display: table-cell; width: 220px; vertical-align: top;} /* changed by lim width: 260px; */
.c_wrap .layout .nav .inner {border: 1px solid #dde2e5; border-radius: 10px;}
.nav_title{padding: 35px 30px 26px 30px;} /* changed by lim border-bottom: 4px solid #dde2e5; */
.c_wrap .layout .nav h2 {color: #222; font-size: 24px;}

View File

@ -0,0 +1,199 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import EgovPaging from 'components/EgovPaging';
import { itemIdxByPage } from 'utils/calc';
function EgovAdminBoardList(props) {
console.group("EgovAdminBoardList");
console.log("[Start] EgovAdminBoardList ------------------------------");
console.log("EgovAdminBoardList [props] : ", props);
const location = useLocation();
console.log("EgovAdminBoardList [location] : ", location);
// eslint-disable-next-line no-unused-vars
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || { pageIndex: 1, searchCnd: '0', searchWrd: '' });// ||
const [paginationInfo, setPaginationInfo] = useState({});
const cndRef = useRef();
const wrdRef = useRef();
const [listTag, setListTag] = useState([]);
const retrieveList = useCallback((srchCnd) => {
console.groupCollapsed("EgovAdminBoardList.retrieveList()");
const retrieveListURL = '/cop/bbs/selectBBSMasterInfsAPI.do';
const requestOptions = {
method: "POST",
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify(srchCnd)
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
setPaginationInfo(resp.result.paginationInfo);
let mutListTag = [];
listTag.push(<p className="no_data" key="0">검색된 결과가 없습니다.</p>); //
const resultCnt = parseInt(resp.result.resultCnt);
const currentPageNo = resp.result.paginationInfo.currentPageNo;
const pageSize = resp.result.paginationInfo.pageSize;
//
resp.result.resultList.forEach(function (item, index) {
if (index === 0) mutListTag = []; //
const listIdx = itemIdxByPage(resultCnt , currentPageNo, pageSize, index);
mutListTag.push(
<Link
to={{pathname: URL.ADMIN_BOARD_MODIFY}}
state={{
bbsId: item.bbsId,
searchCondition: searchCondition
}}
key={listIdx}
className="list_item">
<div>{listIdx}</div>
<div>{item.bbsNm}</div>
<div>{item.bbsTyCodeNm}</div>
<div>{item.bbsAttrbCodeNm}</div>
<div>{item.frstRegisterPnttm}</div>
<div>{item.useAt === "Y" ? "사용" : "사용안함"}</div>
</Link>
);
});
setListTag(mutListTag);
},
function (resp) {
console.log("err response : ", resp);
}
);
console.groupEnd("EgovAdminBoardList.retrieveList()");
},[listTag, searchCondition]);
useEffect(() => {
retrieveList(searchCondition);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
console.log("------------------------------EgovAdminBoardList [End]");
console.groupEnd("EgovAdminBoardList");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>게시판생성 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents BOARD_CREATE_LIST" id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">사이트관리</h1>
</div>
<h2 className="tit_2">게시판생성 관리</h2>
{/* <!-- 검색조건 --> */}
<div className="condition">
<ul>
<li className="third_1 L">
<span className="lb">검색유형선택</span>
<label className="f_select" htmlFor="searchCnd">
<select id="searchCnd" name="searchCnd" title="검색유형선택" ref={cndRef}
onChange={e => {
cndRef.current.value = e.target.value;
}}
>
<option value="0">게시판명</option>
<option value="1">게시판유형</option>
</select>
</label>
</li>
<li className="third_2 R">
<span className="lb">검색어</span>
<span className="f_search w_400">
<input type="text" name="" defaultValue={searchCondition && searchCondition.searchWrd} placeholder="" ref={wrdRef}
onChange={e => {
wrdRef.current.value = e.target.value;
}}
/>
<button type="button"
onClick={() => {
retrieveList({ ...searchCondition, pageIndex: 1, searchCnd: cndRef.current.value, searchWrd: wrdRef.current.value });
}}>조회</button>
</span>
</li>
<li>
<Link to={URL.ADMIN_BOARD_CREATE} className="btn btn_blue_h46 pd35">등록</Link>
</li>
</ul>
</div>
{/* <!--// 검색조건 --> */}
{/* <!-- 게시판목록 --> */}
<div className="board_list BRD006">
<div className="head">
<span>번호</span>
<span>게시판명</span>
<span>게시판유형</span>
<span>게시판속성</span>
<span>생성일</span>
<span>사용여부</span>
</div>
<div className="result">
{listTag}
</div>
</div>
{/* <!--// 게시판목록 --> */}
<div className="board_bot">
{/* <!-- Paging --> */}
<EgovPaging pagination={paginationInfo} moveToPage={passedPage => {
retrieveList({ ...searchCondition, pageIndex: passedPage, searchCnd: cndRef.current.value, searchWrd: wrdRef.current.value })
}} />
{/* <!--/ Paging --> */}
</div>
{/* <!--// 본문 --> */}
</div>
</div>
</div>
</div>
);
}
export default EgovAdminBoardList;

View File

@ -1,430 +1,162 @@
import React, { useState, useEffect, useRef } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import React, {useState, useEffect, useRef} from 'react';
import {Link, useNavigate, useLocation, useParams} from 'react-router-dom';
import Modal from "react-bootstrap/Modal";
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import {default as EgovLeftNav} from 'components/leftmenu/EgovLeftNavAdmin';
import EgovRadioButtonGroup from 'components/EgovRadioButtonGroup';
import {Form} from "react-bootstrap";
function EgovAdminBoardEdit(props) {
function EgovAdminBoardEdit({props, reloadFunction}) {
console.group("EgovAdminBoardEdit");
console.log("[Start] EgovAdminBoardEdit ------------------------------");
console.log("EgovAdminBoardEdit [props] : ", props);
const navigate = useNavigate();
const location = useLocation();
const checkRef = useRef([]);
const checkRef = useRef([]);
console.log("EgovAdminBoardEdit [location] : ", location);
const replyPosblAtRadioGroup = [{ value: "Y", label: "가능" }, { value: "N", label: "불가능" }];
const fileAtchPosblAtRadioGroup = [{ value: "Y", label: "가능" }, { value: "N", label: "불가능" }];
const bbsTyCodeOptions = [{ value: "", label: "선택" }, { value: "BBST01", label: "일반게시판" }, { value: "BBST03", label: "공지게시판" }];
const bbsAttrbCodeOptions = [{ value: "", label: "선택" }, { value: "BBSA02", label: "갤러리" }, { value: "BBSA03", label: "일반게시판" }];
const posblAtchFileNumberOptions = [{ value: 0, label: "선택하세요" }, { value: 1, label: "1개" }, { value: 2, label: "2개" }, { value: 3, label: "3개" }];
const bbsId = location.state?.bbsId || "";
let item = null;
item = props;
console.log("@@@ item : " + JSON.stringify(item));
const [modeInfo, setModeInfo] = useState({ mode: props.mode });
const [modeInfo, setModeInfo] = useState(item != null ? {mode: props.mode} : {mode: CODE.MODE_CREATE});
const [boardDetail, setBoardDetail] = useState({});
const initMode = () => {
switch (props.mode) {
case CODE.MODE_CREATE:
setModeInfo({
...modeInfo,
modeTitle: "등록",
editURL: '/cop/bbs/insertBBSMasterInfAPI.do'
});
break;
case CODE.MODE_MODIFY:
setModeInfo({
...modeInfo,
modeTitle: "수정",
editURL: `/cop/bbs/updateBBSMasterInfAPI/${bbsId}.do`
});
break;
default:
navigate({pathname: URL.ERROR}, {state: {msg : ""}});
}
retrieveDetail();
}
const retrieveDetail = () => {
if (modeInfo.mode === CODE.MODE_CREATE) {// /
setBoardDetail({
tmplatId: "TMPLAT_BOARD_DEFAULT", //Template
replyPosblAt: "Y", //
fileAtchPosblAt: "Y" //
});
return;
}
const retrieveDetailURL = '/cop/bbs/selectBBSMasterInfAPI.do';
const requestOptions = {
method: "POST",
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
bbsId: bbsId
})
}
EgovNet.requestFetch(retrieveDetailURL,
requestOptions,
function (resp) {
//
if (modeInfo.mode === CODE.MODE_MODIFY) {
setBoardDetail(resp.result.boardMasterVO);
}
}
);
}
const formValidator = (formData) => {
if (formData.get('bbsNm') === null || formData.get('bbsNm') === "") {
alert("게시판명은 필수 값입니다.");
return false;
}
if (formData.get('bbsIntrcn') === null || formData.get('bbsIntrcn') === "") {
alert("게시판 소개는 필수 값입니다.");
return false;
}
if (formData.get('bbsTyCode') === null || formData.get('bbsTyCode') === "") {
alert("게시판 유형은 필수 값입니다.");
return false;
}
if (formData.get('bbsAttrbCode') === null || formData.get('bbsAttrbCode') === "") {
alert("게시판 속성은 필수 값입니다.");
return false;
}
if (formData.get('posblAtchFileNumber') === null || formData.get('posblAtchFileNumber') === "") {
alert("첨부파일 가능 숫자는 필수 값입니다.");
return false;
}
return true;
};
const formObjValidator = (checkRef) => {
if(checkRef.current[0].value === ""){
alert("게시판명은 필수 값입니다.");
return false;
}
if(checkRef.current[1].value === ""){
alert("게시판 소개는 필수 값입니다.");
return false;
}
if(checkRef.current[2].value === "0"){
alert("첨부파일 가능 숫자는 필수 값입니다.");
return false;
}
return true;
};
const updateBoard = () => {
let modeStr = modeInfo.mode === CODE.MODE_CREATE ? "POST" : "PUT";
let requestOptions ={};
if (modeStr === "POST") {
const formData = new FormData();
for (let key in boardDetail) {
formData.append(key, boardDetail[key]);
//console.log("boardDetail [%s] ", key, boardDetail[key]);
}
if (formValidator(formData)) {
requestOptions = {
method: modeStr,
headers: {
},
body: formData
}
EgovNet.requestFetch(modeInfo.editURL,
requestOptions,
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
navigate({ pathname: URL.ADMIN_BOARD });
} else {
navigate({pathname: URL.ERROR}, {state: {msg : resp.resultMessage}});
}
}
);
};
} else {
if (formObjValidator(checkRef)) {
requestOptions = {
method: modeStr,
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({...boardDetail})
}
EgovNet.requestFetch(modeInfo.editURL,
requestOptions,
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
navigate({ pathname: URL.ADMIN_BOARD });
} else {
navigate({pathname: URL.ERROR}, {state: {msg : resp.resultMessage}});
}
}
);
}
}
};
const deleteBoardArticle = (bbsId) => {
const deleteBoardURL = `/cop/bbs/deleteBBSMasterInfAPI/${bbsId}.do`;
const requestOptions = {
method: "PUT",
headers: {
'Content-type': 'application/json',
},
body: JSON.stringify({
bbsId: bbsId
})
}
EgovNet.requestFetch(deleteBoardURL,
requestOptions,
(resp) => {
console.log("====>>> board delete= ", resp);
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("게시글이 삭제되었습니다.")
navigate(URL.ADMIN_BOARD, { replace: true });
} else {
alert("ERR : " + resp.resultMessage);
}
}
);
}
const getSelectedLabel = (objArray, findLabel = "") => {
let foundValueLabelObj = objArray.find(o => o['value'] === findLabel);
return foundValueLabelObj['label'];
}
console.log("@@@ mode : " + modeInfo.mode);
useEffect(() => {
initMode();
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const initMode = () => {
if (modeInfo.mode === CODE.MODE_MODIFY) {
setBoardDetail(item);
}
}
function editBoard(e) {
e.preventDefault();
e.stopPropagation();
const form = e.target;
const info = {
bbsId: form.bbsId.value,
bbsTitle: form.bbsTitle.value,
bbsDesc: form.bbsDesc.value
}
if (modeInfo.mode === CODE.MODE_MODIFY) {
info.bbsSeq = props.bbsSeq;
}
EgovNet.requestFetch(
'/admin/boards/board-mgt',
{
method: "PUT",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(info)
},
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("저장되었습니다.");
reloadFunction();
} else if (Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)) {
console.log("토큰 갱신중.")
} else {
alert(resp.result.resultMessage)
}
}
)
}
function deleteBoard(bbs){
if(window.confirm("삭제하시겠습니까?")) {
EgovNet.requestFetch(
'/admin/boards/board-mgt',
{
method: "DELETE",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(bbs)
},
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("삭제되었습니다.")
reloadFunction();
} else if (Number(resp.resultCode) === Number(CODE.RCV_ERROR_AUTH)) {
console.log("토큰 갱신중.")
} else {
alert(resp.result.resultMessage)
}
}
)
}
}
console.log("------------------------------EgovAdminBoardEdit [End]");
console.groupEnd("EgovAdminBoardEdit");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>게시판생성 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<>
{/* <!-- 본문 --> */}
<Modal.Header closeButton>
<Modal.Title>
{modeInfo.mode === CODE.MODE_CREATE && '게시판 생성'}
{modeInfo.mode === CODE.MODE_MODIFY && '게시판 수정'}
</Modal.Title>
</Modal.Header>
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<Modal.Body>
<div className="board_view2">
<Form onSubmit={(e) => {editBoard(e)}} noValidate>
<dl>
<dt><label htmlFor="bbsId">게시판 ID</label><span className="req">필수</span></dt>
<dd>
<Form.Control className="f_input2 w_full" type="text" name="bbsId" placeholder="게시판 ID" required
defaultValue={props?.bbsId} readOnly={props!==undefined}/>
</dd>
</dl>
<dl>
<dt><label htmlFor="bbsTitle">게시판명</label><span className="req">필수</span></dt>
<dd>
<Form.Control className="f_input2 w_full" type="text" name="bbsTitle" placeholder="게시판명" required
defaultValue={props?.bbsTitle}/>
</dd>
</dl>
<dl>
<dt><label htmlFor="bbsDesc">게시판 소개</label><span className="req">필수</span></dt>
<dd>
<Form.Control className="f_txtar w_full h_100" as="textarea" name="bbsDesc" placeholder="게시판 소개" required
defaultValue={props?.bbsDesc}/>
</dd>
</dl>
<div className="contents BOARD_CREATE_REG" id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">사이트관리</h1>
</div>
{modeInfo.mode === CODE.MODE_CREATE &&
<h2 className="tit_2">게시판 생성</h2>
}
{modeInfo.mode === CODE.MODE_MODIFY &&
<h2 className="tit_2">게시판 수정</h2>
}
<div className="board_view2">
<dl>
<dt><label htmlFor="bbsNm">게시판명</label><span className="req">필수</span></dt>
<dd>
<input className="f_input2 w_full" type="text" name="bbsNm" title="" id="bbsNm" placeholder=""
defaultValue={boardDetail.bbsNm}
onChange={e => setBoardDetail({ ...boardDetail, bbsNm: e.target.value })}
ref={el => (checkRef.current[0] = el)}
/>
</dd>
</dl>
<dl>
<dt><label htmlFor="bbsIntrcn">게시판 소개</label><span className="req">필수</span></dt>
<dd>
<textarea className="f_txtar w_full h_100" name="bbsIntrcn" id="bbsIntrcn" cols="30" rows="10" placeholder=""
defaultValue={boardDetail.bbsIntrcn}
onChange={e => setBoardDetail({ ...boardDetail, bbsIntrcn: e.target.value })}
ref={el => (checkRef.current[1] = el)}
></textarea>
</dd>
</dl>
<dl>
<dt>게시판 유형<span className="req">필수</span></dt>
<dd>
{/* 수정/조회 일때 변경 불가 */}
{modeInfo.mode === CODE.MODE_CREATE &&
<label className="f_select w_130" htmlFor="bbsTyCode">
<select
id="bbsTyCode"
name="bbsTyCode"
title="게시판유형선택"
onChange={(e) => setBoardDetail({ ...boardDetail, bbsTyCode: e.target.value })}
value={boardDetail.bbsTyCode}
>
{bbsTyCodeOptions.map((option, i) => {
return (
<option value={option.value} key={option.value}>
{option.label}
</option>
)
})}
</select>
</label>
}
{modeInfo.mode === CODE.MODE_MODIFY &&
<span>
{boardDetail.bbsTyCode && getSelectedLabel(bbsTyCodeOptions, boardDetail.bbsTyCode)}
</span>
}
</dd>
</dl>
<dl>
<dt>게시판 속성<span className="req">필수</span></dt>
<dd>
{/* 등록 일때 변경 가능 */}
{modeInfo.mode === CODE.MODE_CREATE &&
<label className="f_select w_130" htmlFor="bbsAttrbCode">
<select
id="bbsAttrbCode"
name="bbsAttrbCode"
title="게시판속성선택"
onChange={(e) => setBoardDetail({ ...boardDetail, bbsAttrbCode: e.target.value })}
value={boardDetail.bbsAttrbCode}
>
{bbsAttrbCodeOptions.map((option, i) => {
return (
<option value={option.value} key={option.value}>
{option.label}
</option>
)
})}
</select>
</label>
}
{/* 수정/조회 일때 변경 불가 */}
{modeInfo.mode === CODE.MODE_MODIFY &&
<span>
{boardDetail.bbsAttrbCode && getSelectedLabel(bbsAttrbCodeOptions, boardDetail.bbsAttrbCode)}
</span>
}
</dd>
</dl>
<dl>
<dt>답장가능여부<span className="req">필수</span></dt>
<dd>
{/* 등록 일때 변경 가능 */}
{modeInfo.mode === CODE.MODE_CREATE &&
<EgovRadioButtonGroup
name="replyPosblAt"
radioGroup={replyPosblAtRadioGroup}
setValue={boardDetail.replyPosblAt}
setter={(v) => setBoardDetail({ ...boardDetail, replyPosblAt: v })} />
}
{/* 수정/조회 일때 변경 불가 */}
{modeInfo.mode === CODE.MODE_MODIFY &&
<span>
{boardDetail.replyPosblAt && getSelectedLabel(replyPosblAtRadioGroup, boardDetail.replyPosblAt)}
</span>
}
</dd>
</dl>
<dl>
<dt>파일첨부가능여부<span className="req">필수</span></dt>
<dd>
<EgovRadioButtonGroup
name="fileAtchPosblAt"
radioGroup={fileAtchPosblAtRadioGroup}
setValue={boardDetail.fileAtchPosblAt}
setter={(v) => setBoardDetail({ ...boardDetail, fileAtchPosblAt: v })} />
</dd>
</dl>
<dl>
<dt><label htmlFor="schdulDeptName">첨부파일가능파일 숫자</label><span className="req">필수</span></dt>
<dd>
<label className="f_select " htmlFor="posblAtchFileNumber">
<select
id="posblAtchFileNumber"
name="posblAtchFileNumber"
title="첨부가능파일 숫자선택"
onChange={(e) => setBoardDetail({ ...boardDetail, posblAtchFileNumber: e.target.value })}
value={boardDetail.posblAtchFileNumber}
ref={el => (checkRef.current[2] = el)}
>
{posblAtchFileNumberOptions.map((option, i) => {
return (
<option value={option.value} key={option.value}>
{option.label}
</option>
)
})}
</select>
</label>
</dd>
</dl>
{/* <!-- 버튼영역 --> */}
<div className="board_btn_area">
<div className="left_col btn1">
<button className="btn btn_skyblue_h46 w_100"
onClick={() => updateBoard()}>저장</button>
{modeInfo.mode === CODE.MODE_MODIFY &&
<button className="btn btn_skyblue_h46 w_100" onClick={() => {
deleteBoardArticle(boardDetail.bbsId);
}}>삭제</button>
}
</div>
<div className="right_col btn1">
<Link to={URL.ADMIN_BOARD} className="btn btn_blue_h46 w_100">목록</Link>
</div>
{/* <!-- 버튼영역 --> */}
<div className="board_btn_area">
<div className="left_col btn1">
<button type="submit" className="btn btn_skyblue_h46 w_100">저장
</button>
{modeInfo.mode === CODE.MODE_MODIFY &&
<button type={"button"} className="btn btn_skyblue_h46 w_100" onClick={()=>{deleteBoard(props)}}>삭제</button>
}
</div>
{/* <!--// 버튼영역 --> */}
</div>
{/* <!--// 본문 --> */}
</div>
<div className="right_col btn1">
<button type={"button"} className="btn btn_blue_h46 w_100" onClick={()=>{reloadFunction()}}>목록</button>
</div>
</div>
{/* <!--// 버튼영역 --> */}
</Form>
</div>
</div>
</div>
</Modal.Body>
</>
);
}

View File

@ -1,13 +1,41 @@
import React from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
function Keywords(props) {
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
function StandardCodeMgt(props) {
return (
<div className="container">
Keywords
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN} >사이트관리</Link></li>
<li>게시판현황</li>
<li>키워드 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents NOTICE_LIST" id="contents">
<div className="top_tit">
<h1 className="tit_1">키워드 관리</h1>
</div>
</div>
</div>
</div>
</div>
);
}
export default Keywords;
export default StandardCodeMgt;

View File

@ -3,11 +3,12 @@ import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import EgovPaging from 'components/EgovPaging';
import { itemIdxByPage } from 'utils/calc';
import Modal from "react-bootstrap/Modal";
import EgovAdminBoardEdit from "../board/EgovAdminBoardEdit";
import {format} from "date-fns";
function EgovAdminBoardList(props) {
console.group("EgovAdminBoardList");
@ -20,10 +21,15 @@ function EgovAdminBoardList(props) {
// eslint-disable-next-line no-unused-vars
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || { pageIndex: 1, searchCnd: '0', searchWrd: '' });// ||
const [paginationInfo, setPaginationInfo] = useState({});
const [listTag, setListTag] = useState([]);
const [show, setShow] = useState(false);
const [modalBody, setModalBody] = useState();
const handleClose = () => setShow(false);
const handleShow = () => setShow(true);
const retrieveList = useCallback(() => {
handleClose();
console.groupCollapsed("AdminBoardList.retrieveList()");
const retrieveListURL = '/admin/boards/board-list';
@ -49,14 +55,15 @@ function EgovAdminBoardList(props) {
if (index === 0) mutListTag = []; //
mutListTag.push(
<Link className="list_item">
<div className="list_item">
<div>{item.bbsSeq}</div>
<div>{item.bbsId}</div>
<div>{item.bbsTitle}</div>
<div>{item.frstCrtId}</div>
<div>{item.frstCrtDt}</div>
<div>{item.lastChgDt}</div>
</Link>
<div>{item.frstCrtDt ? format(item.frstCrtDt, "yyyy-MM-dd HH:mm") : ""}</div>
<div>{item.lastChgDt ? format(item.lastChgDt, "yyyy-MM-dd HH:mm") : ""}</div>
<div><button className={"btn btn_blue_h31 px-1"} onClick={()=>{editBoard(item)}}>수정</button></div>
</div>
);
});
@ -75,6 +82,14 @@ function EgovAdminBoardList(props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
function editBoard(item){
handleShow();
if(item != undefined) {
item.mode = CODE.MODE_MODIFY;
}
setModalBody(<EgovAdminBoardEdit props={item} reloadFunction={retrieveList}/>)
}
console.log("------------------------------EgovAdminBoardList [End]");
console.groupEnd("EgovAdminBoardList");
return (
@ -150,6 +165,7 @@ function EgovAdminBoardList(props) {
<span>작성자</span>
<span>작성일</span>
<span>수정일</span>
<span><button className={"btn btn_blue_h31 px-1"} onClick={()=>{editBoard(undefined)}}>추가</button></span>
</div>
<div className="result">
{listTag}
@ -169,6 +185,9 @@ function EgovAdminBoardList(props) {
</div>
</div>
</div>
<Modal show={show} onHide={handleClose} keyboard={false}>
{modalBody}
</Modal>
</div>

View File

@ -1,13 +1,41 @@
import React from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
function Posts(props) {
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
function StandardCodeMgt(props) {
return (
<div className="container">
Posts
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN} >사이트관리</Link></li>
<li>게시판현황</li>
<li>게시물 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents NOTICE_LIST" id="contents">
<div className="top_tit">
<h1 className="tit_1">게시물 관리</h1>
</div>
</div>
</div>
</div>
</div>
);
}
export default Posts;
export default StandardCodeMgt;

View File

@ -1,13 +1,41 @@
import React from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
function AboutSiteMgt(props) {
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
function StandardCodeMgt(props) {
return (
<div className="container">
AboutSiteMgt
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN} >사이트관리</Link></li>
<li>환경설정</li>
<li>관련사이트 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents NOTICE_LIST" id="contents">
<div className="top_tit">
<h1 className="tit_1">관련사이트 관리</h1>
</div>
</div>
</div>
</div>
</div>
);
}
export default AboutSiteMgt;
export default StandardCodeMgt;

View File

@ -1,25 +1,8 @@
import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { styled } from '@mui/material/styles';
import Box from '@mui/material/Box';
import Paper from '@mui/material/Paper';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import EditIcon from '@mui/icons-material/Edit';
import ListItemAvatar from '@mui/material/ListItemAvatar';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Avatar from '@mui/material/Avatar';
import IconButton from '@mui/material/IconButton';
import FormGroup from '@mui/material/FormGroup';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import Grid from '@mui/material/Grid';
import Typography from '@mui/material/Typography';
import FolderIcon from '@mui/icons-material/Folder';
import DeleteIcon from '@mui/icons-material/Delete';
import AddIcon from '@mui/icons-material/Add';
import AddBoxOutlinedIcon from '@mui/icons-material/AddBoxOutlined';
import * as EgovNet from 'api/egovFetch';
@ -35,13 +18,12 @@ import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
function CommitteeCodeMgt(props) {
const [dense, setDense] = useState(false);
const [secondary, setSecondary] = useState(false);
const [searchCondition, setSearchCondition] = useState({ paramCodeGroup: null, paramCodeLevel: 'LV_01' });
const [depth01List, setDepth01List] = useState(["중앙건설기술심의", "테스트2"]);
const [depth02List, setDepth02List] = useState(["123", "테스트2"]);
const [depth03List, setDepth03List] = useState(["다람쥐", "쳇바퀴"]);
const [depth04List, setDepth04List] = useState(["임시 텍스트", "text"]);
const [depth01List, setDepth01List] = useState([]);
const [depth02List, setDepth02List] = useState([]);
const [depth03List, setDepth03List] = useState([]);
const [depth04List, setDepth04List] = useState([]);
const [summaryArray, setSummaryArray] = useState({});
const [depth01SelectedIndex, setDepth01SelectedIndex] = React.useState();
@ -49,13 +31,70 @@ function CommitteeCodeMgt(props) {
const [depth03SelectedIndex, setDepth03SelectedIndex] = React.useState();
const [depth04SelectedIndex, setDepth04SelectedIndex] = React.useState();
// '' item , '' .
useEffect(function () {
// 2 .
if( typeof depth01SelectedIndex !== 'undefined' ) {
setSearchCondition({ paramCodeGroup: depth01List[depth01SelectedIndex].orgId, paramCodeLevel: 'LV_02' });
//setDepth02List([]);
}
if( typeof depth02SelectedIndex !== 'undefined' ) {
setSearchCondition({ paramCodeGroup: depth02List[depth02SelectedIndex].orgId, paramCodeLevel: 'LV_03' });
//setDepth03List([]);
}
if( typeof depth03SelectedIndex !== 'undefined' ) {
setSearchCondition({ paramCodeGroup: depth03List[depth03SelectedIndex].orgId, paramCodeLevel: 'LV_04' });
//setDepth04List([]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [depth01SelectedIndex, depth02SelectedIndex, depth03SelectedIndex]);
// .
useEffect(function () {
if( typeof searchCondition !== 'undefined' ) {
getList(searchCondition);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchCondition]);
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json'
}
}
const getList = (searchCondition) => {
EgovNet.requestFetch(`/admin/config/committee-code-management?paramCodeGroup=${searchCondition.paramCodeGroup}&paramCodeLevel=${searchCondition.paramCodeLevel}`,
requestOptions,
function (resp) {
if( searchCondition.paramCodeLevel === 'LV_01' ) {
setDepth01List(resp.result.list);
} else if( searchCondition.paramCodeLevel === 'LV_02' ) {
setDepth02List(resp.result.list);
} else if( searchCondition.paramCodeLevel === 'LV_03' ) {
setDepth03List(resp.result.list);
} else if( searchCondition.paramCodeLevel === 'LV_04' ) {
setDepth04List(resp.result.list);
}
}
);
}
useEffect(function () {
setSummaryArray(
{
"중앙건설기술심의" : depth01List[depth01SelectedIndex],
"총괄위원회" : depth02List[depth02SelectedIndex],
"건설기준위원회" : depth03List[depth03SelectedIndex],
"실무위원회" : depth04List[depth04SelectedIndex],
"중앙건설기술심의" : depth01List[depth01SelectedIndex] && depth01List[depth01SelectedIndex].orgNm ? depth01List[depth01SelectedIndex].orgNm : "",
"총괄위원회" : depth02List[depth02SelectedIndex] && depth02List[depth02SelectedIndex].orgNm ? depth02List[depth02SelectedIndex].orgNm : "",
"건설기준위원회" : depth03List[depth03SelectedIndex] && depth03List[depth03SelectedIndex].orgNm ? depth03List[depth03SelectedIndex].orgNm : "",
"실무위원회" : depth04List[depth04SelectedIndex] && depth04List[depth04SelectedIndex].orgNm ? depth04List[depth04SelectedIndex].orgNm : "",
}
);
console.log(`${depth01List[depth01SelectedIndex]}[${depth01SelectedIndex}]`);
@ -68,13 +107,7 @@ function CommitteeCodeMgt(props) {
depth01SelectedIndex,
depth02SelectedIndex,
depth03SelectedIndex,
depth04SelectedIndex]);
useEffect(function () {
console.log( summaryArray );
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [summaryArray]);
depth04SelectedIndex]);
const Location = React.memo(function Location() {
@ -90,6 +123,8 @@ function CommitteeCodeMgt(props) {
)
});
return (
<div className="container">
<div className="c_wrap">
@ -107,7 +142,6 @@ function CommitteeCodeMgt(props) {
<div className="top_tit">
<h1 className="tit_1">위원회 코드 관리</h1>
</div>
<Box
sx={{
display: 'flex',
@ -119,23 +153,27 @@ function CommitteeCodeMgt(props) {
},
}}
>
<ListCreateUpdateDelete title="중앙건설기술심의" items={depth01List} setItemIndex={setDepth01SelectedIndex}/>
<ListCreateUpdateDelete title="총괄위원회" items={depth02List} setItemIndex={setDepth02SelectedIndex}/>
<ListCreateUpdateDelete title="건설기준위원회" items={depth03List} setItemIndex={setDepth03SelectedIndex}/>
<ListCreateUpdateDelete title="실무위원회" items={depth04List} setItemIndex={setDepth04SelectedIndex}/>
</Box>
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
'& > :not(style)': {
mt: 4,
width: 245,
},
}}
>
<ListLabelInputs title="위원회 코드정보" items={summaryArray} />
<ListCreateUpdateDelete title="중앙건설기술심의" items={depth01List} setItemIndex={setDepth01SelectedIndex} nameKey="orgNm" idKey="orgId" />
<ListCreateUpdateDelete title="총괄위원회" items={depth02List} setItemIndex={setDepth02SelectedIndex} nameKey="orgNm" idKey="orgId" />
<ListCreateUpdateDelete title="건설기준위원회" items={depth03List} setItemIndex={setDepth03SelectedIndex} nameKey="orgNm" idKey="orgId" />
<ListCreateUpdateDelete title="실무위원회" items={depth04List} setItemIndex={setDepth04SelectedIndex} nameKey="orgNm" idKey="orgId" />
</Box>
{ true &&
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
'& > :not(style)': {
mt: 4,
width: 245,
},
}}
>
<ListLabelInputs title="위원회 코드정보" items={summaryArray} />
</Box>
}
{/* <!--// 본문 --> */}
</div>

View File

@ -1,11 +1,39 @@
import React from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
function StandardCodeMgt(props) {
return (
<div className="container">
StandardCodeMgt
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN} >사이트관리</Link></li>
<li>환경설정</li>
<li>건설기준코드 관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents NOTICE_LIST" id="contents">
<div className="top_tit">
<h1 className="tit_1">건설기준코드 관리</h1>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect, useCallback, useRef, PureComponent} from 'react';
import React, {useState, useEffect, useCallback} from 'react';
import { Link, useLocation } from 'react-router-dom';
import {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
@ -145,6 +145,7 @@ function FileConnections(props) {
}
);
// console.groupEnd("EgovAdminPrivacyList.retrieveList()");
// eslint-disable-next-line react-hooks/exhaustive-deps
},[listTag]);
const CustomTooltip = ({ active, payload, label }) => {

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect, useCallback, useRef, PureComponent} from 'react';
import React, {useState, useEffect, useCallback} from 'react';
import { Link, useLocation } from 'react-router-dom';
import {BarChart, Bar, Rectangle, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
@ -85,6 +85,7 @@ function MenuConnections(props) {
}
);
// console.groupEnd("EgovAdminPrivacyList.retrieveList()");
// eslint-disable-next-line react-hooks/exhaustive-deps
},[listTag]);
const CustomTooltip = ({ active, payload, label }) => {

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
@ -77,6 +77,7 @@ function PrivacyConnections(props) {
}
);
// console.groupEnd("EgovAdminPrivacyList.retrieveList()");
// eslint-disable-next-line react-hooks/exhaustive-deps
},[listTag]);
useEffect(() => {

View File

@ -1,4 +1,4 @@
import React, {useState, useEffect, useCallback, useRef, PureComponent} from 'react';
import React, {useState, useEffect, useCallback} from 'react';
import { Link, useLocation } from 'react-router-dom';
import {BarChart, Bar, Rectangle, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
@ -84,6 +84,7 @@ function UserConnections(props) {
}
);
// console.groupEnd("EgovAdminPrivacyList.retrieveList()");
// eslint-disable-next-line react-hooks/exhaustive-deps
},[listTag]);
const CustomTooltip = ({ active, payload, label }) => {

View File

@ -0,0 +1,166 @@
import PropTypes from 'prop-types';
import { useState } from 'react';
import { Link as RouterLink } from 'react-router-dom';
// material-ui
import { Box, Link, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
function createData(trackingNo, title, dt, name) {
return { trackingNo, title, dt, name };
}
const rows = [
createData(1, '흙막이 가시설 띠장 전단 설계시 플래지 ...', '2024-02-04 13:22', '홍길동'),
createData(2, '콘크리트 벽체 설계기준 적용 유무 확인 ...', '2024-02-04 13:22', '홍길동'),
createData(3, '한중콘크리트 초기양생 관련', '2024-02-04 13:22', '홍길동'),
createData(4, 'KDS 21 30 00 : 2022가설흙...', '2024-02-04 13:22', '홍길동'),
createData(5, '인테리어필름 시방서 관련', '2024-02-04 13:22', '홍길동'),
createData(6, '고온고압증기양생기포콘크리트(ALC) 구조', '2024-02-04 13:22', '홍길동'),
createData(7, '지반을 최저등급으로 가정한 경우란', '2024-02-04 13:22', '홍길동')
];
function descendingComparator(a, b, orderBy) {
if (b[orderBy] < a[orderBy]) {
return -1;
}
if (b[orderBy] > a[orderBy]) {
return 1;
}
return 0;
}
function getComparator(order, orderBy) {
return order === 'desc' ? (a, b) => descendingComparator(a, b, orderBy) : (a, b) => -descendingComparator(a, b, orderBy);
}
function stableSort(array, comparator) {
const stabilizedThis = array.map((el, index) => [el, index]);
stabilizedThis.sort((a, b) => {
const order = comparator(a[0], b[0]);
if (order !== 0) {
return order;
}
return a[1] - b[1];
});
return stabilizedThis.map((el) => el[0]);
}
// ==============================|| ORDER TABLE - HEADER CELL ||============================== //
const headCells = [
{
id: 'trackingNo',
align: 'left',
disablePadding: false,
label: 'No.'
},
{
id: 'title',
align: 'center',
disablePadding: true,
label: '제목'
},
{
id: 'dt',
align: 'center',
disablePadding: false,
label: '작성일자'
},
{
id: 'name',
align: 'center',
disablePadding: false,
label: '작성자'
},
];
// ==============================|| ORDER TABLE - HEADER ||============================== //
function OrderTableHead({ order, orderBy }) {
return (
<TableHead>
<TableRow>
{headCells.map((headCell) => (
<TableCell
key={headCell.id}
align={headCell.align}
padding={headCell.disablePadding ? 'none' : 'normal'}
sortDirection={orderBy === headCell.id ? order : false}
sx={{ width:headCell.id === 'trackingNo' ? '10%' :
headCell.id === 'title' ? '50%' :
headCell.id === 'dt' ? '25%' : '15%', }}
>
{headCell.label}
</TableCell>
))}
</TableRow>
</TableHead>
);
}
OrderTableHead.propTypes = {
order: PropTypes.string,
orderBy: PropTypes.string
};
// ==============================|| ORDER TABLE ||============================== //
export default function OrderTable() {
const [order] = useState('asc');
const [orderBy] = useState('trackingNo');
const [selected] = useState([]);
const isSelected = (trackingNo) => selected.indexOf(trackingNo) !== -1;
return (
<Box>
<TableContainer
sx={{
width: '100%',
overflowX: 'auto',
position: 'relative',
display: 'block',
maxWidth: '100%',
'& td, & th': { whiteSpace: 'nowrap' }
}}
>
<Table
aria-labelledby="tableTitle"
sx={{
'& .MuiTableCell-root:first-of-type': {
pl: 2
},
'& .MuiTableCell-root:last-of-type': {
pr: 0
}
}}
>
<OrderTableHead order={order} orderBy={orderBy} />
<TableBody>
{stableSort(rows, getComparator(order, orderBy)).map((row, index) => {
const isItemSelected = isSelected(row.trackingNo);
const labelId = `enhanced-table-checkbox-${index}`;
return (
<TableRow
hover
role="checkbox"
sx={{ '&:last-child td, &:last-child th': { border: 0 } }}
aria-checked={isItemSelected}
tabIndex={-1}
key={row.trackingNo}
selected={isItemSelected}
>
<TableCell width="10%" align="left" component="th" id={labelId} scope="row">{row.trackingNo}</TableCell>
<TableCell width="50%" align="left"><Link color="secondary" component={RouterLink} to="" sx={{ overflow: 'hidden', textOverflow: 'ellipsis', display: 'block'}}>{row.title}</Link></TableCell>
<TableCell width="25%" align="center">{row.dt}</TableCell>
<TableCell width="15%" align="center">{row.name}</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</TableContainer>
</Box>
);
}

View File

@ -1,202 +0,0 @@
import React, { useState, useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import EgovAttachFile from 'components/EgovAttachFile';
function EgovAdminScheduleDetail(props) {
console.group("EgovAdminScheduleDetail");
console.log("[Start] EgovAdminScheduleDetail ------------------------------");
console.log("EgovAdminScheduleDetail [props] : ", props);
const navigate = useNavigate();
const location = useLocation();
console.log("EgovAdminScheduleDetail [location] : ", location);
const [scheduleDetail, setScheduleDetail] = useState({});
const [boardAttachFiles, setBoardAttachFiles] = useState();
const [user, setUser] = useState({});
const retrieveDetail = () => {
const retrieveDetailURL = `/schedule/${location.state?.schdulId}`;
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(retrieveDetailURL,
requestOptions,
function (resp) {
let rawScheduleDetail = resp.result.scheduleDetail;
rawScheduleDetail.startDateTime = convertDate(rawScheduleDetail.schdulBgnde);
rawScheduleDetail.endDateTime = convertDate(rawScheduleDetail.schdulEndde);
rawScheduleDetail.reptitSeCodeNm = getCodeName(resp.result.reptitSeCode, resp.result.scheduleDetail.reptitSeCode);
rawScheduleDetail.schdulIpcrCodeNm = getCodeName(resp.result.schdulIpcrCode, resp.result.scheduleDetail.schdulIpcrCode);
rawScheduleDetail.schdulSeNm = getCodeName(resp.result.schdulSe, resp.result.scheduleDetail.schdulSe);
setScheduleDetail(rawScheduleDetail);
setUser(resp.result.user);
setBoardAttachFiles(resp.result.resultFiles);
}
);
}
const convertDate = (str) => {
let year = str.substring(0, 4);
let month = str.substring(4, 6);
let date = str.substring(6, 8);
let hour = str.substring(8, 10);
let minute = str.substring(10, 12);
return {
year: year,
month: month,
date: date,
hour: hour,
minute: minute,
dateForm: year + "년 " + month + "월 " + date + "일 " + hour + "시 " + minute + "분 "
}
}
const getCodeName = (codeArr, code) => {
return (
codeArr.map((codeObj) => {
if (codeObj.code === code.trim()) return codeObj.codeNm
else return "";
})
);
};
const onClickDeleteSchedule = (schdulId) => {
const deleteBoardURL = `/schedule/${schdulId}`;
const requestOptions = {
method: "DELETE",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(deleteBoardURL,
requestOptions,
(resp) => {
console.log("====>>> Schdule delete= ", resp);
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
alert("게시글이 삭제되었습니다.")
navigate(URL.ADMIN_SCHEDULE ,{ replace: true });
} else {
// alert("ERR : " + resp.message);
navigate({pathname: URL.ERROR}, {state: {msg : resp.resultMessage}});
}
}
);
}
useEffect(function () {
retrieveDetail();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
console.log("------------------------------EgovAdminScheduleDetail [End]");
console.groupEnd("EgovAdminScheduleDetail");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>일정관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents SITE_GALLARY_VIEW" id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">사이트관리</h1>
</div>
<h2 className="tit_2">일정관리 상세보기</h2>
{/* <!-- 게시판 상세보기 --> */}
<div className="board_view2">
<dl>
<dt>일정구분</dt>
<dd>{scheduleDetail.schdulSeNm}</dd>
</dl>
<dl>
<dt>중요도</dt>
<dd>{scheduleDetail.schdulIpcrCodeNm}</dd>
</dl>
<dl>
<dt>부서</dt>
<dd>{scheduleDetail.schdulDeptName}</dd>
</dl>
<dl>
<dt>일정명</dt>
<dd>{scheduleDetail.schdulNm}</dd>
</dl>
<dl>
<dt>일정내용</dt>
<dd>{scheduleDetail.schdulCn}</dd>
</dl>
<dl>
<dt>반복구분</dt>
<dd>{scheduleDetail.reptitSeCodeNm}</dd>
</dl>
<dl>
<dt>날짜/시간</dt>
<dd> {scheduleDetail.startDateTime?.dateForm} ~ {scheduleDetail.endDateTime?.dateForm}</dd>
</dl>
<dl>
<dt>담당자</dt>
<dd>{scheduleDetail.schdulChargerName}</dd>
</dl>
<EgovAttachFile boardFiles={boardAttachFiles} />
{/* <!-- 버튼영역 --> */}
<div className="board_btn_area">
{user.id &&
<div className="left_col btn1">
<Link to={{pathname: URL.ADMIN_SCHEDULE_MODIFY}}
state={{
schdulId: location.state?.schdulId
}}
className="btn btn_skyblue_h46 w_100">수정</Link>
<button className="btn btn_skyblue_h46 w_100"
onClick={(e) => {
onClickDeleteSchedule(location.state?.schdulId);
}}>삭제</button>
</div>
}
<div className="right_col btn1">
<Link to={URL.ADMIN_SCHEDULE} className="btn btn_blue_h46 w_100">목록</Link>
</div>
</div>
{/* <!--// 버튼영역 --> */}
</div>
{/* <!-- 게시판 상세보기 --> */}
{/* <!--// 본문 --> */}
</div>
</div>
</div>
</div>
);
}
export default EgovAdminScheduleDetail;

View File

@ -1,364 +0,0 @@
import React, { useState, useEffect } from 'react';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import DatePicker from "react-datepicker";
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import EgovAttachFile from 'components/EgovAttachFile';
import EgovRadioButtonGroup from 'components/EgovRadioButtonGroup';
import 'react-datepicker/dist/react-datepicker.css';
function EgovAdminScheduleEdit(props) {
console.group("EgovAdminScheduleEdit");
console.log("[Start] EgovAdminScheduleEdit ------------------------------");
console.log("EgovAdminScheduleEdit [props] : ", props);
const navigate = useNavigate();
const location = useLocation();
console.log("EgovAdminScheduleEdit [location] : ", location);
const reptitSeCodeRadioGroup = [{ value: "1", label: "당일" }, { value: "2", label: "반복" }, { value: "3", label: "연속" }];
const [modeInfo, setModeInfo] = useState({ mode: props.mode });
const [scheduleDetail, setScheduleDetail] = useState({ schdulDeptName: "관리자부서", schdulChargerName: "관리자", schdulKindCode: 2, reptitSeCode: "1", startDate: new Date(), endDate: new Date() });
const [boardAttachFiles, setBoardAttachFiles] = useState();
const [schdulBgndeHH, setSchdulBgndeHH] = useState();
const [schdulBgndeMM, setSchdulBgndeMM] = useState();
const [schdulEnddeHH, setSchdulEnddeHH] = useState();
const [schdulEnddeMM, setSchdulEnddeMM] = useState();
const initMode = () => {
switch (props.mode) {
case CODE.MODE_CREATE:
setModeInfo({
...modeInfo,
modeTitle: "등록",
method : "POST",
editURL: '/schedule'
});
break;
case CODE.MODE_MODIFY:
setModeInfo({
...modeInfo,
modeTitle: "수정",
method : "PUT",
editURL: '/schedule'
});
break;
default:
navigate({pathname: URL.ERROR}, {state: {msg : ""}});
}
retrieveDetail();
}
const convertDate = (str) => {
let year = str.substring(0, 4);
let month = str.substring(4, 6);
let date = str.substring(6, 8);
let hour = str.substring(8, 10);
let minute = str.substring(10, 12);
return new Date(year, month - 1, date, hour, minute)
}
const retrieveDetail = () => {
if (modeInfo.mode === CODE.MODE_CREATE) {// /
setScheduleDetail({
...scheduleDetail,
schdulBgnde: location.state.iUseDate,
schdulEndde: location.state.iUseDate,
startDate: convertDate(location.state.iUseDate),
endDate: convertDate(location.state.iUseDate),
});
return;
}
const retrieveDetailURL = `/schedule/${location.state?.schdulId}`;
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json'
}
}
EgovNet.requestFetch(retrieveDetailURL,
requestOptions,
function (resp) {
let rawScheduleDetail = resp.result.scheduleDetail;
//
setScheduleDetail({
...scheduleDetail,
...rawScheduleDetail,
startDate: convertDate(rawScheduleDetail.schdulBgnde),
endDate: convertDate(rawScheduleDetail.schdulEndde),
atchFileId : rawScheduleDetail.atchFileId.trim(),
});
setBoardAttachFiles(resp.result.resultFiles);
}
);
}
const updateSchedule = () => {
const formData = new FormData();
for (let key in scheduleDetail) {
formData.append(key, scheduleDetail[key]);
console.log("scheduleDetail [%s] ", key, scheduleDetail[key]);
}
if (formValidator(formData)) {
const requestOptions = {
method: modeInfo.method,
body: formData
}
if (modeInfo.mode === CODE.MODE_MODIFY) {
modeInfo.editURL = `${modeInfo.editURL}/${location.state?.schdulId}`;
}
EgovNet.requestFetch(modeInfo.editURL,
requestOptions,
(resp) => {
if (Number(resp.resultCode) === Number(CODE.RCV_SUCCESS)) {
navigate({ pathname: URL.ADMIN_SCHEDULE });
} else {
navigate({pathname: URL.ERROR}, {state: {msg : resp.resultMessage}});
}
}
);
}
}
const formValidator = (formData) => {
if (formData.get('schdulNm') === null || formData.get('schdulNm') === "") {
alert("일정명은 필수 값입니다.");
return false;
}
if (formData.get('schdulCn') === null || formData.get('schdulCn') === "") {
alert("일정내용은 필수 값입니다.");
return false;
}
if (formData.get('schdulSe') === null || formData.get('schdulSe') === "") {
alert("일정구분은 필수 값입니다.");
return false;
}
if (formData.get('schdulIpcrCode') === null || formData.get('schdulIpcrCode') === "") {
alert("중요도는 필수 값입니다.");
return false;
}
if (formData.get('reptitSeCode') === null ||formData.get('reptitSeCode') === "") {
alert("반복구분은 필수 값입니다.");
return false;
}
if (formData.get('schdulBgnde') > formData.get('schdulEndde')) {
alert("종료일시는 시작일시보다 앞 설 수 없습니다.");
return false;
}
return true;
}
const getDateFourteenDigit = (date) => {
return getYYYYMMDD(date).toString() + makeTwoDigit(date.getHours()) + makeTwoDigit(date.getMinutes()) + makeTwoDigit(date.getSeconds());
}
const getYYYYMMDD = (date) => {
return date.getFullYear().toString() + makeTwoDigit(Number(date.getMonth() + 1)) + makeTwoDigit(date.getDate());
}
const makeTwoDigit = (number) => {
return number < 10 ? "0" + number : number.toString();
}
useEffect(function () {
initMode();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
console.log("------------------------------EgovAdminScheduleEdit [End]");
console.groupEnd("EgovAdminScheduleEdit");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>일정관리</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
{/* <!-- Navigation --> */}
<EgovLeftNav></EgovLeftNav>
{/* <!--// Navigation --> */}
<div className="contents SITE_SCHDULE_REG" id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">사이트관리</h1>
</div>
<h2 className="tit_2">일정관리 상세보기</h2>
{/* <!-- 게시판 상세보기 --> */}
<div className="board_view2">
<dl>
<dt>일정구분<span className="req">필수</span></dt>
<dd>
<label className="f_select w_130" htmlFor="schdulSe">
<select id="schdulSe" name="schdulSe" title="일정구분"
value={scheduleDetail.schdulSe}
onChange={(e) => setScheduleDetail({ ...scheduleDetail, schdulSe: e.target.value })}>
<option value="">선택</option>
<option value="1">회의</option>
<option value="2">세미나</option>
<option value="3">강의</option>
<option value="4">교육</option>
<option value="5">기타</option>
</select>
</label>
</dd>
</dl>
<dl>
<dt>중요도<span className="req">필수</span></dt>
<dd>
<label className="f_select w_130" htmlFor="schdulIpcrCode">
<select id="schdulIpcrCode" name="schdulIpcrCode" title="중요도"
value={scheduleDetail.schdulIpcrCode}
onChange={(e) => setScheduleDetail({ ...scheduleDetail, schdulIpcrCode: e.target.value })}>
<option value="">선택</option>
<option value="A">높음</option>
<option value="B">보통</option>
<option value="C">낮음</option>
</select>
</label>
</dd>
</dl>
<dl>
<dt><label htmlFor="schdulDeptName">부서</label><span className="req">필수</span></dt>
<dd>
<input className="f_input2 w_full" type="text" name="schdulDeptName" title="부서" id="schdulDeptName"
value={scheduleDetail.schdulDeptName} readOnly
/>
</dd>
</dl>
<dl>
<dt><label htmlFor="schdulNm">일정명</label><span className="req">필수</span></dt>
<dd>
<input className="f_input2 w_full" type="text" name="schdulNm" title="부서" id="schdulNm" placeholder="일정 테스트"
defaultValue={scheduleDetail.schdulNm}
onChange={(e) => setScheduleDetail({ ...scheduleDetail, schdulNm: e.target.value })} />
</dd>
</dl>
<dl>
<dt><label htmlFor="schdulCn">일정내용</label><span className="req">필수</span></dt>
<dd>
<textarea className="f_txtar w_full h_100" name="schdulCn" id="schdulCn" cols="30" rows="10" placeholder="일정내용"
defaultValue={scheduleDetail.schdulCn}
onChange={(e) => setScheduleDetail({ ...scheduleDetail, schdulCn: e.target.value })}
></textarea>
</dd>
</dl>
<dl>
<dt>반복구분<span className="req">필수</span></dt>
<dd>
<EgovRadioButtonGroup
name="reptitSeCode"
radioGroup={reptitSeCodeRadioGroup}
setValue={scheduleDetail.reptitSeCode.trim()}
setter={(v) => setScheduleDetail({ ...scheduleDetail, reptitSeCode: v })} />
</dd>
</dl>
<dl>
<dt>날짜/시간<span className="req">필수</span></dt>
<dd className="datetime">
<span className="line_break">
<DatePicker
selected={scheduleDetail.startDate}
name="schdulBgnde"
className="f_input"
dateFormat="yyyy-MM-dd HH:mm"
showTimeInput
onChange={(date) => {
console.log("setStartDate : ", date);
setScheduleDetail({ ...scheduleDetail, schdulBgnde: getDateFourteenDigit(date), schdulBgndeYYYMMDD: getYYYYMMDD(date), schdulBgndeHH: date.getHours(), schdulBgndeMM: date.getMinutes(), startDate: date });
setSchdulBgndeHH(date.getHours());
setSchdulBgndeMM(date.getMinutes());
}} />
<input type="hidden" name="schdulBgndeHH" defaultValue={schdulBgndeHH} readOnly />
<input type="hidden" name="schdulBgndeMM" defaultValue={schdulBgndeMM} readOnly />
<span className="f_inn_txt">~</span>
</span>
<span className="line_break">
<DatePicker
selected={scheduleDetail.endDate}
name="schdulEndde"
className="f_input"
dateFormat="yyyy-MM-dd HH:mm"
showTimeInput
minDate={scheduleDetail.startDate}
onChange={(date) => {
console.log("setEndDate: ", date);
setScheduleDetail({ ...scheduleDetail, schdulEndde: getDateFourteenDigit(date), schdulEnddeYYYMMDD: getYYYYMMDD(date), schdulEnddeHH: date.getHours(), schdulEnddeMM: date.getMinutes(), endDate: date });
setSchdulEnddeHH(date.getHours());
setSchdulEnddeMM(date.getMinutes());
}
} />
<input type="hidden" name="schdulEnddeHH" defaultValue={schdulEnddeHH} readOnly />
<input type="hidden" name="schdulEnddeMM" defaultValue={schdulEnddeMM} readOnly />
</span>
</dd>
</dl>
<dl>
<dt><label htmlFor="schdulChargerName">담당자</label><span className="req">필수</span></dt>
<dd>
<input className="f_input2 w_full" type="text" name="schdulChargerName" id="schdulChargerName" defaultValue="관리자" readOnly
/>
</dd>
</dl>
<EgovAttachFile
fnChangeFile={(attachfile) => {
console.log("====>>> Changed attachfile file = ", attachfile);
const arrayConcat = { ...scheduleDetail}; // ( for)
for ( let i = 0; i < attachfile.length; i++) {
arrayConcat[`file_${i}`] = attachfile[i];
}
setScheduleDetail(arrayConcat);
}}
fnDeleteFile={(deletedFile) => {
console.log("====>>> Delete deletedFile = ", deletedFile);
setBoardAttachFiles(deletedFile);
}}
boardFiles={boardAttachFiles}
mode={props.mode} />
{/* <!-- 버튼영역 --> */}
<div className="board_btn_area">
<div className="left_col btn1">
<button className="btn btn_skyblue_h46 w_100"
onClick={() => updateSchedule()}
> 저장</button>
<a href="#!" className="btn btn_skyblue_h46 w_100">삭제</a>
</div>
<div className="right_col btn1">
<Link to={URL.ADMIN_SCHEDULE} className="btn btn_blue_h46 w_100">목록</Link>
</div>
</div>
{/* <!--// 버튼영역 --> */}
</div>
{/* <!-- 게시판 상세보기 --> */}
{/* <!--// 본문 --> */}
</div>
</div>
</div>
</div >
);
}
export default EgovAdminScheduleEdit;

View File

@ -1,328 +1,221 @@
import React, {useState, useEffect, useCallback, PureComponent} from 'react';
import {Link, useLocation} from 'react-router-dom';
import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
import React, {useState, useEffect, useCallback} from 'react'; // PureComponent
import {Link} from 'react-router-dom'; //useLocation
// import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
// import CODE from 'constants/code';
// material-ui
import { Box, Button, Grid, Stack, Typography } from '@mui/material';
import MainCard from 'components/cards/MainCard';
import BbsTable from './BbsTable';
import IncomeAreaChart from './IncomeAreaChart';
import MonthlyBarChart from './MonthlyBarChart';
import ReportAreaChart from './ReportAreaChart';
import AnalyticEcommerce from 'components/cards/AnalyticEcommerce';
import {default as EgovLeftNav} from 'components/leftmenu/EgovLeftNavAdmin';
function EgovAdminScheduleList(props) {
console.group("EgovAdminScheduleList");
console.log("[Start] EgovAdminScheduleList ------------------------------");
console.log("EgovAdminScheduleList [props] : ", props);
function EgovAdminDashboard(props) {
// console.group("EgovAdminScheduleList");
// console.log("[Start] EgovAdminScheduleList ------------------------------");
// console.log("EgovAdminScheduleList [props] : ", props);
const location = useLocation();
console.log("EgovAdminScheduleList [location] : ", location);
// const location = useLocation();
// console.log("EgovAdminScheduleList [location] : ", location);
const DATE = new Date();
const TODAY = new Date(DATE.getFullYear(), DATE.getMonth(), DATE.getDate());
// const TODAY = new Date(DATE.getFullYear(), DATE.getMonth(), DATE.getDate());
//
// const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || {schdulSe: '', year: TODAY.getFullYear(), month: TODAY.getMonth(), date: TODAY.getDate()});
// const [calendarTag, setCalendarTag] = useState([]);
//
// const [scheduleList, setScheduleList] = useState([]);
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || {schdulSe: '', year: TODAY.getFullYear(), month: TODAY.getMonth(), date: TODAY.getDate()});
const [calendarTag, setCalendarTag] = useState([]);
// const innerConsole = (...args) => {
// console.log(...args);
// }
const [scheduleList, setScheduleList] = useState([]);
const [dailyUserLogList, setDailyUserLogList] = useState([]);
// const changeDate = (target, amount) => {
// let changedDate;
//
// if (target === CODE.DATE_YEAR) {
// changedDate = new Date(searchCondition.year + amount, searchCondition.month, searchCondition.date);
// }
//
// if (target === CODE.DATE_MONTH) {
// changedDate = new Date(searchCondition.year, searchCondition.month + amount, searchCondition.date);
// }
// setSearchCondition({...searchCondition, year: changedDate.getFullYear(), month: changedDate.getMonth(), date: changedDate.getDate()});
// }
const innerConsole = (...args) => {
console.log(...args);
}
// const retrieveList = useCallback((srchcnd) => {
// console.groupCollapsed("EgovAdminScheduleList.retrieveList()");
//
// const retrieveListURL = '/schedule/month' + EgovNet.getQueryString(srchcnd);
//
// const requestOptions = {
// method: "GET",
// headers: {
// 'Content-type': 'application/json',
// }
// }
//
// EgovNet.requestFetch(retrieveListURL,
// requestOptions,
// (resp) => {
// setScheduleList(resp.result.resultList);
// },
// function (resp) {
// console.log("err response : ", resp);
// }
// );
// console.groupEnd("EgovAdminScheduleList.retrieveList()");
// }, []);
const getLastDateOfMonth = (year, month) => {
const LAST_DATE_SUPPLMENT = 1;
return new Date(year, month + LAST_DATE_SUPPLMENT, 0);
}
const getFirstDateOfMonth = (year, month) => {
return new Date(year, month, 1);
}
const changeDate = (target, amount) => {
let changedDate;
// const Location = React.memo(function Location() {
// return (
// <div className="location">
// <ul>
// <li><Link to={URL.MAIN} className="home">Home</Link></li>
// <li><Link to={URL.ADMIN}></Link></li>
// <li>Dashboard</li>
// </ul>
// </div>
// )
// });
if (target === CODE.DATE_YEAR) {
changedDate = new Date(searchCondition.year + amount, searchCondition.month, searchCondition.date);
}
// useEffect(() => {
// //retrieveList(searchCondition); disabled by thkim
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [searchCondition]);
if (target === CODE.DATE_MONTH) {
changedDate = new Date(searchCondition.year, searchCondition.month + amount, searchCondition.date);
}
setSearchCondition({...searchCondition, year: changedDate.getFullYear(), month: changedDate.getMonth(), date: changedDate.getDate()});
}
// const [dailyUserLogList, setDailyUserLogList] = useState([]);
// const [isDailyChart, setIsDailyChart] = useState(true);
//
// const getDailyUserLogList = useCallback(() => {
// // console.groupCollapsed("EgovAdminScheduleList.getDailyUserLogList()");
// //
// // console.log("@@@ isDailyChart : " + isDailyChart);
//
// const dailyUserLogListURL = isDailyChart ? '/admin/dashboard/daily-user-log-list' : '/admin/dashboard/monthly-user-log-list';
//
// const requestOptions = {
// method: "GET",
// headers: {
// 'Content-type': 'application/json',
// }
// }
//
// EgovNet.requestFetch(dailyUserLogListURL,
// requestOptions,
// (resp) => {
// setDailyUserLogList(resp.result.dailyUserLogList);
// // console.log("@@@ : " + dailyUserLogList);
// },
// function (resp) {
// // console.log("err response : ", resp);
// }
// );
// // console.groupEnd("EgovAdminScheduleList.getDailyUserLogList()");
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [isDailyChart]);
//
// useEffect(() => {
// getDailyUserLogList();
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [isDailyChart]);
//
// const handleChartToggle = () => {
// setIsDailyChart(!isDailyChart);
// };
//
// const ChartToggle = ({onToggle}) => {
//
// const handleToggle = () => {
// onToggle(!isDailyChart);
// };
//
// return (
// <button onClick={handleToggle}>
// {isDailyChart ? '' : ''}
// </button>
// )
// }
//
// const data = dailyUserLogList.map(item => ({
// logDt: item.logDt,
// uv: item.mobileCnt,
// " ": item.logCnt,
// amt: item.pcCnt,
// }));
//
// const CustomTooltip = ({active, payload, label}) => {
// if (active && payload && payload.length) {
// return (
// <div className="custom-tooltip">
// <p className="desc"> </p>
// <p className="label">{`${label} : ${payload[0].value}`}</p>
// </div>
// );
// }
//
// return null;
// };
const retrieveList = useCallback((srchcnd) => {
console.groupCollapsed("EgovAdminScheduleList.retrieveList()");
// class UserLogChart extends PureComponent {
// render() {
// return (
// <ResponsiveContainer width="100%" height="100%">
// <BarChart
// width={500}
// height={300}
// data={data}
// margin={{
// top: 5,
// right: 30,
// left: 20,
// bottom: 5,
// }}
// >
// <CartesianGrid strokeDasharray="3 3"/>
// <XAxis dataKey="logDt"/>
// <YAxis/>
// <Tooltip content={<CustomTooltip/>}/>
// <Legend/>
// <Bar dataKey=" " barSize={20} fill="#87CEFA"/>
// </BarChart>
// </ResponsiveContainer>
// );
// }
// }
const retrieveListURL = '/schedule/month' + EgovNet.getQueryString(srchcnd);
// console.log("------------------------------EgovAdminScheduleList [End]");
// console.groupEnd("EgovAdminScheduleList");
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json',
}
}
// const [value, setValue] = useState('today');
const [slot, setSlot] = useState('week');
const [totalDownloads, setTotalDownloads] = useState(0);
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
setScheduleList(resp.result.resultList);
},
function (resp) {
console.log("err response : ", resp);
}
);
console.groupEnd("EgovAdminScheduleList.retrieveList()");
}, []);
const drawCalendar = () => {
console.groupCollapsed("EgovAdminScheduleList.drawCalendar()");
const PREV_MONTH_ADDITION = -1;
let lastOfLastMonth = getLastDateOfMonth(searchCondition.year, searchCondition.month + PREV_MONTH_ADDITION);
let firstOfThisMonth = getFirstDateOfMonth(searchCondition.year, searchCondition.month);
let lastOfThisMonth = getLastDateOfMonth(searchCondition.year, searchCondition.month);
console.log("lastOfLastMonth : ", lastOfLastMonth, lastOfLastMonth.getDay());
console.log("firstOfThisMonth :", firstOfThisMonth, firstOfThisMonth.getDay());
console.log("lastOfThisMonth :", lastOfThisMonth, lastOfThisMonth.getDay());
console.log("scheduleList : ", scheduleList);
let firstDayOfThisMonth = firstOfThisMonth.getDay();
let lastDateOfThisMonth = lastOfThisMonth.getDate();
console.log("firstDayOfThisMonth", firstDayOfThisMonth, "lastDateOfThisMonth", lastDateOfThisMonth)
let monthArr = [];
let weekArr = [];
// firstWeek Date Set START
let firstWeekDateCount = 0;
for (let day = 0; day < 7; day++) {
if (day < firstDayOfThisMonth) { //
weekArr.push(0);
firstWeekDateCount = 0;
} else {
weekArr.push(++firstWeekDateCount);
}
}
monthArr.push(weekArr);
console.log("FirstWeek monthArr : ", monthArr);
// firstWeek Date Set END
// otherWeek Date Set START
let dayCount = 0;
weekArr = [];//
for (let day = firstWeekDateCount + 1; day <= lastDateOfThisMonth; day++) {
if (dayCount % 7 !== 6) {
weekArr.push(day);
} else {
weekArr.push(day);
monthArr.push(weekArr);
weekArr = [];
dayCount = -1;
}
dayCount++;
}
// otherWeek Date Set END
// lastWeek Date Set START
if (weekArr.length > 0) {//
for (let day = weekArr.length; day < 7; day++) {
weekArr.push(0);
}
monthArr.push(weekArr);
}
// lastWeek Date Set END
console.log("OtherWeek monthArr : ", monthArr);
let mutsUseYearMonth = searchCondition.year.toString() + ((searchCondition.month + 1).toString().length === 1 ? "0" + (searchCondition.month + 1).toString() : (searchCondition.month + 1).toString());
console.log("mutsUseYearMonth : ", mutsUseYearMonth);
let mutCalendarTagList = [];
let keyIdx = 0;
//draw Calendar
monthArr.forEach((week, weekIdx) => {
console.log();
mutCalendarTagList.push(
<tr key={keyIdx++}>{
week.map((day, dayIdx) => {
if (day !== 0) {//
let sDate = day.toString().length === 1 ? "0" + day.toString() : day.toString();
let iUseDate = Number(mutsUseYearMonth + sDate);
if (scheduleList.length > 0) {//
return (
<td key={keyIdx++}>
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate: mutsUseYearMonth + sDate + "000000"}} className="day"
key={keyIdx++}>{day}</Link><br/>
{
scheduleList.map((schedule, scheduleIdx) => {
let iBeginDate = Number(schedule.schdulBgnde.substring(0, 8));
let iEndDate = Number(schedule.schdulEndde.substring(0, 8));
innerConsole("scheduleList ", day, scheduleIdx, iBeginDate, iUseDate, iEndDate, iUseDate >= iBeginDate && iUseDate <= iEndDate);
innerConsole("schedule.schdulId ", schedule.schdulId);
if (iUseDate >= iBeginDate && iUseDate <= iEndDate) {
return (
<>
<Link to={{pathname: URL.ADMIN_SCHEDULE_DETAIL}}
state={{schdulId: schedule.schdulId}}
key={keyIdx++}>{schedule.schdulNm}
</Link>
<br/>
</>
);
} else return <></>
})
}
</td>
);
} else {//
return (
<td key={keyIdx++}>
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate: mutsUseYearMonth + sDate + "000000"}} className="day"
key={keyIdx++}>{day}</Link><br/>
</td>);
}
} else if (day === 0) {// /
return (<td key={keyIdx++}></td>);
} else return <></>
})
}</tr>);
})
console.log("mutCalendarTagList : ", mutCalendarTagList);
setCalendarTag(mutCalendarTagList);
console.groupEnd("EgovAdminScheduleList.drawCalendar()");
}
const Location = React.memo(function Location() {
return (
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN}>사이트관리</Link></li>
<li>일정관리</li>
</ul>
</div>
)
});
useEffect(() => {
//retrieveList(searchCondition); disabled by thkim
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchCondition]);
useEffect(() => {
drawCalendar();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [scheduleList]);
const [isDailyChart, setChart] = useState(true);
const getDailyUserLogList = useCallback(() => {
console.groupCollapsed("EgovAdminScheduleList.getDailyUserLogList()");
console.log("@@@ isDailyChart : " + isDailyChart);
const dailyUserLogListURL = isDailyChart ? '/admin/dashboard/daily-user-log-list' : '/admin/dashboard/monthly-user-log-list';
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(dailyUserLogListURL,
requestOptions,
(resp) => {
setDailyUserLogList(resp.result.dailyUserLogList);
console.log("@@@ : " + dailyUserLogList);
},
function (resp) {
console.log("err response : ", resp);
}
);
console.groupEnd("EgovAdminScheduleList.getDailyUserLogList()");
}, [isDailyChart]);
useEffect(() => {
getDailyUserLogList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isDailyChart]);
const handleChartToggle = () => {
setChart(!isDailyChart);
// state
const handleTotalDownloads = (sum) => {
setTotalDownloads(sum);
};
const ChartToggle = ({onToggle}) => {
const handleToggle = () => {
onToggle(!isDailyChart);
};
return (
<button onClick={handleToggle}>
{isDailyChart ? '월별차트보기' : '일별차트보기'}
</button>
)
}
const data = dailyUserLogList.map(item => ({
logDt: item.logDt,
uv: item.mobileCnt,
"사용자 접속현황": item.logCnt,
amt: item.pcCnt,
}));
const CustomTooltip = ({active, payload, label}) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="desc">사용자 접속 현황</p>
<p className="label">{`${label} : ${payload[0].value}`}</p>
</div>
);
}
return null;
};
class UserLogChart extends PureComponent {
static demoUrl = 'https://codesandbox.io/s/tooltip-with-customized-content-lyxvs';
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<BarChart
width={500}
height={300}
data={data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis dataKey="logDt"/>
<YAxis/>
<Tooltip content={<CustomTooltip/>}/>
<Legend/>
<Bar dataKey="사용자 접속현황" barSize={20} fill="#87CEFA"/>
</BarChart>
</ResponsiveContainer>
);
}
}
console.log("------------------------------EgovAdminScheduleList [End]");
console.groupEnd("EgovAdminScheduleList");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<Location/>
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to={URL.ADMIN} >사이트관리</Link></li>
<li>Dashboard</li>
</ul>
</div>
{/* <!--// Location --> */}
<div className="layout">
@ -334,16 +227,132 @@ function EgovAdminScheduleList(props) {
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">사이트관리</h1>
<h1 className="tit_1">Dashboard</h1>
</div>
<h2 className="tit_2"></h2>
<Grid container rowSpacing={4.5} columnSpacing={2.75}>
{/* row 1 */}
{/*<Grid item xs={12} sx={{ mb: -2.25 }}>*/}
{/* <Typography variant="h5">Dashboard</Typography>*/}
{/*</Grid>*/}
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title={`총접속자수 (${DATE.getMonth() + 1}월)`} count="442,236" percentage={59.3} extra="35,000" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title={`건설기준 오류건수 (${DATE.getMonth() + 1}월)`} count="78,250" percentage={70.5} extra="8,900" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title={`기준코드 등록건수 (${DATE.getMonth() + 1}월)`} count="18,800" percentage={27.4} isLoss color="warning" extra="1,943" />
</Grid>
<Grid item xs={12} sm={6} md={4} lg={3}>
<AnalyticEcommerce title={`민원건수 (${DATE.getMonth() + 1}월)`} count="5" percentage={80} isLoss color="warning" extra="1" />
</Grid>
<ChartToggle isDailyChart={isDailyChart} onToggle={handleChartToggle}/>
<Grid item md={8} sx={{ display: { sm: 'none', md: 'block', lg: 'none' } }} />
<div style={{width: 1000, height: 300}}>
<UserLogChart/>
</div>
{/* row 2 */}
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">{ DATE.getFullYear() } 메뉴접속 / 방문수 </Typography>
</Grid>
<Grid item>
<Stack direction="row" alignItems="center" spacing={0}>
<Button
size="small"
onClick={() => setSlot('month')}
color={slot === 'month' ? 'primary' : 'secondary'}
variant={slot === 'month' ? 'outlined' : 'text'}
>
Month
</Button>
<Button
size="small"
onClick={() => setSlot('week')}
color={slot === 'week' ? 'primary' : 'secondary'}
variant={slot === 'week' ? 'outlined' : 'text'}
>
Week
</Button>
</Stack>
</Grid>
</Grid>
<MainCard content={false} sx={{ mt: 1.5 }}>
<Box sx={{ pt: 1, pr: 2 }}>
<IncomeAreaChart slot={slot} />
</Box>
</MainCard>
</Grid>
<Grid item xs={12} md={5} lg={4}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">다운로드수</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<Box sx={{ p: 3, pb: 0 }}>
<Stack spacing={2}>
<Typography variant="h3" color="textSecondary">
주간 현황
</Typography>
<Typography variant="h6"> {totalDownloads}</Typography>
</Stack>
</Box>
<MonthlyBarChart onDataFetched={handleTotalDownloads} />
</MainCard>
</Grid>
{/* row 3 */}
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">최근 문의사항</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<BbsTable />
</MainCard>
</Grid>
<Grid item xs={12} md={5} lg={4}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="h5">접속 방법</Typography>
</Grid>
<Grid item />
</Grid>
<MainCard sx={{ mt: 2 }} content={false}>
<ReportAreaChart />
</MainCard>
</Grid>
</Grid>
{/*<ChartToggle isDailyChart={isDailyChart} onToggle={handleChartToggle}/>*/}
{/*<div style={{width: 1000, height: 300}}>*/}
{/* <ResponsiveContainer width="100%" height="100%">*/}
{/* <BarChart*/}
{/* width={500}*/}
{/* height={300}*/}
{/* data={data}*/}
{/* margin={{*/}
{/* top: 5,*/}
{/* right: 30,*/}
{/* left: 20,*/}
{/* bottom: 5,*/}
{/* }}*/}
{/* >*/}
{/* <CartesianGrid strokeDasharray="3 3"/>*/}
{/* <XAxis dataKey="logDt"/>*/}
{/* <YAxis/>*/}
{/* <Tooltip content={<CustomTooltip/>}/>*/}
{/* <Legend/>*/}
{/* <Bar dataKey="사용자 접속현황" barSize={20} fill="#87CEFA"/>*/}
{/* </BarChart>*/}
{/* </ResponsiveContainer>*/}
{/*</div>*/}
</div>
</div>
</div>
@ -351,4 +360,4 @@ function EgovAdminScheduleList(props) {
);
}
export default EgovAdminScheduleList;
export default EgovAdminDashboard;

View File

@ -0,0 +1,156 @@
import PropTypes from 'prop-types';
import { useState, useEffect, useCallback } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
import * as EgovNet from 'api/egovFetch';
// chart options
const areaChartOptions = {
chart: {
height: 450,
type: 'area',
toolbar: {
show: false
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 2
},
grid: {
strokeDashArray: 0
}
};
// ==============================|| INCOME AREA CHART ||============================== //
const IncomeAreaChart = ({ slot }) => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const [options, setOptions] = useState(areaChartOptions);
const [menuMonthlyList, setMenuMonthlyList] = useState([]);
const [menuDailyList, setMenuDailyList] = useState([]);
const [loginMonthlyList, setLoginMonthlyList] = useState([]);
const [loginDailyList, setLoginDailyList] = useState([]);
//
const retrieveList = useCallback(() => {
const retrieveListURL = '/admin/dashboard/menu-login'
const requestOptions = {
method: "POST",
headers: {
'Content-type': 'application/json',
},
// body: JSON.stringify()
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
setMenuMonthlyList(resp.result.menuMonthlyList);
setMenuDailyList(resp.result.menuDailyList);
setLoginMonthlyList(resp.result.loginMonthlyList);
setLoginDailyList(resp.result.loginDailyList);
},
function (resp) {
console.log("err response : ", resp);
}
);
// eslint-disable-next-lie react-hooks/exhaustive-deps
}, []);
useEffect(() => {
retrieveList();
setOptions((prevState) => ({
...prevState,
colors: [theme.palette.primary.main, theme.palette.primary[700]],
xaxis: {
categories:
slot === 'month'
? ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
labels: {
style: {
colors: [
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary,
secondary
]
}
},
axisBorder: {
show: true,
color: line
},
tickAmount: slot === 'month' ? 11 : 7
},
yaxis: {
labels: {
style: {
colors: [secondary]
}
}
},
grid: {
borderColor: line
},
tooltip: {
theme: 'light'
}
}));
}, [primary, secondary, line, theme, slot, retrieveList]);
const [series, setSeries] = useState([
{
name: 'Menu Views',
data: menuDailyList
},
{
name: 'Login Count',
data: loginDailyList
}
]);
useEffect(() => {
setSeries([
{
name: 'Menu Views',
data: slot === 'month' ? menuMonthlyList : menuDailyList
},
{
name: 'Login Count',
data: slot === 'month' ? loginMonthlyList : loginDailyList
}
]);
}, [slot, menuMonthlyList, menuDailyList, loginMonthlyList, loginDailyList]);
return <ReactApexChart options={options} series={series} type="area" height={450} />;
};
IncomeAreaChart.propTypes = {
slot: PropTypes.string
};
export default IncomeAreaChart;

View File

@ -0,0 +1,125 @@
import {useCallback, useEffect, useState} from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
import * as EgovNet from 'api/egovFetch';
// chart options
const barChartOptions = {
chart: {
type: 'bar',
height: 365,
toolbar: {
show: false
}
},
plotOptions: {
bar: {
columnWidth: '45%',
borderRadius: 4
}
},
dataLabels: {
enabled: true
},
xaxis: {
categories: ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'],
axisBorder: {
show: false
},
axisTicks: {
show: false
}
},
yaxis: {
show: false
},
grid: {
show: false
}
};
// ==============================|| MONTHLY BAR CHART ||============================== //
const MonthlyBarChart = ({ onDataFetched }) => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const info = theme.palette.info.light;
const [options, setOptions] = useState(barChartOptions);
const [fileDailyList, setFileDailyList] = useState([]);
//
const retrieveList = useCallback(() => {
const retrieveListURL = '/admin/dashboard/file'
const requestOptions = {
method: "POST",
headers: {
'Content-type': 'application/json',
},
// body: JSON.stringify()
}
EgovNet.requestFetch(retrieveListURL,
requestOptions,
(resp) => {
setFileDailyList(resp.result.fileDailyList);
const sum = resp.result.fileDailyList.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
onDataFetched(sum);
},
function (resp) {
console.log("err response : ", resp);
}
);
// eslint-disable-next-lie react-hooks/exhaustive-deps
}, []);
const [series, setSeries] = useState([
{
name: '다운로드수',
data: fileDailyList
}
]);
useEffect(() => {
retrieveList();
}, [onDataFetched]);
useEffect(() => {
setSeries([
{
data: fileDailyList
},
]);
setOptions((prevState) => ({
...prevState,
colors: [info],
xaxis: {
labels: {
style: {
colors: [secondary, secondary, secondary, secondary, secondary, secondary, secondary]
}
}
},
tooltip: {
theme: 'light'
},
}));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [primary, info, secondary, fileDailyList]);
return (
<div id="chart">
<ReactApexChart options={options} series={series} type="bar" height={365} />
</div>
);
};
export default MonthlyBarChart;

View File

@ -0,0 +1,83 @@
import { useEffect, useState } from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
// third-party
import ReactApexChart from 'react-apexcharts';
// chart options
const areaChartOptions = {
chart: {
type: 'donut',
},
plotOptions: {
pie: {
startAngle: -90,
endAngle: 90,
offsetY: 10,
expandOnClick:false,
}
},
dataLabels: {
enabled: true
},
title: {
text: '2024년 2월',
align: 'center'
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
},
legend: {
position: 'bottom'
}
}
}],
grid: {
padding: {
bottom: -150
}
},
};
// ==============================|| REPORT AREA CHART ||============================== //
const ReportAreaChart = () => {
const theme = useTheme();
const { primary, secondary } = theme.palette.text;
const line = theme.palette.divider;
const [options, setOptions] = useState(areaChartOptions);
useEffect(() => {
setOptions((prevState) => ({
...prevState,
labels: ['PC', 'Mobile'],
colors: ['#448EF7', '#FFC107'],
// colors: [theme.palette.warning.main],
grid: {
borderColor: line
},
tooltip: {
theme: 'light'
},
legend: {
position: 'bottom',
labels: {
colors: 'grey.500'
}
}
}));
}, [primary, secondary, line, theme]);
const [series] = useState([90, 10]);
return <ReactApexChart options={options} series={series} type="donut" />;
};
export default ReportAreaChart;

View File

@ -35,11 +35,9 @@ function InfoDisclosure(props) {
<div className="contents " id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">건설기준 관리</h1>
<h1 className="tit_1">건설기준 내용 관리</h1>
</div>
<h2 className="tit_2">건설기준 내용 관리</h2>
여기에 구현해주세요.
{/* <!--// 본문 --> */}
</div>

View File

@ -35,11 +35,9 @@ function ReferenceCodes(props) {
<div className="contents " id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">건설기준 관리</h1>
<h1 className="tit_1">참조코드 관리</h1>
</div>
<h2 className="tit_2">참조코드 관리</h2>
여기에 구현해주세요.
{/* <!--// 본문 --> */}
</div>

View File

@ -35,10 +35,9 @@ function SimilarityCheck(props) {
<div className="contents " id="contents">
{/* <!-- 본문 --> */}
<div className="top_tit">
<h1 className="tit_1">건설기준 관리</h1>
<h1 className="tit_1">유사성 검사</h1>
</div>
<h2 className="tit_2">유사성 검사</h2>
여기에 구현해주세요.
{/* <!--// 본문 --> */}

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef } from 'react';
import React, { useState } from 'react';
import {Link, useLocation, useNavigate} from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
@ -7,7 +7,7 @@ import CODE from "constants/code";
import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import { getLocalItem, setLocalItem, setSessionItem } from 'utils/storage';
import { setLocalItem } from 'utils/storage';
import InfoShareChk from "./InfoShareChk";
function Join(props) {
@ -21,7 +21,6 @@ function Join(props) {
const [userInfo, setUserInfo] = useState({ id: '', password: '', passwordChk: '', userNm: '', email: '', phoneNum: ''});
const [infoShareChk, setInfoShareChk] = useState(false);
const [submitFlag, setSubmitFlag] = useState(true);
const submitFormHandler = (e) => {
console.log("JoinContent submitFormHandler()");

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,88 @@
import React, {useEffect, useState} from "react";
import {Button, Modal, Nav} from "react-bootstrap";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import * as EgovNet from "api/egovFetch";
function DownloadModal({closeFn}){
const [tab, setTab] = useState(10);
const [subTabsVisible, setSubTabsVisible] = useState(false);
const [listData, setListData] = useState([]);
useEffect(() => {
EgovNet.requestFetch('/standardCode/standard-code-download-list?listCode='+tab,
{
method: "GET",
headers: {
'Content-type': 'application/json',
}
},
(resp) => {
setListData(resp.result.resultList);
},
function (resp) {
console.log("err response : ", resp);
}
);
}, [tab]);
return(
<>
<Modal.Header closeButton>
<Modal.Title id="example-modal-sizes-title-lg">통합다운로드</Modal.Title>
</Modal.Header>
<Modal.Body>
<Row className={"justify-content-start py-1 mx-1"}>
<Col xs={"auto px-1"}>
<div className={`tab ${tab === 10 ? 'active' : ''}`}
onClick={() => {setTab(10); setSubTabsVisible(false)}}>설계기준</div>
</Col>
<Col xs={"auto px-1"}>
<div className={`tab ${tab === 20 ? 'active' : ''}`}
onClick={() => {setTab(20); setSubTabsVisible(false)}}>표준시방서</div>
</Col>
<Col xs={"auto px-1"}>
<div className={`tab ${[40, 50, 60, 70, 80, 90].includes(tab) ? 'active' : ''}`}
onClick={() => {setTab(40); setSubTabsVisible(true)}}>전문시방서</div>
</Col>
</Row>
{subTabsVisible && (
<Nav className={"tabs"} variant={"tabs"} >
<Nav.Item><Nav.Link className={`${tab === 40 ? 'active' : ''}`} onClick={() => {setTab(40)}}>서울특별시</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${tab === 50 ? 'active' : ''}`} onClick={() => {setTab(50)}}>고속도로공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${tab === 60 ? 'active' : ''}`} onClick={() => {setTab(60)}}>한국농어촌공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${tab === 70 ? 'active' : ''}`} onClick={() => {setTab(70)}}>철도건설공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${tab === 80 ? 'active' : ''}`} onClick={() => {setTab(80)}}>LH한국토지주택공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${tab === 90 ? 'active' : ''}`} onClick={() => {setTab(90)}}>K-Water</Nav.Link></Nav.Item>
</Nav>
)}
<div className="board_list standard_code_modal download_list">
<div className="head">
<span>구분</span>
<span>코드</span>
<span>다운로드</span>
</div>
<div className={"result"}>
{listData.filter(item => {
return item;
}).map(item => {
return (
<div className="list_item">
<div className="mainCategory">{item.groupNm}</div>
<div className="middleCategory">{item.groupCurCd}</div>
<div className="kcscCd">
<Button size={"sm"} variant={"outline-secondary"}>다운로드</Button>
</div>
</div>
)
})}
</div>
</div>
</Modal.Body>
<Modal.Footer><Button onClick={closeFn}>닫기</Button></Modal.Footer>
</>
)
}
export default DownloadModal;

View File

@ -0,0 +1,46 @@
import React, {useState} from "react";
import {AiFillStar} from "react-icons/ai";
import {getLocalItem} from "utils/storage";
import * as EgovNet from "../../../api/egovFetch";
function FavoriteIcon({item}){
const [favoriteChk, setFavoriteChk] = useState(item.favoriteChk);
function favoriteStateChange(groupSeq, checked){
EgovNet.requestFetch(
'/standardCode/document-favorite',
{
method: "POST",
headers: {
'Content-type': 'application/json',
},
body:JSON.stringify({groupSeq: groupSeq, active: checked})
},
(resp) => {
},
function (resp) {
console.log("err response : ", resp);
}
);
}
return (
<div className="star clickable"
onClick={()=>{
const accessToken = getLocalItem('accessToken')
if(accessToken) {
favoriteStateChange(item.groupSeq, !favoriteChk)
setFavoriteChk(!favoriteChk)
}else{
alert("로그인 후 이용 가능한 서비스 입니다.")
}
}}>
<AiFillStar color={favoriteChk?'#FFC000':''}/>
</div>
);
}
export default FavoriteIcon;

View File

@ -0,0 +1,47 @@
import React, {useEffect, useState} from "react";
import {Button, Modal, Nav} from "react-bootstrap";
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import * as EgovNet from "api/egovFetch";
function HistoryModal({closeFn, standardCode}){
return(
<>
<Modal.Header closeButton>
<Modal.Title id="example-modal-sizes-title-lg">개정이력</Modal.Title>
</Modal.Header>
<Modal.Body>
<div>코드 : {standardCode.kcscCd}</div>
<div className="board_list standard_code_modal">
<div className="head">
<span>고시일</span>
<span>기준코드</span>
<span>신구건설기준비교</span>
</div>
<div className={"result"}>
{standardCode.historyList.filter(history => {
return history;
}).map(history => {
return (
<div className="list_item">
<div className="mainCategory">{history.rvsnYmd.split('T')[0]}</div>
<div className="middleCategory">
<Button size={"sm"} variant={"outline-secondary"}>다운로드</Button>
</div>
<div className="kcscCd">
<Button size={"sm"} variant={"outline-secondary"}>다운로드</Button>
</div>
</div>
)
})}
</div>
</div>
</Modal.Body>
<Modal.Footer><Button onClick={closeFn}>닫기</Button></Modal.Footer>
</>
)
}
export default HistoryModal;

View File

@ -1,143 +1,66 @@
import React, {useState, useEffect, useCallback, useRef} from 'react';
import {Link, useLocation, useParams} from 'react-router-dom';
import React from 'react';
import Col from "react-bootstrap/Col";
import Row from "react-bootstrap/Row";
import FavoriteIcon from "./FavoriteIcon";
import Button from "react-bootstrap/Button";
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import {StandardCodeListModal, StandardCodeListModalTable} from './StandardCodeListModal'
import {AiFillFileMarkdown, AiFillStar} from "react-icons/ai";
import StandardCodeSearchForm from "./StandardCodeSearchForm";
function StandardCodeList({listData, filterData, getHistoryModal}) {
function StandardCodeList({}) {
const {listCode} = useParams();
const [listData, setListData] = useState([])
const [filterData, setFilterData] = useState('');
const [resultCnt, setResultCnt] = useState(0);
const [groupSeq, setGroupSeq] = useState();
const [show, setShow] = useState(false);
function close() {
setShow(false);
function historyBtn(item){
getHistoryModal(item);
}
function showHandling(e) {
const param = e.currentTarget.dataset;
const groupSeq = param.groupSeq;
console.log(groupSeq);
EgovNet.requestFetch(
'/standardCode/codeListModal.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify(
groupSeq
)
}, (resp) => {
console.log(resp + "------------------------resp")
const body = [];
const head = [];
if (resp.length > 0) {
resp.forEach(function (item, index) {
const formattedDate = item.aplcnBgngYmd.match(/\d{4}-\d{2}-\d{2}/)[0];
const url = "https://www.kcsc.re.kr/file/DownloadGrp/" + item.docFileGrpId;
body.push(
<tr>
<td>{formattedDate}</td>
<td><a href={url}><AiFillFileMarkdown/></a></td>
<td></td>
</tr>)
})
head.push(
<tr>
<td>년도</td>
<td>기준코드</td>
<td>신구건설기준비교</td>
</tr>
)
}
setGroupSeq(<StandardCodeListModalTable head={head} content={body}/>);
}
)
setShow(true);
}
const retrieveList = useCallback((searchCondition) => {
if(searchCondition?.tab){
EgovNet.requestFetch('/standardCode/standard-code-list'+EgovNet.convParams(searchCondition),
{
method: "GET",
headers: {
'Content-type': 'application/json',
}
},
(resp) => {
setListData(resp.result.resultList);
setResultCnt(resp.result.resultCnt);
},
function (resp) {
console.log("err response : ", resp);
}
);
}
}, []);
return (
<div className="StandardCodeList container">
<div className="c_wrap codelistcontent">
<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li><Link to='#'>건설기준코드</Link></li>
<li><Link to={URL.STANDARD_CODE_LIST}>건설기준코드 검색</Link></li>
</ul>
</div>
<div className="layout">
<div className="contents NOTICE_LIST listtablediv">
<div className="top_tit">
<h2 className="tit_1">건설기준코드 검색</h2>
<div className={"result standard_code_result"}>
{listData.filter(item => {
if (item.groupNm.includes(filterData)) {
return item
}
return null
}).map(item => {
return (
<div className="list_item List_Codes">
<div className="mainCategory">{item.mainCategory}</div>
<div className="middleCategory">{item.middleCategory}</div>
<div className="kcscCd">{item.kcscCd}</div>
<div className="groupNm">{item.groupNm}<br/><span className={"text-danger"}>{item.rvsnRemark}</span></div>
<div className="Revisionhistory">
<Button size={"sm"} variant={"outline-secondary"} onClick={()=>{historyBtn(item)}}>개정 이력</Button>
</div>
<StandardCodeSearchForm param={listCode} reloadFunction={retrieveList}/>
<div><span>전체 {resultCnt} </span></div>
{/* <!-- 게시판목록 --> */}
<div className="board_list code_list">
<div className="head">
<span>대분류</span>
<span>중분류</span>
<span>코드번호</span>
<span>코드명</span>
<span>개정이력</span>
<span>보기</span>
<span>즐겨찾기</span>
</div>
<div className="result">
{listData.filter(item => {
if (item.groupNm.includes(filterData)) {
return item
<div className="fille">
<Row className={"justify-content-start"}>
{item.historyList.filter(history => {
return history;
}).map(history => {
let buttonClass = "btn btn-sm docInfoBtn docInfoActive "
let pClass = "yearInfo yearInfoActive";
if(history.docEr === 'E'){
buttonClass += "btn-success "
}else{
buttonClass += "btn-primary "
}
return null
}).map(item => {
return (
<div className="list_item List_Codes">
<div className="mainCategory">{item.mainCategory}</div>
<div className="middleCategory">{item.middleCategory}</div>
<div className="kcscCd">{item.kcscCd}</div>
<div className="groupNm">{item.groupNm}</div>
<div className="Revisionhistory"><a className="vieweratag" onClick={showHandling} data-groupSeq={item.groupSeq}>개정이력</a></div>
<div className="fille">{item.contentcount > 0 ? <a className="vieweratag" href={"/standardCode/viewer/" + item.kcscCd}>내용보기</a> : null}</div>
<div className="star"><AiFillStar/></div>
</div>
<Col xs={"auto"} className={"px-1"}>
<input type="button"
className={buttonClass}
value={history.docEr==='E'?'제':'개'}
onClick={()=>{
const rvsnYmd = new Date(history.rvsnYmd)
rvsnYmd.setHours(rvsnYmd.getHours()+9)
window.open("/standardCode/viewer/"+history.kcscCd+":"+rvsnYmd.toISOString().split('T')[0]);
}}
/>
<br/>
<p className={pClass}>{history.docYr}</p>
</Col>
)
})}
</div>
</Row>
</div>
<StandardCodeListModal size={"lg"} show={show} content={groupSeq} onClose={close} title={"개정이력"}/>
<FavoriteIcon item={item}/>
</div>
</div>
</div>
)
})}
</div>
);
}

View File

@ -1,30 +0,0 @@
import {Button, Modal, ModalBody, ModalFooter, ModalHeader, ModalTitle} from "react-bootstrap";
function StandardCodeListModal({show,content,onClose,title,size}){
return(
<Modal size={size} show={show} aria-labelledby="example-modal-sizes-title-lg">
<ModalHeader>
<ModalTitle id="example-modal-sizes-title-lg">{title}</ModalTitle>
</ModalHeader>
<ModalBody>
{content}
</ModalBody>
<ModalFooter><Button onClick={onClose}>닫기</Button></ModalFooter>
</Modal>)
}
function StandardCodeListModalTable({head,content}){
return(
<table>
<thead>
{head}
</thead>
<tbody>
{content}
</tbody>
</table>
)
}
export {StandardCodeListModal,StandardCodeListModalTable};

View File

@ -0,0 +1,111 @@
import React, {useState, useCallback} from 'react';
import {useParams} from 'react-router-dom';
import * as EgovNet from 'api/egovFetch';
import DownloadModal from './DownloadModal'
import StandardCodeSearchForm from "./StandardCodeSearchForm";
import Loading from "components/Loading";
import StandardCodeList from "./StandardCodeList";
import Modal from "react-bootstrap/Modal";
import HistoryModal from "./HistoryModal";
function StandardCodePage({}) {
const {listCode} = useParams();
const [listData, setListData] = useState([])
const [listLoading, setListLoading] = useState(true);
const [filterData, setFilterData] = useState('');
const [resultCnt, setResultCnt] = useState(0);
const [remarkCnt, setRemarkCnt] = useState(0);
const [modalContent, setModalContent] = useState([]);
const [show, setShow] = useState(false);
function close() {
setShow(false);
}
const retrieveList = useCallback((searchCondition) => {
setListLoading(true)
EgovNet.requestFetch('/standardCode/standard-code-list'+EgovNet.convParams(searchCondition),
{
method: "GET",
headers: {
'Content-type': 'application/json',
}
},
(resp) => {
setListData(resp.result.resultList);
setResultCnt(resp.result.resultCnt.allCnt);
setRemarkCnt(resp.result.resultCnt.remarkCnt);
setListLoading(false)
},
function (resp) {
console.log("err response : ", resp);
}
);
}, []);
function downloadModal(){
setShow(true);
setModalContent(<DownloadModal closeFn={close}/>)
}
function historyModal(item){
setShow(true);
setModalContent(<HistoryModal closeFn={close} standardCode={item}/>)
}
return (
<div className="">
<div className="c_wrap">
{/*<div className="location">
<ul>
<li><Link to={URL.MAIN} className="home">Home</Link></li>
<li>건설기준코드</li>
<li><Link to={URL.STANDARD_CODE_LIST} >건설기준코드 검색</Link></li>
</ul>
</div>*/}
<div className="layout">
<div className="contents NOTICE_LIST" id="contents">
{/*<div className="top_tit">
<h1 className="tit_1">건설기준코드 검색</h1>
</div>*/}
<div className="StandardCodeList container">
<div className="c_wrap codeListContent">
<div className="layout">
<div className="contents NOTICE_LIST listTableDiv">
<StandardCodeSearchForm param={listCode?listCode:'10'} reloadFunction={retrieveList} resultCnt={resultCnt} remarkCnt={remarkCnt} downloadModal={downloadModal}/>
<div className="board_list code_list">
<div className="head">
<span>대분류</span>
<span>중분류</span>
<span>코드번호</span>
<span>코드명</span>
<span>개정이력</span>
<span className={"text-start"}>보기</span>
<span>즐겨찾기</span>
</div>
{
listLoading?(<Loading loadingState={true}/>):(
<StandardCodeList listData={listData} filterData={filterData} getHistoryModal={historyModal}/>
)
}
</div>
<Modal size={"lg"} show={show} onHide={close}>
{modalContent}
</Modal>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
export default StandardCodePage;

View File

@ -1,16 +1,41 @@
import React, {useEffect, useState} from "react";
import {Nav} from "react-bootstrap";
import Row from "react-bootstrap/Row";
import Col from "react-bootstrap/Col";
import {Link} from "react-router-dom";
import Button from "react-bootstrap/Button";
import * as EgovNet from "../../../api/egovFetch";
function StandardCodeSearchForm({param, reloadFunction}){
function StandardCodeSearchForm({param, reloadFunction, resultCnt, remarkCnt, downloadModal}){
const [searchCondition, setSearchCondition] = useState({
pageIndex: 1,
tab: Number(param?.substring(0, 2)),
category1: param?.substring(2, 4),
category2: param?.substring(4, 6),
searchWrd: ''
category3: param?.substring(6, 8),
});
const [subTabsVisible, setSubTabsVisible] = useState(false);
const [cat1SelectOption, setCat1SelectOption] = useState([])
const [cat2SelectOption, setCat2SelectOption] = useState([])
const [cat3SelectOption, setCat3SelectOption] = useState([])
function getSelectBoxOption(groupCd, handler){
EgovNet.requestFetch(
'/standardCode/category-option?listCode='+groupCd,
{
method: "GET",
headers: {
'Content-type': 'application/json',
}
},
handler,
function (resp) {
console.log("err response : ", resp);
}
);
}
useEffect(() => {
if(searchCondition.tab){
if(searchCondition.tab !== 10 && searchCondition.tab !== 20){
@ -22,6 +47,43 @@ function StandardCodeSearchForm({param, reloadFunction}){
reloadFunction(searchCondition)
}, [searchCondition]);
useEffect(() => {
setSearchCondition({...searchCondition, category1: '', category2: '', category3: ''})
const groupCd = searchCondition.tab;
getSelectBoxOption(groupCd, (resp)=>{
const options = [];
resp.result.groupList.forEach(function (item, index){
options.push(<option value={item.groupCurCd}>{item.groupNm}</option>)
})
setCat1SelectOption(options)
})
}, [searchCondition.tab]);
useEffect(() => {
setSearchCondition({...searchCondition, category2: '', category3: ''})
const groupCd = searchCondition.tab+searchCondition.category1;
getSelectBoxOption(groupCd, (resp)=>{
const options = [];
resp.result.groupList.forEach(function (item, index){
options.push(<option value={item.groupCurCd}>{item.groupNm}</option>)
})
setCat2SelectOption(options)
})
}, [searchCondition.category1]);
useEffect(() => {
setSearchCondition({...searchCondition, category3: ''})
const groupCd = searchCondition.tab+searchCondition.category1+searchCondition.category2;
getSelectBoxOption(groupCd, (resp)=>{
const options = [];
resp.result.groupList.forEach(function (item, index){
options.push(<option value={item.groupCurCd}>{item.groupNm}</option>)
})
setCat3SelectOption(options)
})
}, [searchCondition.category2]);
return (
<>
<div className="condition">
@ -40,45 +102,61 @@ function StandardCodeSearchForm({param, reloadFunction}){
</li>
<li className="third_1 L">
<label className="f_select" htmlFor="sel1">
<select id="sel1" title="조건" value={searchCondition.category1}>
<select id="sel1" title="조건" value={searchCondition.category1}
onChange={(e)=>{setSearchCondition({...searchCondition, category1: e.target.value})}}>
<option value="">전체</option>
{cat1SelectOption}
</select>
</label>
</li>
<li className="third_1 L">
<label className="f_select w_306" htmlFor="sel1">
<select id="sel2" title="조건" value={searchCondition.category2}
onChange={(e)=>{setSearchCondition({...searchCondition, category2: e.target.value})}}>
<option value="">전체</option>
{cat2SelectOption}
</select>
</label>
</li>
<li className="third_1 L">
<label className="f_select w_306" htmlFor="sel1">
<select id="sel3" title="조건" value={searchCondition.category3}
onChange={(e)=>{setSearchCondition({...searchCondition, category3: e.target.value})}}>
<option value="">전체</option>
{cat3SelectOption}
</select>
</label>
</li>
<li className="third_1 L">
{remarkCnt?(
<span>전체 {resultCnt} / <span className={"text-danger"}>{remarkCnt}</span> </span>
):(
<span>전체 {resultCnt} </span>
)}
</select>
</label>
</li>
<li className="third_1 L">
<label className="f_select w_306" htmlFor="sel1">
<select id="sel2" title="조건" value={searchCondition.category2}>
<option value="">전체</option>
</select>
</label>
</li>
<li className="third_1 L">
<label className="f_select w_306" htmlFor="sel1">
<select id="sel3" title="조건" value={searchCondition.category3} >
<option value="">전체</option>
</select>
</label>
</li>
<li className="third_1 L">
<div className={`tab`}>통합 다운로드</div>
<div className={`tab`} onClick={downloadModal}>통합 다운로드</div>
</li>
</ul>
</div>
{subTabsVisible && (
<div className="right_col">
<div className="mini_board">
<ul>
<div className={`tab ${searchCondition.tab === 40 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 40})}}>서울특별시</div>
<div className={`tab ${searchCondition.tab === 50 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 50})}}>고속도로공사</div>
<div className={`tab ${searchCondition.tab === 60 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 60})}}>한국농어촌공사</div>
<div className={`tab ${searchCondition.tab === 70 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 70})}}>철도건설공사</div>
<div className={`tab ${searchCondition.tab === 80 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 80})}}>LH한국토지주택공사</div>
<div className={`tab ${searchCondition.tab === 90 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 90})}}>K-Water</div>
</ul>
</div>
</div>
)}
<Row className={"justify-content-between"}>
<Col>
{subTabsVisible && (
<Nav className={"tabs"} variant={"tabs"} >
<Nav.Item><Nav.Link className={`${searchCondition.tab === 40 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 40})}}>서울특별시</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${searchCondition.tab === 50 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 50})}}>고속도로공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${searchCondition.tab === 60 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 60})}}>한국농어촌공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${searchCondition.tab === 70 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 70})}}>철도건설공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${searchCondition.tab === 80 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 80})}}>LH한국토지주택공사</Nav.Link></Nav.Item>
<Nav.Item><Nav.Link className={`${searchCondition.tab === 90 ? 'active' : ''}`} onClick={() => {setSearchCondition({...searchCondition, tab: 90})}}>K-Water</Nav.Link></Nav.Item>
</Nav>
)}
</Col>
<Col xs={"auto"}>
<Button href={"/standardCode/info"} size={"sm"} variant={"secondary"}>건설기준코드 안내</Button>
</Col>
</Row>
</>
);
}

View File

@ -49,17 +49,13 @@ const BookmarkModal = ({docCode, docPart, ymd}) => {
})
const getCodeInfo = useCallback(() => {
console.groupCollapsed("EgovMain.getCodeInfo()");
EgovNet.requestFetch(
'/standardCode/getCodeInfo.do',
'/standardCode/code-info?docCode='+docCode,
{
method: "POST",
method: "GET",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
docCode: docCode
})
}
},
(resp) => {
const docInfo = resp.result.docInfo;

View File

@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import { useLocation, useParams } from 'react-router-dom';
import SbItem from './SbItem'
import Loading from '../../../components/Loading'
import Loading from 'components/Loading'
import BookmarkModal from './BookmarkModal';
import {SbContainer} from './Sb.style'
import {VwDiv, VwPtag} from './Vw.style'
@ -10,18 +10,21 @@ import Row from 'react-bootstrap/Row';
import Col from 'react-bootstrap/Col';
import Modal from 'react-bootstrap/Modal';
import * as EgovNet from 'api/egovFetch';
import {getLocalItem} from "../../../utils/storage";
import CODE from "../../../constants/code";
import {parseJwt} from "../../../utils/parseJwt";
import {getLocalItem} from "utils/storage";
import CODE from "constants/code";
import {parseJwt} from "utils/parseJwt";
import Button from "react-bootstrap/Button";
import {InputGroup} from "react-bootstrap";
import ViewerTree from "./ViewerTree";
function CodeViewer(props) {
const [treeLoading, setTreeLoading] = useState(true);
const [docLoading, setDocLoading] = useState(true);
const {linkedDocCode} = useParams();
const [docCode, setDocCode] = useState(linkedDocCode !== undefined?linkedDocCode.split(':')[0]:props.docCode);
const [ymd, setYmd] = useState(linkedDocCode !== undefined?linkedDocCode.split(':')[1]:props.ymd);
const [docInfo, setDocInfo] = useState();
const [codeTree, setCodeTree] = useState();
const [docSummary, setDocSummary] = useState();
const [docDetail, setDocDetail] = useState();
const [errorSelector, setErrorSelector] = useState();
@ -29,6 +32,8 @@ function CodeViewer(props) {
const [show, setShow] = useState(false);
const [bookMarkModal, setBookMarkModal] = useState();
const [colList, setColList] = useState([3,2,7]);
const sessionUser = parseJwt(getLocalItem('accessToken'));
const sessionUserSe = sessionUser?.userSe;
@ -44,65 +49,21 @@ function CodeViewer(props) {
console.log("viewer [docCode] : ", docCode);
const updateDocCode = (docCode)=>{
setDocSummary([<div></div>])
setDocDetail([<div>불러오는중</div>])
setDocInfo([])
setDocLoading(true);
setDocCode(docCode);
getCodeInfo(docCode);
getCodeDetailInfo(docCode);
}
const getCodeTree = ()=>{
EgovNet.requestFetch(
'/standardCode/getCodeTree.do',
{
method: "POST",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({})
},
(resp) => {
const menuData = resp.result.codeTree;
//
// https://garve32.tistory.com/52
const nest = (menuData, parent_seq = null, link = 'parent_seq') =>
menuData.filter(item => item[link] === parent_seq)
.map(item => ({ ...item, childrens: nest(menuData, item.seq) }));
const tree = nest(menuData);
let treeTag = [];
if(tree.length>0){
treeTag.push(
<SbContainer>
{tree.map((subItem) =>
<SbItem item={subItem} openDocCode={docCode} updateDocCode={updateDocCode} />
)}
</SbContainer>
)
}else{
treeTag.push(<div>검색된 결과가 없습니다.</div>); //
}
setCodeTree(treeTag);
setTreeLoading(false);
},
function (resp) {
console.log("err response : ", resp);
}
);
}
const getCodeInfo = useCallback((docCode) => {
console.groupCollapsed("EgovMain.getCodeInfo()");
EgovNet.requestFetch(
'/standardCode/getCodeInfo.do',
'/standardCode/code-info?docCode='+docCode,
{
method: "POST",
method: "GET",
headers: {
'Content-type': 'application/json'
},
body: JSON.stringify({
docCode: docCode
})
}
},
(resp) => {
const docInfo = resp.result.docInfo;
@ -307,6 +268,7 @@ function CodeViewer(props) {
clickBtn.parentElement.querySelector("p").className += " yearInfoActive"
getCodeDetailInfo(clickBtn.dataset.doccode, clickBtn.dataset.ymd);
})
const actionAppend = (el) => {
if(!el) return;
if(el.childNodes.length===0){
@ -420,9 +382,15 @@ function CodeViewer(props) {
}
)
}
function treeControl(){
if(colList[0]===3){
setColList([0,3,9]);
}else{
setColList([3,2,7]);
}
}
useEffect(() => {
getCodeTree();
getCodeInfo(docCode);
getCodeDetailInfo(docCode);
}, []);
@ -431,38 +399,49 @@ function CodeViewer(props) {
console.groupEnd("viewer");
return (
<>
{treeLoading || docLoading? (<Loading/>):(
<Row className="mx-0">
<Col xs={12} className="border-bottom">
<Row>
<Col xs={3}></Col>
<Col xs={9}>
<Row className="justify-content-between">
<Col xs={"auto"}>
<Row>
<Col xs={"auto"}>{docInfo}</Col>
<Col>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="연혁"/>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="비교"/>
</Col>
</Row>
</Col>
<Col xs={"auto"}>{errorSelector}</Col>
</Row>
</Col>
</Row>
</Col>
<Col xs={3} className="border-end viewerDiv">
{codeTree}
</Col>
<Col xs={2} className="border-end viewerDiv">
{docSummary}
</Col>
<Col xs={7} className="viewerDiv detailInfoDiv" ref={actionAppend}>
{docDetail}
</Col>
</Row>
)}
{/*{treeLoading || docLoading? ():()}*/}
<Loading loadingState={treeLoading || docLoading}/>
<Row className={`mx-0 ${treeLoading || docLoading?'d-none':''}`}>
<Col xs={12} className="border-bottom">
<Row>
<Col xs={3}></Col>
<Col xs={9}>
<Row className="justify-content-between">
<Col xs={"auto"}>
<Row>
<Col xs={"auto"}>{docInfo}</Col>
<Col>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="연혁"/>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="2단비교"/>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="신구조문"/>
<input type="button" className="btn btn-sm btn-primary optionBtn" value="첨부파일"/>
</Col>
</Row>
</Col>
<Col xs={"auto"}>
<InputGroup>
<Form.Control type="text" size={"sm"} placeholder={"문서 내 검색"}/>
{errorSelector}
</InputGroup>
</Col>
</Row>
</Col>
</Row>
</Col>
<Col xs={colList[0]} className={`border-end viewerDiv ${colList[0]===3?'':'d-none'}`}>
{/*{codeTree}*/}
<ViewerTree docCode={docCode} updateDocCode={updateDocCode} setTreeLoading={setTreeLoading}/>
</Col>
<Col xs={colList[1]} className="border-end viewerDiv">
<div>
<Button size={"sm"} variant={"outline-secondary"} onClick={treeControl}>{colList[0]===3?'트리 접기':'트리 펼치기'}</Button>
</div>
{docSummary}
</Col>
<Col xs={colList[2]} className="viewerDiv detailInfoDiv" ref={actionAppend}>
{docDetail}
</Col>
</Row>
<Modal show={show} onHide={handleClose} size="xl" keyboard={false} scrollable>
{bookMarkModal}
</Modal>
@ -471,8 +450,8 @@ function CodeViewer(props) {
}
CodeViewer.defaultProps = {
docCode: 'KCS 24 31 10',
docName: '용접(한계상태설계법)'
docCode: 'KDS 11 10 15',
docName: '지반계측'
}
export default CodeViewer;

View File

@ -0,0 +1,47 @@
import React, {useEffect, useState} from "react";
import {SbContainer} from "./Sb.style";
import SbItem from "./SbItem";
import * as EgovNet from "api/egovFetch";
function ViewerTree({docCode, updateDocCode, setTreeLoading}){
const [tree, setTree] = useState([]);
function getCodeTree(){
EgovNet.requestFetch(
'/standardCode/code-tree',
{
method: "GET",
headers: {
'Content-type': 'application/json'
}
},
(resp) => {
const menuData = resp.result.codeTree;
//
// https://garve32.tistory.com/52
const nest = (menuData, parent_seq = null, link = 'parent_seq') =>
menuData.filter(item => item[link] === parent_seq)
.map(item => ({ ...item, childrens: nest(menuData, item.seq) }));
setTree(nest(menuData));
setTreeLoading(false);
},
function (resp) {
console.log("err response : ", resp);
}
);
}
useEffect(() => {
getCodeTree()
}, []);
return (
<SbContainer>
{tree.map((subItem) =>
<SbItem item={subItem} openDocCode={docCode} updateDocCode={updateDocCode} />
)}
</SbContainer>
);
}
export default ViewerTree;

View File

@ -47,8 +47,8 @@ import EgovGalleryEdit from 'pages/inform/gallery/EgovGalleryEdit';
//ADMIN
import EgovAdminScheduleList from 'pages/admin/schedule/EgovAdminScheduleList';
import EgovAdminScheduleDetail from 'pages/admin/schedule/EgovAdminScheduleDetail';
import EgovAdminScheduleEdit from 'pages/admin/schedule/EgovAdminScheduleEdit';
// import EgovAdminScheduleDetail from 'pages/admin/schedule/EgovAdminScheduleDetail';
// import EgovAdminScheduleEdit from 'pages/admin/schedule/EgovAdminScheduleEdit';
import EgovAdminBoardList from 'pages/admin/board/EgovAdminBoardList';
import EgovAdminBoardEdit from 'pages/admin/board/EgovAdminBoardEdit';
@ -94,7 +94,7 @@ import AdminContentsPopUp from 'pages/admin/contents/PopUp';
import AdminContentsPopUpEditor from 'pages/admin/contents/PopUp/PopupEditor'; // - / /
import AdminContentsStandardResearch from 'pages/admin/contents/StandardResearch'; // - /
import AdminContentsStandardResearchEditor from 'pages/admin/contents/StandardResearch/StandardResearchEditor'; // / /
import AdminContentsTextMessages from 'pages/admin/contents/TextMessages'; // - /
// import AdminContentsTextMessages from 'pages/admin/contents/TextMessages'; // - /
// -
import AdminCommitteeProgressStatus from 'pages/admin/committee/ProgressStatus'; // - /
@ -110,11 +110,13 @@ import AdminLogsFileDownloadStatus from 'pages/admin/logs/FileDownloadStatus';
//
import CodeViewer from 'pages/standardCode/viewer/viewer';
import CodeViewer from 'pages/standardCode/viewer/CodeViewer';
import StandardCodePage from "../pages/standardCode/list/StandardCodePage";
import StandardCodeInfo from "../pages/standardCode/info/StandardCodeInfo";
import * as EgovNet from 'api/egovFetch'; // jwt
import initPage from 'js/ui';
import StandardCodeList from "../pages/standardCode/list/StandardCodeList";
const RootRoutes = () => {
//useLocation /admin/~ ( 1) */}
@ -136,6 +138,8 @@ const RootRoutes = () => {
(resp) => {
if (resp === false) {
setMounted(false);
alert("관리자 전용 페이지입니다.")
window.location.href="/";
} else {
setMounted(true); // true .
}
@ -248,9 +252,9 @@ const SecondRoutes = () => {
{/* ADMIN */}
<Route path={URL.ADMIN} element={<Navigate to={URL.ADMIN_SCHEDULE} />} />
<Route path={URL.ADMIN_SCHEDULE} element={<EgovAdminScheduleList />} />
<Route path={URL.ADMIN_SCHEDULE_DETAIL} element={<EgovAdminScheduleDetail />} />
<Route path={URL.ADMIN_SCHEDULE_CREATE} element={<EgovAdminScheduleEdit mode={CODE.MODE_CREATE} />} />
<Route path={URL.ADMIN_SCHEDULE_MODIFY} element={<EgovAdminScheduleEdit mode={CODE.MODE_MODIFY} />} />
{/*<Route path={URL.ADMIN_SCHEDULE_DETAIL} element={<EgovAdminScheduleDetail />} />*/}
{/*<Route path={URL.ADMIN_SCHEDULE_CREATE} element={<EgovAdminScheduleEdit mode={CODE.MODE_CREATE} />} />*/}
{/*<Route path={URL.ADMIN_SCHEDULE_MODIFY} element={<EgovAdminScheduleEdit mode={CODE.MODE_MODIFY} />} />*/}
<Route path={URL.ADMIN_BOARD} element={<EgovAdminBoardList />} />
<Route path={URL.ADMIN_BOARD_CREATE} element={<EgovAdminBoardEdit mode={CODE.MODE_CREATE} />} />
@ -304,7 +308,7 @@ const SecondRoutes = () => {
<Route path={URL.ADMIN__CONTENTS__STANDARDS_RESEARCH} element={<AdminContentsStandardResearch />} />
<Route path={URL.ADMIN__CONTENTS__STANDARDS_RESEARCH__CREATE} element={<AdminContentsStandardResearchEditor mode={CODE.MODE_CREATE} />} />
<Route path={URL.ADMIN__CONTENTS__STANDARDS_RESEARCH__MODIFY} element={<AdminContentsStandardResearchEditor mode={CODE.MODE_MODIFY} />} />
<Route path={URL.ADMIN__CONTENTS__TEXT_MESSAGES} element={<AdminContentsTextMessages />} />
{/*<Route path={URL.ADMIN__CONTENTS__TEXT_MESSAGES} element={<AdminContentsTextMessages />} />*/}
{/* 관리자 - 위원회 관리 */}
<Route path={URL.ADMIN__COMMITTEE__PROGRESS_STATUS} element={<AdminCommitteeProgressStatus />} />
@ -327,9 +331,10 @@ const SecondRoutes = () => {
<Route path={URL.STANDARD_CODE_VIEWER} element={<CodeViewer mode={CODE.MODE_READ} />} />
<Route path={URL.STANDARD_CODE_VIEWER_LINK} element={<CodeViewer mode={CODE.MODE_READ} />} />
<Route path={URL.STANDARD_CODE_INFO} element={<StandardCodeInfo />} />
{/*기준코드리스트*/}
<Route path={URL.STANDARD_CODE_LIST} element={<StandardCodeList />} />
<Route path={URL.STANDARD_CODE_LIST_LINK} element={<StandardCodeList />} />
<Route path={URL.STANDARD_CODE_LIST} element={<StandardCodePage />} />
<Route path={URL.STANDARD_CODE_LIST_LINK} element={<StandardCodePage />} />
</Routes>
<EgovFooter />

View File

@ -0,0 +1,64 @@
import PropTypes from 'prop-types';
import { useMemo } from 'react';
// material-ui
import { CssBaseline, StyledEngineProvider } from '@mui/material';
import { createTheme, ThemeProvider } from '@mui/material/styles';
// project import
import Palette from './palette';
import Typography from './typography';
import CustomShadows from './shadows';
import componentsOverride from './overrides';
// ==============================|| DEFAULT THEME - MAIN ||============================== //
export default function ThemeCustomization({ children }) {
const theme = Palette('light', 'default');
// eslint-disable-next-line react-hooks/exhaustive-deps
const themeTypography = Typography(`'Public Sans', sans-serif`);
const themeCustomShadows = useMemo(() => CustomShadows(theme), [theme]);
const themeOptions = useMemo(
() => ({
breakpoints: {
values: {
xs: 0,
sm: 768,
md: 1024,
lg: 1266,
xl: 1536
}
},
direction: 'ltr',
mixins: {
toolbar: {
minHeight: 60,
paddingTop: 8,
paddingBottom: 8
}
},
palette: theme.palette,
customShadows: themeCustomShadows,
typography: themeTypography
}),
[theme, themeTypography, themeCustomShadows]
);
const themes = createTheme(themeOptions);
themes.components = componentsOverride(themes);
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={themes}>
<CssBaseline />
{children}
</ThemeProvider>
</StyledEngineProvider>
);
}
ThemeCustomization.propTypes = {
children: PropTypes.node
};

View File

@ -0,0 +1,15 @@
// ==============================|| OVERRIDES - BADGE ||============================== //
export default function Badge(theme) {
return {
MuiBadge: {
styleOverrides: {
standard: {
minWidth: theme.spacing(2),
height: theme.spacing(2),
padding: theme.spacing(0.5)
}
}
}
};
}

View File

@ -0,0 +1,28 @@
// ==============================|| OVERRIDES - BUTTON ||============================== //
export default function Button(theme) {
const disabledStyle = {
'&.Mui-disabled': {
backgroundColor: theme.palette.grey[200]
}
};
return {
MuiButton: {
defaultProps: {
disableElevation: true
},
styleOverrides: {
root: {
fontWeight: 400
},
contained: {
...disabledStyle
},
outlined: {
...disabledStyle
}
}
}
};
}

View File

@ -0,0 +1,16 @@
// ==============================|| OVERRIDES - CARD CONTENT ||============================== //
export default function CardContent() {
return {
MuiCardContent: {
styleOverrides: {
root: {
padding: 20,
'&:last-child': {
paddingBottom: 20
}
}
}
}
};
}

View File

@ -0,0 +1,13 @@
// ==============================|| OVERRIDES - CHECKBOX ||============================== //
export default function Checkbox(theme) {
return {
MuiCheckbox: {
styleOverrides: {
root: {
color: theme.palette.secondary[300]
}
}
}
};
}

View File

@ -0,0 +1,40 @@
// ==============================|| OVERRIDES - CHIP ||============================== //
export default function Chip(theme) {
return {
MuiChip: {
styleOverrides: {
root: {
borderRadius: 4,
'&:active': {
boxShadow: 'none'
}
},
sizeLarge: {
fontSize: '1rem',
height: 40
},
light: {
color: theme.palette.primary.main,
backgroundColor: theme.palette.primary.lighter,
borderColor: theme.palette.primary.light,
'&.MuiChip-lightError': {
color: theme.palette.error.main,
backgroundColor: theme.palette.error.lighter,
borderColor: theme.palette.error.light
},
'&.MuiChip-lightSuccess': {
color: theme.palette.success.main,
backgroundColor: theme.palette.success.lighter,
borderColor: theme.palette.success.light
},
'&.MuiChip-lightWarning': {
color: theme.palette.warning.main,
backgroundColor: theme.palette.warning.lighter,
borderColor: theme.palette.warning.light
}
}
}
}
};
}

View File

@ -0,0 +1,28 @@
// ==============================|| OVERRIDES - ICON BUTTON ||============================== //
export default function IconButton(theme) {
return {
MuiIconButton: {
styleOverrides: {
root: {
borderRadius: 4
},
sizeLarge: {
width: theme.spacing(5.5),
height: theme.spacing(5.5),
fontSize: '1.25rem'
},
sizeMedium: {
width: theme.spacing(4.5),
height: theme.spacing(4.5),
fontSize: '1rem'
},
sizeSmall: {
width: theme.spacing(3.75),
height: theme.spacing(3.75),
fontSize: '0.75rem'
}
}
}
};
}

View File

@ -0,0 +1,25 @@
// ==============================|| OVERRIDES - INPUT LABEL ||============================== //
export default function InputLabel(theme) {
return {
MuiInputLabel: {
styleOverrides: {
root: {
color: theme.palette.grey[600]
},
outlined: {
lineHeight: '0.8em',
'&.MuiInputLabel-sizeSmall': {
lineHeight: '1em'
},
'&.MuiInputLabel-shrink': {
background: theme.palette.background.paper,
padding: '0 8px',
marginLeft: -6,
lineHeight: '1.4375em'
}
}
}
}
};
}

View File

@ -0,0 +1,17 @@
// ==============================|| OVERRIDES - LINER PROGRESS ||============================== //
export default function LinearProgress() {
return {
MuiLinearProgress: {
styleOverrides: {
root: {
height: 6,
borderRadius: 100
},
bar: {
borderRadius: 100
}
}
}
};
}

View File

@ -0,0 +1,11 @@
// ==============================|| OVERRIDES - LINK ||============================== //
export default function Link() {
return {
MuiLink: {
defaultProps: {
underline: 'hover'
}
}
};
}

View File

@ -0,0 +1,13 @@
// ==============================|| OVERRIDES - LIST ITEM ICON ||============================== //
export default function ListItemIcon() {
return {
MuiListItemIcon: {
styleOverrides: {
root: {
minWidth: 24
}
}
}
};
}

View File

@ -0,0 +1,47 @@
// material-ui
import { alpha } from '@mui/material/styles';
// ==============================|| OVERRIDES - OUTLINED INPUT ||============================== //
export default function OutlinedInput(theme) {
return {
MuiOutlinedInput: {
styleOverrides: {
input: {
padding: '10.5px 14px 10.5px 12px'
},
notchedOutline: {
borderColor: theme.palette.grey[300]
},
root: {
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.primary.light
},
'&.Mui-focused': {
boxShadow: `0 0 0 2px ${alpha(theme.palette.primary.main, 0.2)}`,
'& .MuiOutlinedInput-notchedOutline': {
border: `1px solid ${theme.palette.primary.light}`
}
},
'&.Mui-error': {
'&:hover .MuiOutlinedInput-notchedOutline': {
borderColor: theme.palette.error.light
},
'&.Mui-focused': {
boxShadow: `0 0 0 2px ${alpha(theme.palette.error.main, 0.2)}`,
'& .MuiOutlinedInput-notchedOutline': {
border: `1px solid ${theme.palette.error.light}`
}
}
}
},
inputSizeSmall: {
padding: '7.5px 8px 7.5px 12px'
},
inputMultiline: {
padding: 0
}
}
}
};
}

View File

@ -0,0 +1,14 @@
// ==============================|| OVERRIDES - TAB ||============================== //
export default function Tab(theme) {
return {
MuiTab: {
styleOverrides: {
root: {
minHeight: 46,
color: theme.palette.text.primary
}
}
}
};
}

View File

@ -0,0 +1,20 @@
// ==============================|| OVERRIDES - TABLE CELL ||============================== //
export default function TableCell(theme) {
return {
MuiTableCell: {
styleOverrides: {
root: {
fontSize: '0.875rem',
padding: 12,
borderColor: theme.palette.divider
},
head: {
fontWeight: 600,
paddingTop: 20,
paddingBottom: 20
}
}
}
};
}

View File

@ -0,0 +1,13 @@
// ==============================|| OVERRIDES - TABS ||============================== //
export default function Tabs() {
return {
MuiTabs: {
styleOverrides: {
vertical: {
overflow: 'visible'
}
}
}
};
}

View File

@ -0,0 +1,13 @@
// ==============================|| OVERRIDES - TYPOGRAPHY ||============================== //
export default function Typography() {
return {
MuiTypography: {
styleOverrides: {
gutterBottom: {
marginBottom: 12
}
}
}
};
}

View File

@ -0,0 +1,41 @@
// third-party
import { merge } from 'lodash';
// project import
import Badge from './Badge';
import Button from './Button';
import CardContent from './CardContent';
import Checkbox from './Checkbox';
import Chip from './Chip';
import IconButton from './IconButton';
import InputLabel from './InputLabel';
import LinearProgress from './LinearProgress';
import Link from './Link';
import ListItemIcon from './ListItemIcon';
import OutlinedInput from './OutlinedInput';
import Tab from './Tab';
import TableCell from './TableCell';
import Tabs from './Tabs';
import Typography from './Typography';
// ==============================|| OVERRIDES - MAIN ||============================== //
export default function ComponentsOverrides(theme) {
return merge(
Button(theme),
Badge(theme),
CardContent(),
Checkbox(theme),
Chip(theme),
IconButton(theme),
InputLabel(theme),
LinearProgress(),
Link(),
ListItemIcon(),
OutlinedInput(theme),
Tab(theme),
TableCell(theme),
Tabs(),
Typography()
);
}

View File

@ -0,0 +1,60 @@
// material-ui
import { createTheme } from '@mui/material/styles';
// third-party
import { presetPalettes } from '@ant-design/colors';
// project import
import ThemeOption from './theme';
// ==============================|| DEFAULT THEME - PALETTE ||============================== //
const Palette = (mode) => {
const colors = presetPalettes;
const greyPrimary = [
'#ffffff',
'#fafafa',
'#f5f5f5',
'#f0f0f0',
'#d9d9d9',
'#bfbfbf',
'#8c8c8c',
'#595959',
'#262626',
'#141414',
'#000000'
];
const greyAscent = ['#fafafa', '#bfbfbf', '#434343', '#1f1f1f'];
const greyConstant = ['#fafafb', '#e6ebf1'];
colors.grey = [...greyPrimary, ...greyAscent, ...greyConstant];
const paletteColor = ThemeOption(colors);
return createTheme({
palette: {
mode,
common: {
black: '#000',
white: '#fff'
},
...paletteColor,
text: {
primary: paletteColor.grey[700],
secondary: paletteColor.grey[500],
disabled: paletteColor.grey[400]
},
action: {
disabled: paletteColor.grey[300]
},
divider: paletteColor.grey[200],
background: {
paper: paletteColor.grey[0],
// default: paletteColor.grey.A50 // 뒷배경 색이 깔려서 주석처리함
}
}
});
};
export default Palette;

View File

@ -0,0 +1,13 @@
// material-ui
import { alpha } from '@mui/material/styles';
// ==============================|| DEFAULT THEME - CUSTOM SHADOWS ||============================== //
const CustomShadows = (theme) => ({
button: `0 2px #0000000b`,
text: `0 -1px 0 rgb(0 0 0 / 12%)`,
z1: `0px 2px 8px ${alpha(theme.palette.grey[900], 0.15)}`
// only available in paid version
});
export default CustomShadows;

View File

@ -0,0 +1,92 @@
// ==============================|| PRESET THEME - THEME SELECTOR ||============================== //
const Theme = (colors) => {
const { blue, red, gold, cyan, green, grey } = colors;
const greyColors = {
0: grey[0],
50: grey[1],
100: grey[2],
200: grey[3],
300: grey[4],
400: grey[5],
500: grey[6],
600: grey[7],
700: grey[8],
800: grey[9],
900: grey[10],
A50: grey[15],
A100: grey[11],
A200: grey[12],
A400: grey[13],
A700: grey[14],
A800: grey[16]
};
const contrastText = '#fff';
return {
primary: {
lighter: blue[0],
100: blue[1],
200: blue[2],
light: blue[3],
400: blue[4],
main: blue[5],
dark: blue[6],
700: blue[7],
darker: blue[8],
900: blue[9],
contrastText
},
secondary: {
lighter: greyColors[100],
100: greyColors[100],
200: greyColors[200],
light: greyColors[300],
400: greyColors[400],
main: greyColors[500],
600: greyColors[600],
dark: greyColors[700],
800: greyColors[800],
darker: greyColors[900],
A100: greyColors[0],
A200: greyColors.A400,
A300: greyColors.A700,
contrastText: greyColors[0]
},
error: {
lighter: red[0],
light: red[2],
main: red[4],
dark: red[7],
darker: red[9],
contrastText
},
warning: {
lighter: gold[0],
light: gold[3],
main: gold[5],
dark: gold[7],
darker: gold[9],
contrastText: greyColors[100]
},
info: {
lighter: cyan[0],
light: cyan[3],
main: cyan[5],
dark: cyan[7],
darker: cyan[9],
contrastText
},
success: {
lighter: green[0],
light: green[3],
main: green[5],
dark: green[7],
darker: green[9],
contrastText
},
grey: greyColors
};
};
export default Theme;

View File

@ -0,0 +1,71 @@
// ==============================|| DEFAULT THEME - TYPOGRAPHY ||============================== //
const Typography = (fontFamily) => ({
htmlFontSize: 16,
fontFamily,
fontWeightLight: 300,
fontWeightRegular: 400,
fontWeightMedium: 500,
fontWeightBold: 600,
h1: {
fontWeight: 600,
fontSize: '2.375rem',
lineHeight: 1.21
},
h2: {
fontWeight: 600,
fontSize: '1.875rem',
lineHeight: 1.27
},
h3: {
fontWeight: 600,
fontSize: '1.5rem',
lineHeight: 1.33
},
h4: {
fontWeight: 600,
fontSize: '1.25rem',
lineHeight: 1.4
},
h5: {
fontWeight: 600,
fontSize: '1rem',
lineHeight: 1.5
},
h6: {
fontWeight: 400,
fontSize: '0.875rem',
lineHeight: 1.57
},
caption: {
fontWeight: 400,
fontSize: '0.75rem',
lineHeight: 1.66
},
body1: {
fontSize: '0.875rem',
lineHeight: 1.57
},
body2: {
fontSize: '0.75rem',
lineHeight: 1.66
},
subtitle1: {
fontSize: '0.875rem',
fontWeight: 600,
lineHeight: 1.57
},
subtitle2: {
fontSize: '0.75rem',
fontWeight: 500,
lineHeight: 1.66
},
overline: {
lineHeight: 1.66
},
button: {
textTransform: 'capitalize'
}
});
export default Typography;

View File

@ -0,0 +1,19 @@
import PropTypes from 'prop-types';
// third-party
import SyntaxHighlighter from 'react-syntax-highlighter';
import { a11yDark } from 'react-syntax-highlighter/dist/esm/styles/hljs';
// ==============================|| CODE HIGHLIGHTER ||============================== //
export default function SyntaxHighlight({ children, ...others }) {
return (
<SyntaxHighlighter language="javacript" showLineNumbers style={a11yDark} {...others}>
{children}
</SyntaxHighlighter>
);
}
SyntaxHighlight.propTypes = {
children: PropTypes.node
};

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,11 @@
package com.dbnt.kcscbackend.admin.boards;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.service.AdminBoardsService;
import com.dbnt.kcscbackend.admin.config.entity.TcMenu;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@ -9,21 +13,27 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequiredArgsConstructor
@RequestMapping("/admin/boards")
@Tag(name="AdminBoardsController", description = "사이트관리 게시판현황")
@Tag(name = "AdminBoardsController", description = "사이트관리 게시판현황")
public class AdminBoardsController extends BaseController {
private final AdminBoardsService adminBoardsService;
/* ---- 게시판관리 ----- */
@Operation(
summary = "게시판 목록 조회",
description = "게시판 목록 조회",
@ -42,4 +52,82 @@ public class AdminBoardsController extends BaseController {
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "게시판 저장",
description = "게시판 저장",
tags = {"AdminBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "저장 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.PUT, value = "/board-mgt")
public ResultVO saveBoardMgt(@RequestBody @Valid TnBbs bbs, Errors errors, @AuthenticationPrincipal LoginVO user) {
ResultVO resultVO = new ResultVO();
if (user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
if (errors.hasErrors()) {
StringBuilder msg = new StringBuilder();
for (FieldError error : errors.getFieldErrors()) {
msg.append(error.getDefaultMessage());
msg.append("\n");
}
resultVO.setResultCode(ResponseCode.INPUT_CHECK_ERROR.getCode());
resultVO.setResultMessage(msg.toString());
} else {
System.out.println("@@@ bbs.getBbsSeq() : " + bbs.getBbsSeq());
adminBoardsService.saveBoard(bbs, user.getId());
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
}
}
return resultVO;
}
@Operation(
summary = "게시판 삭제",
description = "게시판 삭제",
tags = {"AdminBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "삭제 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.DELETE, value = "/board-mgt")
public ResultVO removeBoardMgt(@RequestBody TnBbs bbs, @AuthenticationPrincipal LoginVO user) {
ResultVO resultVO = new ResultVO();
if (user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
String result = adminBoardsService.deleteBoard(bbs, user.getId());
if (result == null) {
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
} else if (result.equals("notFind")) {
resultVO.setResultCode(ResponseCode.SAVE_ERROR.getCode());
resultVO.setResultMessage("대상이 존재하지 않습니다.");
}
}
return resultVO;
}
/* ---- 게시물관리 ----- */
@Operation(
summary = "게시물 목록 조회",
description = "게시물 목록 조회",
tags = {"AdminBoardsController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/post-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getPostList() throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("boardList", adminBoardsService.selectBoardList());
resultVO.setResult(resultMap);
return resultVO;
}
}

View File

@ -6,7 +6,8 @@ import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.time.LocalDate;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
@Getter
@Setter
@ -22,22 +23,25 @@ public class TnBbs {
private Long bbsSeq;
@Column(name = "bbs_id", nullable = false)
@NotBlank(message = "게시판 ID를 입력해주세요.")
private String bbsId;
@Column(name = "bbs_title", nullable = false)
@NotBlank(message = "게시판 이름을 입력해주세요.")
private String bbsTitle;
@Column(name = "bbs_desc")
@NotBlank(message = "게시판 설명을 입력해주세요.")
private String bbsDesc;
@Column(name = "bbs_type")
private String bbsType;
@Column(name = "bbs_ans_yn", nullable = false)
private char bbsAnsYn;
private String bbsAnsYn;
@Column(name = "bbs_repl_yn", nullable = false)
private char bbsReplYn;
private String bbsReplYn;
@Column(name = "read_role_grp_id")
private String readRoleGrpId;
@ -52,16 +56,16 @@ public class TnBbs {
private String frstCrtId;
@Column(name = "frst_crt_dt", nullable = false)
private LocalDate frstCrtDt;
private LocalDateTime frstCrtDt;
@Column(name = "last_chg_id")
private String lastChgId;
@Column(name = "last_chg_dt")
private LocalDate lastChgDt;
private LocalDateTime lastChgDt;
@Column(name = "use_yn", nullable = false)
private char useYn;
private String useYn;
@Column(name = "oldd_seq")
private Long olddSeq;

View File

@ -0,0 +1,83 @@
package com.dbnt.kcscbackend.admin.boards.entity;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.time.LocalDateTime;
@Getter
@Setter
@Entity
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Table(name = "tn_bbs_contents")
public class TnBbsContents {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "bbs_cont_seq")
private Integer bbsContSeq;
@Column(name = "bbs_seq", nullable = false)
private Integer bbsSeq;
@Column(name = "bbs_cont_title")
private String bbsContTitle;
@Column(name = "bbs_contents", columnDefinition = "TEXT")
private String bbsContents;
@Column(name = "bbs_cont_seq_group")
private Integer bbsContSeqGroup;
@Column(name = "bbs_cont_seq_parent")
private Integer bbsContSeqParent;
@Column(name = "bbs_cont_level", nullable = false)
private Integer bbsContLevel;
@Column(name = "bbs_cont_sort")
private Integer bbsContSort;
@Column(name = "file_grp_id")
private String fileGrpId;
@Column(name = "bbs_read_cnt", nullable = false)
private Integer bbsReadCnt;
@Column(name = "fixed_yn", nullable = false)
private String fixedYn;
@Column(name = "secret_yn", nullable = false)
private String secretYn;
@Column(name = "secret_pwd")
private String secretPwd;
@Column(name = "doc_info_seq")
private Integer docInfoSeq;
@Column(name = "ip_address", nullable = false)
private String ipAddress;
@Column(name = "frst_crt_id", nullable = false)
private String frstCrtId;
@Column(name = "frst_crt_dt", nullable = false)
private LocalDateTime frstCrtDt;
@Column(name = "last_chg_id")
private String lastChgId;
@Column(name = "last_chg_dt")
private LocalDateTime lastChgDt;
@Column(name = "use_yn", nullable = false)
private String useYn;
@Column(name = "old_seq")
private Integer oldSeq;
}

View File

@ -0,0 +1,8 @@
package com.dbnt.kcscbackend.admin.boards.repository;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import org.springframework.data.jpa.repository.JpaRepository;
public interface TnBbsContentsRepository extends JpaRepository<TnBbsContents, Long> {
}

View File

@ -2,7 +2,13 @@ package com.dbnt.kcscbackend.admin.boards.repository;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface TnBbsRepository extends JpaRepository<TnBbs, Long> {
@Query(value = "SELECT * FROM tn_bbs WHERE use_yn = 'Y' ORDER BY bbs_seq DESC", nativeQuery = true)
List<TnBbs> findAllByOrderByBbsSeqDesc();
}

View File

@ -1,19 +1,71 @@
package com.dbnt.kcscbackend.admin.boards.service;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbs;
import com.dbnt.kcscbackend.admin.boards.entity.TnBbsContents;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsContentsRepository;
import com.dbnt.kcscbackend.admin.boards.repository.TnBbsRepository;
import com.dbnt.kcscbackend.admin.config.entity.TcMenu;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
public class AdminBoardsService extends EgovAbstractServiceImpl {
private final TnBbsRepository tnBbsRepository;
private final TnBbsContentsRepository tnBbsContentsRepository;
public List<TnBbs> selectBoardList() { return tnBbsRepository.findAll(); }
public List<TnBbs> selectBoardList() {
return tnBbsRepository.findAllByOrderByBbsSeqDesc();
}
public Optional<TnBbs> selectBoard(Long bbsSeq) {
return tnBbsRepository.findById(bbsSeq);
}
@Transactional
public void saveBoard(TnBbs bbs, String userId) {
if (bbs.getBbsSeq() == null) {
bbs.setFrstCrtDt(LocalDateTime.now());
bbs.setFrstCrtId(userId);
bbs.setBbsAnsYn("N");
bbs.setBbsReplYn("N");
bbs.setUseYn("Y");
tnBbsRepository.save(bbs);
} else {
TnBbs savedBoard = tnBbsRepository.findById(bbs.getBbsSeq()).orElse(null);
savedBoard.setBbsId(bbs.getBbsId());
savedBoard.setBbsTitle(bbs.getBbsTitle());
savedBoard.setBbsDesc(bbs.getBbsDesc());
//savedBoard.setUseYn("Y");
savedBoard.setLastChgId(userId);
savedBoard.setLastChgDt(LocalDateTime.now());
tnBbsRepository.save(savedBoard);
}
}
@Transactional
public String deleteBoard(TnBbs bbs, String userId) {
TnBbs savedBoard = tnBbsRepository.findById(bbs.getBbsSeq()).orElse(null);
if (savedBoard == null) {
return "notFind";
} else {
savedBoard.setUseYn("N");
savedBoard.setLastChgDt(LocalDateTime.now());
savedBoard.setLastChgId(userId);
tnBbsRepository.save(savedBoard);
return null;
}
}
public List<TnBbsContents> selectPostList() {
return tnBbsContentsRepository.findAll();
}
}

View File

@ -1,6 +1,8 @@
package com.dbnt.kcscbackend.admin.config;
import com.dbnt.kcscbackend.admin.config.entity.TcMenu;
import com.dbnt.kcscbackend.admin.config.service.AdminCommitteeCodeManagementService;
import com.dbnt.kcscbackend.admin.standardResearch.service.AdminStandardResearchService;
import com.dbnt.kcscbackend.commonCode.entity.TcCodeGrp;
import com.dbnt.kcscbackend.commonCode.entity.TcCodeItem;
import com.dbnt.kcscbackend.admin.config.service.AdminConfigService;
@ -9,20 +11,21 @@ import com.dbnt.kcscbackend.commonCode.service.CommonCodeService;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Pageable;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.HashMap;
@ -37,6 +40,9 @@ public class AdminConfigController extends BaseController {
private final AdminConfigService adminConfigService;
private final CommonCodeService commonCodeService;
@Resource(name = "adminCommitteeCodeManagementService")
private AdminCommitteeCodeManagementService adminCommitteeCodeManagementService;
@Operation(
summary = "기본코드 그룹 조회",
description = "기본코드 그룹 조회",
@ -350,4 +356,50 @@ public class AdminConfigController extends BaseController {
return resultVO;
}
@Operation(
summary = "'위원회 코드 관리' 페이지에서 목록 불러오는 API",
description = "관리자 단에서 '환경설정' > '위원회코드 관리' 페이지에서 목록 불러오는 API",
tags = {"AdminConfigController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "303", description = "만료된 토큰"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@GetMapping(value = "/committee-code-management")
public ResultVO getCommitteeCodeManagement(
@AuthenticationPrincipal LoginVO user,
HttpServletRequest request,
@ApiParam(value="상위 code") @RequestParam(required=true) String paramCodeGroup,
@ApiParam(value="code level") @RequestParam(required=true) String paramCodeLevel
) throws Exception {
ResultVO resultVO = new ResultVO();
if(user == null) {
resultVO.setResultCode(ResponseCode.TOKEN_EXPIRED.getCode());
} else {
try {
Long upCmtSeq = null;
if (!paramCodeGroup.equals("null")) {
upCmtSeq = Long.parseLong(paramCodeGroup);
}
resultVO = adminCommitteeCodeManagementService.getCommitteeCodeManagement(resultVO, request, user, upCmtSeq, paramCodeLevel);
} catch (Exception e) {
resultVO.setResultCode(ResponseCode.FAILED.getCode());
resultVO.setResultMessage(e.getMessage());
}
}
System.out.println(
"\n--------------------------------------------------------------\n" +
request.getRequestURI() + " OUT:" +
"\n--------------------------------------------------------------\n" +
"resultVO.toString():" + "\n" +
resultVO.toString() + "\n" +
"\n--------------------------------------------------------------\n"
);
return resultVO;
}
}

View File

@ -0,0 +1,20 @@
package com.dbnt.kcscbackend.admin.config.service;
import com.dbnt.kcscbackend.admin.standardResearch.model.CreateStandardResearchVO;
import com.dbnt.kcscbackend.admin.standardResearch.model.UpdateStandardResearchVO;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.annotations.ApiParam;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
public interface AdminCommitteeCodeManagementService {
public ResultVO createCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, final MultipartHttpServletRequest multiRequest, CreateStandardResearchVO createStandardResearchVO) throws Exception;
public ResultVO getCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, Long upCmtSeq, String cmtType) throws Exception;
public ResultVO setCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, UpdateStandardResearchVO updateStandardResearchVO, Long popupId) throws Exception;
public ResultVO deleteCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, Long popupId) throws Exception;
}

View File

@ -0,0 +1,75 @@
package com.dbnt.kcscbackend.admin.config.service.impl;
import com.dbnt.kcscbackend.admin.config.service.AdminCommitteeCodeManagementService;
import com.dbnt.kcscbackend.admin.standardResearch.model.CreateStandardResearchVO;
import com.dbnt.kcscbackend.admin.standardResearch.model.UpdateStandardResearchVO;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.commonCode.repository.TnCmtOrgRepository;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service("adminCommitteeCodeManagementService")
@RequiredArgsConstructor
public class AdminCommitteeCodeManagementServiceImpl extends EgovAbstractServiceImpl implements AdminCommitteeCodeManagementService {
private final TnCmtOrgRepository tnCmtOrgRepository;
@Override
public ResultVO createCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, MultipartHttpServletRequest multiRequest, CreateStandardResearchVO createStandardResearchVO) throws Exception {
return null;
}
@Override
public ResultVO getCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, Long upCmtSeq, String cmtType) throws Exception {
System.out.println(
"\n--------------------------------------------------------------\n" +
request.getRequestURI() + " IN:" +
"\n--------------------------------------------------------------\n" +
"user.getEmail():" + "\n" +
user.getEmail() + "\n" +
"\n--------------------------------------------------------------\n"
);
List<Map<String, Object>> list = tnCmtOrgRepository.findByUseYnAndUpCmtSeqAndCmtTypeOrderByCmtOrder("Y", upCmtSeq, cmtType).stream()
.map(item -> {
Map<String, Object> returnMap = new HashMap<>();
returnMap.put("orgId", item.getCmtSeq());
returnMap.put("orgNm", item.getCmtNm());
returnMap.put("orgDesc", item.getCmtDesc());
return returnMap;
})
.collect(Collectors.toList());
Map<String, Object> dto = new HashMap<String, Object>();
dto.put("list", list);
resultVO.setResult(dto);
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
return resultVO;
}
@Override
public ResultVO setCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, UpdateStandardResearchVO updateStandardResearchVO, Long popupId) throws Exception {
return null;
}
@Override
public ResultVO deleteCommitteeCodeManagement(ResultVO resultVO, HttpServletRequest request, LoginVO user, Long popupId) throws Exception {
return null;
}
}

View File

@ -1,8 +1,10 @@
package com.dbnt.kcscbackend.admin.dashboard;
import com.dbnt.kcscbackend.admin.dashboard.dto.MonthlyUserLogDTO;
//import com.dbnt.kcscbackend.admin.dashboard.dto.MonthlyUserLogDTO;
import com.dbnt.kcscbackend.admin.dashboard.service.AdminDashboardService;
import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@ -10,6 +12,8 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@ -30,57 +34,116 @@ public class AdminDashboardController extends BaseController {
private final AdminDashboardService adminDashboardService;
@Operation(
summary = "일별 사용자 현황 차트 조회",
description = "일별 사용자 현황 차트 조회",
summary = "해당년도 메뉴/방문자수 월/요일별 조회",
description = "해당년도 메뉴/방문자수 월/요일별 조회",
tags = {"AdminDashboardController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/daily-user-log-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getDailyUserLogList() throws Exception {
@RequestMapping(method = RequestMethod.POST, value = "/menu-login", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getMenuVisit(@AuthenticationPrincipal LoginVO user)
throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
// 현재 날짜
// todo endDate 뒤에 .minus 지워야함
LocalDate endDate = LocalDate.now().minusMonths(6);
// 3개월 전 날짜 계산
LocalDate startDate = endDate.minusMonths(3);
resultMap.put("menuMonthlyList", adminDashboardService.selectMenuMonthly());
resultMap.put("menuDailyList", adminDashboardService.selectMenuDaily());
resultMap.put("loginMonthlyList", adminDashboardService.selectLoginMonthly());
resultMap.put("loginDailyList", adminDashboardService.selectLoginDaily());
resultMap.put("dailyUserLogList", adminDashboardService.selectDailyUserLogList(startDate, endDate));
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "월별 사용자 현황 차트 조회",
description = "월별 사용자 현황 차트 조회",
summary = "이번주 다운로드 조회",
description = "이번주 다운로드 조회",
tags = {"AdminDashboardController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/monthly-user-log-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getMonthlyUserLogList() throws Exception {
@RequestMapping(method = RequestMethod.POST, value = "/file", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getfile(@AuthenticationPrincipal LoginVO user)
throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
// 현재 날짜
// todo endDate 뒤에 .minus 지워야함
LocalDate endDate = LocalDate.now().minusMonths(6);
// 3개월 전 날짜 계산
LocalDate startDate = endDate.minusMonths(3);
resultMap.put("fileDailyList", adminDashboardService.selectFileDaily());
List<Object[]> result = adminDashboardService.selectMonthlyUserLogList(startDate, endDate);
List<MonthlyUserLogDTO> monthlyUserLogDTOList = result.stream()
.map(row -> new MonthlyUserLogDTO((String) row[0], (BigInteger) row[1]))
.collect(Collectors.toList());
resultMap.put("dailyUserLogList", monthlyUserLogDTOList);
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
resultVO.setResult(resultMap);
return resultVO;
}
// @Operation(
// summary = "일별 사용자 현황 차트 조회",
// description = "일별 사용자 현황 차트 조회",
// tags = {"AdminDashboardController"}
// )
// @ApiResponses(value = {
// @ApiResponse(responseCode = "200", description = "조회 성공"),
// @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
// })
// @RequestMapping(method = RequestMethod.GET, value = "/daily-user-log-list", consumes = MediaType.APPLICATION_JSON_VALUE)
// public ResultVO getDailyUserLogList() throws Exception {
// ResultVO resultVO = new ResultVO();
// Map<String, Object> resultMap = new HashMap<>();
//
// // 현재 날짜
// // todo endDate 뒤에 .minus 지워야함
// LocalDate endDate = LocalDate.now().minusMonths(6);
// // 3개월 전 날짜 계산
// LocalDate startDate = endDate.minusMonths(3);
//
// resultMap.put("dailyUserLogList", adminDashboardService.selectDailyUserLogList(startDate, endDate));
// resultVO.setResult(resultMap);
// return resultVO;
// }
//
// @Operation(
// summary = "월별 사용자 현황 차트 조회",
// description = "월별 사용자 현황 차트 조회",
// tags = {"AdminDashboardController"}
// )
// @ApiResponses(value = {
// @ApiResponse(responseCode = "200", description = "조회 성공"),
// @ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
// })
// @RequestMapping(method = RequestMethod.GET, value = "/monthly-user-log-list", consumes = MediaType.APPLICATION_JSON_VALUE)
// public ResultVO getMonthlyUserLogList() throws Exception {
// ResultVO resultVO = new ResultVO();
// Map<String, Object> resultMap = new HashMap<>();
//
// // 현재 날짜
// // todo endDate 뒤에 .minus 지워야함
// LocalDate endDate = LocalDate.now().minusMonths(6);
// // 3개월 전 날짜 계산
// LocalDate startDate = endDate.minusMonths(3);
//
// List<Object[]> result = adminDashboardService.selectMonthlyUserLogList(startDate, endDate);
// List<MonthlyUserLogDTO> monthlyUserLogDTOList = result.stream()
// .map(row -> new MonthlyUserLogDTO((String) row[0], (BigInteger) row[1]))
// .collect(Collectors.toList());
//
// resultMap.put("dailyUserLogList", monthlyUserLogDTOList);
// resultVO.setResult(resultMap);
// return resultVO;
// }
}

View File

@ -0,0 +1,83 @@
package com.dbnt.kcscbackend.admin.dashboard.repository;
import com.dbnt.kcscbackend.admin.logs.entity.TnDailyMenuLog;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface MenuMonthlyRepository extends JpaRepository<TnDailyMenuLog, Long> {
@Query(value = "WITH all_months AS (" +
" SELECT generate_series(DATE_TRUNC('year', CURRENT_DATE), DATE_TRUNC('year', CURRENT_DATE) + INTERVAL '11 month', INTERVAL '1 month') AS month_start" +
")" +
"SELECT COALESCE(SUM(tn.log_cnt), 0) AS log_cnt " +
"FROM all_months " +
"LEFT JOIN tn_daily_menu_log tn ON TO_CHAR(all_months.month_start, 'yyyy-mm') = TO_CHAR(tn.log_dt, 'yyyy-mm') " +
"GROUP BY TO_CHAR(all_months.month_start, 'yyyy-mm') " +
"ORDER BY TO_CHAR(all_months.month_start, 'yyyy-mm') ASC", nativeQuery = true)
List<Long> MenuMonthlyList();
@Query(value = "WITH all_days AS (" +
" SELECT generate_series(" +
" DATE_TRUNC('year', now())," +
" DATE_TRUNC('year', now()) + INTERVAL '1 year' - INTERVAL '1 day'," +
" INTERVAL '1 day'" +
" ) AS day" +
")" +
"SELECT COALESCE(SUM(tn.log_cnt), 0) AS log_cnt " +
"FROM all_days ad " +
"LEFT JOIN tn_daily_menu_log tn ON ad.day = DATE_TRUNC('day', tn.log_dt) " +
"GROUP BY TO_CHAR(ad.day, 'Day') " +
"ORDER BY MIN(ad.day)", nativeQuery = true)
List<Long> MenuDailyList();
@Query(value = "WITH all_months AS (" +
" SELECT generate_series(" +
" DATE_TRUNC('year', now())," +
" DATE_TRUNC('year', now()) + INTERVAL '11 month'," +
" INTERVAL '1 month'" +
" ) AS month_start" +
")" +
"SELECT COALESCE(SUM(tn.log_cnt), 0) AS log_cnt " +
"FROM all_months " +
"LEFT JOIN tn_daily_user_log tn ON TO_CHAR(all_months.month_start, 'yyyy-mm') = TO_CHAR(tn.log_dt, 'yyyy-mm') " +
"GROUP BY TO_CHAR(all_months.month_start, 'yyyy-mm') " +
"ORDER BY TO_CHAR(all_months.month_start, 'yyyy-mm') ASC", nativeQuery = true)
List<Long> LoginMonthlyList();
@Query(value = "WITH all_days AS (" +
" SELECT generate_series(" +
" DATE_TRUNC('year', CURRENT_DATE)," +
" DATE_TRUNC('year', CURRENT_DATE) + INTERVAL '1 year' - INTERVAL '1 day'," +
" INTERVAL '1 day'" +
" ) AS day" +
")" +
"SELECT COALESCE(SUM(tn.log_cnt), 0) AS log_cnt " +
"FROM all_days ad " +
"LEFT JOIN tn_daily_user_log tn ON ad.day = tn.log_dt " +
"GROUP BY TO_CHAR(ad.day, 'Day') " +
"ORDER BY MIN(ad.day)", nativeQuery = true)
List<Long> LoginDailyList();
@Query(value = "WITH all_days AS (" +
" SELECT generate_series(" +
" DATE_TRUNC('year', CURRENT_DATE)," +
" DATE_TRUNC('year', CURRENT_DATE) + INTERVAL '1 year' - INTERVAL '1 day'," +
" INTERVAL '1 day'" +
" ) AS day" +
")" +
"SELECT COALESCE(COUNT(tn.access_dt), 0) AS log_cnt " +
"FROM all_days ad " +
"LEFT JOIN (SELECT access_dt FROM public.th_attach_file_log WHERE\n" +
" access_dt >= CURRENT_DATE - INTERVAL '6 day') tn ON ad.day = DATE_TRUNC('day', tn.access_dt) " +
"GROUP BY TO_CHAR(ad.day, 'Day') " +
"ORDER BY MIN(ad.day)", nativeQuery = true)
List<Long> FileDailyList();
}

View File

@ -1,24 +1,38 @@
package com.dbnt.kcscbackend.admin.dashboard.service;
import com.dbnt.kcscbackend.admin.dashboard.entity.TnDailyUserLog;
import com.dbnt.kcscbackend.admin.dashboard.repository.TnDailyUserLogRepository;
//import com.dbnt.kcscbackend.admin.dashboard.entity.TnDailyUserLog;
import com.dbnt.kcscbackend.admin.dashboard.repository.MenuMonthlyRepository;
//import com.dbnt.kcscbackend.admin.dashboard.repository.TnDailyUserLogRepository;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
//import java.time.LocalDate;
import java.util.List;
@Service
@RequiredArgsConstructor
public class AdminDashboardService extends EgovAbstractServiceImpl {
private final TnDailyUserLogRepository tnDailyUserLogRepository;
// private final TnDailyUserLogRepository tnDailyUserLogRepository;
private final MenuMonthlyRepository menuMonthlyRepository;
public List<TnDailyUserLog> selectDailyUserLogList(LocalDate startDate, LocalDate endDate) {
return tnDailyUserLogRepository.findByLogDtBetweenOrderByLogDt(startDate, endDate);
}
public List<Long> selectMenuMonthly() { return menuMonthlyRepository.MenuMonthlyList(); }
public List<Object[]> selectMonthlyUserLogList(LocalDate startDate, LocalDate endDate) {
return tnDailyUserLogRepository.selectMonthlyUserLogStatistics(startDate, endDate);
}
public List<Long> selectMenuDaily() { return menuMonthlyRepository.MenuDailyList(); }
public List<Long> selectLoginMonthly() { return menuMonthlyRepository.LoginMonthlyList(); }
public List<Long> selectLoginDaily() { return menuMonthlyRepository.LoginDailyList(); }
public List<Long> selectFileDaily() { return menuMonthlyRepository.FileDailyList(); }
// public List<TnDailyUserLog> selectDailyUserLogList(LocalDate startDate, LocalDate endDate) {
// return tnDailyUserLogRepository.findByLogDtBetweenOrderByLogDt(startDate, endDate);
// }
//
// public List<Object[]> selectMonthlyUserLogList(LocalDate startDate, LocalDate endDate) {
// return tnDailyUserLogRepository.selectMonthlyUserLogStatistics(startDate, endDate);
// }
}

View File

@ -1,8 +1,6 @@
package com.dbnt.kcscbackend.admin.standardResearch;
import com.dbnt.kcscbackend.admin.contents.popUp.model.CreatePopupVO;
import com.dbnt.kcscbackend.admin.contents.popUp.model.UpdatePopupVO;
import com.dbnt.kcscbackend.admin.contents.popUp.utils.EgovFileMngUtil;
import com.dbnt.kcscbackend.admin.standardResearch.model.CreateStandardResearchVO;
import com.dbnt.kcscbackend.admin.standardResearch.model.UpdateStandardResearchVO;
@ -11,7 +9,6 @@ import com.dbnt.kcscbackend.auth.entity.LoginVO;
import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

View File

@ -179,7 +179,7 @@ public class EgovLoginApiController extends BaseController {
if (refreshToken != null){
String serverToken = refreshToken.getRefreshToken();
if(egovJwtTokenUtil.getUserSeFromToken(clientToken).equals(egovJwtTokenUtil.getUserSeFromToken(serverToken))){
return true;
return egovJwtTokenUtil.getUserIdFromToken(clientToken).equals("admin");
}
}
return false;

View File

@ -34,6 +34,9 @@ public class LoginVO implements Serializable{
*
*/
private static final long serialVersionUID = -8274004534207618049L;
@Schema(description = "사용자 번호")
private Integer userSeq;
@Schema(description = "아이디")
@Pattern(regexp = "^[a-zA-Z]{1}[a-zA-Z0-9_]{4,11}$")

View File

@ -8,5 +8,6 @@ import java.util.List;
public interface TnCmtOrgRepository extends JpaRepository<TnCmtOrg, TnCmtOrg.TnCmtOrgId> {
List<TnCmtOrg> findByUseYnAndUpCmtSeqOrderByCmtOrder(String useYn, Long upCmtSeq);
TnCmtOrg findByUseYnAndCmtSeq(String useYn, Long cmtSeq);
List<TnCmtOrg> findByUseYnAndUpCmtSeqAndCmtTypeOrderByCmtOrder(String useYn, Long upCmtSeq, String cmtType);
}

View File

@ -67,6 +67,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
LoginVO loginVO = new LoginVO();
if( verificationFlag ){
logger.debug("jwtToken validated");
loginVO.setUserSeq(Integer.parseInt(jwtTokenUtil.getUserSeqFromToken(jwtToken)));
loginVO.setId(id);
loginVO.setUserSe( jwtTokenUtil.getUserSeFromToken(jwtToken) );
// loginVO.setUniqId( jwtTokenUtil.getInfoFromToken("uniqId",jwtToken) );

View File

@ -57,7 +57,8 @@ public class CustomUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticati
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
MediaType jsonMimeType = MediaType.APPLICATION_JSON;
HashMap<String, Object> resultMap = new HashMap<>();
if(securityUser.getUserId().equals("admin") && !adminIpList.contains(ClientUtils.getRemoteIP(request))){
/*if(securityUser.getUserId().equals("admin") && !adminIpList.contains(ClientUtils.getRemoteIP(request))){
resultMap.put("resultCode", ResponseCode.FAILED.getCode());
resultMap.put("resultMessage", "관리자 계정은 지정된 아이피에서만 접속할 수 있습니다.\n필요한 경우 관리자에게 요청하십시오.\n접속자 아이피: "+ClientUtils.getRemoteIP(request));
}else{
@ -69,7 +70,13 @@ public class CustomUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticati
// response.addHeader("Authorization", "BEARER "+accessToken);
// Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken);
// response.addCookie(refreshTokenCookie);
}
}*/
String accessToken = jwtTokenUtil.generateAccessToken(securityUser, request.getRemoteAddr());
String refreshToken = jwtTokenUtil.generateRefreshTokenToken(securityUser, request.getRemoteAddr());
resultMap.put("resultCode", ResponseCode.SUCCESS.getCode());
resultMap.put("accessToken", accessToken);
resultMap.put("refreshToken", refreshToken);
if (jsonConverter.canWrite(resultMap.getClass(), jsonMimeType)) {
jsonConverter.write(resultMap, jsonMimeType, new ServletServerHttpResponse(response));

View File

@ -83,7 +83,7 @@ public class SecurityConfig {
"/swagger-ui/**",
/*기준코드 조회*/
"/standardCode/**.do"
"/standardCode/**"
};
private static final String[] ORIGINS_WHITELIST = {
"http://localhost:3000",

View File

@ -6,6 +6,7 @@ import com.dbnt.kcscbackend.config.common.ResponseCode;
import com.dbnt.kcscbackend.config.common.ResultVO;
import com.dbnt.kcscbackend.standardCode.entity.TnDocumentCodeList;
import com.dbnt.kcscbackend.standardCode.entity.TnDocumentContent;
import com.dbnt.kcscbackend.standardCode.entity.TnDocumentFavorites;
import com.dbnt.kcscbackend.standardCode.entity.TnDocumentInfo;
import com.dbnt.kcscbackend.standardCode.service.StandardCodeService;
import com.dbnt.kcscbackend.standardCode.service.StandardCodeVO;
@ -60,7 +61,7 @@ public class StandardCodeController extends BaseController {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.POST, value = "/getCodeTree.do", consumes = MediaType.APPLICATION_JSON_VALUE)
@RequestMapping(method = RequestMethod.GET, value = "/code-tree")
public ResultVO getCodeTree() throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
@ -98,8 +99,8 @@ public class StandardCodeController extends BaseController {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@PostMapping(value = "/getCodeInfo.do", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getCodeInfo(@RequestBody StandardCodeVO param, @AuthenticationPrincipal LoginVO user)
@GetMapping(value = "/code-info", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getCodeInfo(StandardCodeVO param, @AuthenticationPrincipal LoginVO user)
throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
@ -151,25 +152,10 @@ public class StandardCodeController extends BaseController {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
String tab = tnDocumentInfo.getTab() != null ? tnDocumentInfo.getTab() : "";
String category1 = tnDocumentInfo.getCategory1() != null ? tnDocumentInfo.getCategory1() : "";
String category2 = tnDocumentInfo.getCategory2() != null ? tnDocumentInfo.getCategory2() : "";
String category3 = tnDocumentInfo.getCategory3() != null ? tnDocumentInfo.getCategory3() : "";
Integer categorySeq1 = standardCodeService.selectStandardCodeGroupSeq(tab);
Integer categorySeq2 = standardCodeService.selectStandardCodeGroupSeq(tab + category1);
Integer categorySeq3 = standardCodeService.selectStandardCodeGroupSeq(tab + category1 + category2);
resultMap.put("category1List", standardCodeService.selectStandardCodeCategoryList(categorySeq1));
resultMap.put("category2List", standardCodeService.selectStandardCodeCategoryList(categorySeq2));
resultMap.put("category3List", standardCodeService.selectStandardCodeCategoryList(categorySeq3));
tnDocumentInfo.setListCode(tab + category1 + category2 + category3);
List<TnDocumentCodeList> tnDocumentCodeList = standardCodeService.selectStandardCodeList(tnDocumentInfo);
resultMap.put("resultList", tnDocumentCodeList);
Integer totCnt = tnDocumentCodeList.get(0).getContentcount();
resultMap.put("resultCnt", totCnt);
resultMap.put("user", user);
tnDocumentInfo.makeListCode();
tnDocumentInfo.setUserSeq(user.getUserSeq());
resultMap.put("resultList", standardCodeService.selectStandardCodeList(tnDocumentInfo));
resultMap.put("resultCnt", standardCodeService.selectStandardCodeListCnt(tnDocumentInfo));
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
@ -177,6 +163,84 @@ public class StandardCodeController extends BaseController {
return resultVO;
}
@Operation(
summary = "건설기준코드 다운로드 리스트 조회",
description = "건설기준코드 다운로드 리스트 조회",
tags = {"StandardCodeController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@GetMapping(value = "/standard-code-download-list")
public ResultVO selectStandardCodeDownloadList(TnDocumentInfo tnDocumentInfo, @AuthenticationPrincipal LoginVO user)
throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
String listCode = tnDocumentInfo.getListCode();
if(listCode.equals("60")){
listCode += "______";
}else{
listCode += "____";
}
resultMap.put("resultList", standardCodeService.selectTnDocumentGroupToGroupFullCdLike(listCode));
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "건설기준코드 검색조건 옵션 조회",
description = "건설기준코드 검색조건 옵션 조회",
tags = {"StandardCodeController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@GetMapping(value = "/category-option")
public ResultVO getCategoryOption(TnDocumentInfo tnDocumentInfo) throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
resultMap.put("groupList", standardCodeService.selectTnDocumentGroupToGroupFullCdLike(tnDocumentInfo.getListCode()+"__"));
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
resultVO.setResult(resultMap);
return resultVO;
}
@Operation(
summary = "건설기준코드 즐겨찾기 추가, 삭제",
description = "건설기준코드 즐겨찾기 추가, 삭제",
tags = {"StandardCodeController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "수정 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@PostMapping(value = "/document-favorite")
public ResultVO setDocumentFavorite(@RequestBody TnDocumentFavorites favorites, @AuthenticationPrincipal LoginVO loginUser) throws Exception {
ResultVO resultVO = new ResultVO();
if(favorites.getActive()==null){
resultVO.setResultCode(ResponseCode.SAVE_ERROR.getCode());
resultVO.setResultMessage(ResponseCode.SAVE_ERROR.getMessage());
return resultVO;
}
if(favorites.getActive()){
standardCodeService.saveFavorites(loginUser.getUserSeq(), favorites.getGroupSeq());
}else{
standardCodeService.deleteFavorites(loginUser.getUserSeq(), favorites.getGroupSeq());
}
resultVO.setResultCode(ResponseCode.SUCCESS.getCode());
resultVO.setResultMessage(ResponseCode.SUCCESS.getMessage());
return resultVO;
}
@Operation(
summary = "건설기준코드 개정이력 조회",
description = "건설기준코드 개정이력 조회",

View File

@ -10,6 +10,7 @@ import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Transient;
import java.util.List;
@Getter
@Setter
@ -28,15 +29,24 @@ public class TnDocumentCodeList {
private String middleCategory;
@Column(name = "group_nm")
private String groupNm;
@Column(name = "rvsn_remark")
private String rvsnRemark;
@Column(name = "kcsc_cd")
private String kcscCd;
@Column(name = "doc_file_grp_id")
private String docFileGrpId;
@Column(name = "contentcount")
private Integer contentcount;
@Column(name = "parent_group_seq")
private String parentGroupSeq;
@Column(name = "group_full_cd")
private String groupFullCd;
@Transient
private Integer allCnt;
@Transient
private Integer remarkCnt;
@Transient
private Boolean favoriteChk;
@Transient
private List<TnDocumentInfo> historyList;
}

View File

@ -0,0 +1,40 @@
package com.dbnt.kcscbackend.standardCode.entity;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.springframework.format.annotation.DateTimeFormat;
import javax.persistence.*;
import java.io.Serializable;
import java.time.LocalDateTime;
@Getter
@Setter
@Entity
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Table(name = "tn_document_favorites")
@IdClass(TnDocumentFavorites.TnDocumentFavoritesId.class)
public class TnDocumentFavorites {
@Id
@Column(name = "user_seq")
private Integer userSeq;
@Id
@Column(name = "group_seq")
private Integer groupSeq;
@Transient
private Boolean active;
@Embeddable
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class TnDocumentFavoritesId implements Serializable {
private Integer userSeq;
private Integer groupSeq;
}
}

View File

@ -1,5 +1,6 @@
package com.dbnt.kcscbackend.standardCode.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.NoArgsConstructor;
@ -92,11 +93,18 @@ public class TnDocumentInfo {
@Transient
private String searchWrd;
@Transient
private String tab;
private String tab = "";
@Transient
private String category1;
private String category1 = "";
@Transient
private String category2;
private String category2 = "";
@Transient
private String category3;
private String category3 = "";
@Transient
private Integer userSeq;
@JsonIgnore
public void makeListCode(){
setListCode(getTab()+getCategory1()+getCategory2()+getCategory3());
}
}

Some files were not shown because too many files have changed in this diff Show More