Merge branch 'dev'

# Conflicts:
#	README.md
main
Lim Jonguk 3 years ago
commit 456f48edd6

@ -1,3 +1,3 @@
REACT_APP_VERSION = v0.0.1 REACT_APP_VERSION = v0.0.1
REACT_APP_API_URL=http://localhost:8090 REACT_APP_API_URL=http://localhost:8090
REACT_APP_SERVER_TIMEOUT=60000 REACT_APP_SERVER_TIMEOUT=6000

@ -10,3 +10,84 @@ core-js 버전 3.3 미만은 유지보수가 안되니 사용하지 말라
# yarn add yarn-audit-fix -D # yarn add yarn-audit-fix -D
# npx yarn-audit-fix # npx yarn-audit-fix
``` ```
### yarn install 실패시 : --force vs --legacy-peer-deps
```text
--force : 충돌 우회
--legacy-peer-deps : 충돌 무시
--force 시도 실패시 --legacy-peer-deps 실행
$ yarn install --force --network-timeout 6000000
```
### npm(yarn) install시 ECOCKETTIMEOUT 에러
```shell
npm(yarn) install --network-timeout 6000000
# 캐쉬 삭제
npm
npm cache verify or npm cache clean --force
yarn
yarn cache clean --force
```
### useEffect, useMemo, useCallback lifeCycle
```javascript
// componentDidMount
useEffect(() => {
// 실행 할 로직
}, []);
// componentDidUpdate
const mounted = useRef(false);
useEffect(() => {
if(!mounted.current) mounted.current = true;
// 실행 할 로직
}, [/*변경되는값*/]);
// componentDidMount, componentDidUpdate 둘다
useEffect(() => {
// 실행 할 로직
}, [/*변경되는값*/]);
```
### useMemo(callback, [변경되는값]);
```text
- 두번째 배열이 바뀌기전까지 값을 기억
- 함수 컴포넌트는 매번 함수가 새로 그려지며 실행 --> 한번만 실행되면 되는 함수도 계속 호출되는 문제 발생
- 변경되는 값이 없다면 한번만 실행후 값을 보관하는 역할로 사용
```
### useCallback(callback, [변경되는값]);
```text
- 두번째 배열이 바뀌기전까지 함수 자체를 기억
- 함수 생성 자체가 오래걸리는 경우(=함수 내의 연산이 복잡한 경우)쓰면 최적화에 도움됨
- 변경되는 값이 없다면 state 값을 맨처음 값만 기억(console로 확인)
--> 변경되는 값이 있을때 새로운 값을 기억
- 자식컴포넌트에 함수를 props로 내릴때 반드시 useCallback 사용
--> 자식 리렌더링 방지
```
### useMemo()와 useCallback() 차이점
```text
- useMemo : '함수 return 값'을 기억
- useCallback : '함수 reference'를 기억
```
### useRef()와 useMemo() 차이점
```text
useRef : 클래스로 치면 멤버변수 혹은 dom객체 처럼 특정한 '값'만 기억해야 할 때
useMemo : 복잡한 함수의 'retur값'을 기억해야 할 때
```
### typeof : primitive type string return vs instanceof
```js
'undefined' | 'boolean' | 'number' | 'string' | 'object' | 'function'
typeof 변수명
typeof 변수명 === 'object'
변수명 instanceof 클래스
```

@ -3,7 +3,6 @@
"version": "0.0.1", "version": "0.0.1",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@auth0/auth0-spa-js": "^1.19.4",
"@emotion/cache": "^11.7.1", "@emotion/cache": "^11.7.1",
"@emotion/react": "^11.7.1", "@emotion/react": "^11.7.1",
"@emotion/styled": "^11.6.0", "@emotion/styled": "^11.6.0",
@ -23,16 +22,15 @@
"@mui/x-data-grid": "^5.5.0", "@mui/x-data-grid": "^5.5.0",
"@reduxjs/toolkit": "^1.7.2", "@reduxjs/toolkit": "^1.7.2",
"@tabler/icons": "^1.53.0", "@tabler/icons": "^1.53.0",
"amazon-cognito-identity-js": "^5.2.6",
"apexcharts": "^3.33.1", "apexcharts": "^3.33.1",
"axios": "^0.25.0", "axios": "^0.25.0",
"axios-mock-adapter": "^1.20.0", "axios-mock-adapter": "^1.20.0",
"chance": "^1.1.8", "chance": "^1.1.8",
"core-js": "^3",
"csstype": "^3.0.10", "csstype": "^3.0.10",
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"draft-js": "^0.11.7", "draft-js": "^0.11.7",
"emoji-picker-react": "^3.5.1", "emoji-picker-react": "^3.5.1",
"firebase": "^9.6.6",
"formik": "^2.2.9", "formik": "^2.2.9",
"framer-motion": "^4.1.13", "framer-motion": "^4.1.13",
"history": "^5.2.0", "history": "^5.2.0",
@ -128,6 +126,7 @@
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.3.0",
"prettier": "^2.5.1", "prettier": "^2.5.1",
"react-error-overlay": "6.0.9", "react-error-overlay": "6.0.9",
"sass": "^1.49.7" "sass": "^1.49.7",
"yarn-audit-fix": "^9.2.0"
} }
} }

@ -1,133 +0,0 @@
import axios, { AxiosInstance, AxiosInterceptorManager, AxiosRequestConfig, AxiosResponse } from 'axios';
import Swal from 'sweetalert2';
type CustomResponseFormat<T = any> = {
response: T, // e<T>;
refreshedToken?: string
};
export interface CustomInstance extends AxiosInstance {
interceptors: {
request: AxiosInterceptorManager<AxiosRequestConfig>,
response: AxiosInterceptorManager<AxiosResponse<CustomResponseFormat>>
};
getUri(config?: AxiosRequestConfig): string;
request<T>(config: AxiosRequestConfig): Promise<T>;
get<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
delete<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
head<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
options<T>(url: string, config?: AxiosRequestConfig): Promise<T>;
post<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
put<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
patch<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
}
const reqApi: CustomInstance = axios.create({
baseURL: process.env.NODE_ENV === 'development' ? process.env.REACT_APP_API_URL : '',
withCredentials: process.env.NODE_ENV === 'development', // 개발시만 사용 : crossdomain
timeout: 1000
// headers: {
// 'Content-Type': 'application/json'
// // AUTH_HEADER_NAME: accessToken
// }
// params: {key: key}
});
/**
* before axios request
*/
reqApi.interceptors.request.use(
(config) => {
Swal.fire({
title: 'Please Wait ...',
// html: '',
// imageUrl:
// timer: 10000,
didOpen: () => Swal.showLoading()
}).then((r) => {});
return config;
},
({ config, request, response, ...error }) => {
console.error('========== ApiService.request error Data ==========');
return alertError(config, request, response, error);
}
);
/**
* after axios response
*/
reqApi.interceptors.response.use(
(response: AxiosResponse) => {
Swal.close();
if (!response.data.success) {
Swal.fire({
icon: 'error',
title: 'Api Error',
html: `${response.data.message}`,
// imageUrl:
timer: 5000
}).then((r) => {});
console.log(response);
}
return Promise.resolve(response.data);
},
({ config, request, response, ...error }) => {
console.error('========== ApiService.response Error Data ==========');
alertError(config, request, response, error);
// error 데이타 return
// return response.data;
return error;
}
);
/**
* 에러 처리
* TODO :: 토큰 에러인 경우 업무 정의에 따라 alert 처리 여부등 결정하여 내용 추가
* @param config
* @param request
* @param response
* @param error
*/
const alertError = (config: AxiosRequestConfig, request: any, response: AxiosResponse, error: Error) => {
if (!response) {
Swal.fire({
icon: 'error',
title: 'Api Error',
html: `시스템 에러~~~~~~~~~~~~`,
// imageUrl:
timer: 5000
}).then((r) => {});
return;
}
const errCode = response.data.code;
const errMsg = response.data.error;
console.error(`${errCode}: ${errMsg}`);
console.error('=================================');
// Alert.error(`${errCode}: ${errMsg}`);
Swal.fire({
icon: 'error',
title: 'Api Error',
html: `${errCode}: ${errMsg}`,
// imageUrl:
timer: 5000
}).then((r) => {});
// return Promise.reject({
// config,
// //message: errMsg,
// response,
// ...error
// })
};
export default reqApi;

@ -1,14 +0,0 @@
import { BOARD_LIST_URL } from 'commons/ApiUrl';
import axios from 'utils/axios';
class BoardService {
// eslint-disable-next-line no-return-await
getBoardList = async (params) => await axios.get(BOARD_LIST_URL, { params });
// const res = await axios.get(BOARD_LIST_URL, { params });
// return res;
// axios.get(BOARD_LIST_URL, { params }).then((r) => {
// console.log(r);
// return r;
// });
}
export default new BoardService();

@ -1,17 +1,17 @@
import reqApi from './ApiService';
import { CMM_CODE_LIST_URL } from 'commons/ApiUrl'; import { CMM_CODE_LIST_URL } from 'commons/ApiUrl';
import { IApiResponse } from 'types/api'; import axios from 'utils/axios';
import { IComboCode, IParam } from 'types/Data';
class CmmService { class CmmService {
// eslint-disable-next-line class-methods-use-this // eslint-disable-next-line no-return-await
getComboCodeList(params) { getComboCodeList = async (params) => await axios.get(CMM_CODE_LIST_URL, { params });
return reqApi.get(CMM_CODE_LIST_URL, { params });
// getComboCodeList(params) {
// return reqApi.get(CMM_CODE_LIST_URL, { params });
// .then(r => console.log(r)) // .then(r => console.log(r))
// .catch(e => { // .catch(e => {
// console.log(e) // console.log(e)
// }); // });
} // }
} }
export default CmmService; export default CmmService;

@ -0,0 +1,32 @@
import { BOARD_LIST_URL, SIMSA_LIST_URL } from 'commons/ApiUrl';
import axios from 'utils/axios';
class OpstBizService {
// eslint-disable-next-line no-return-await
getBoardList = async (params) => {
const res = await axios.get(BOARD_LIST_URL, { params });
if (res.success) {
res.data = res.data.map((d, idx) => ({ ...d, rowId: idx }));
return res;
}
return res;
};
// const res = await axios.get(BOARD_LIST_URL, { params });
// return res;
// axios.get(BOARD_LIST_URL, { params }).then((r) => {
// console.log(r);
// return r;
// });
// eslint-disable-next-line no-return-await
getSimsa680GroupList = async (params) => {
const res = await axios.get(SIMSA_LIST_URL, { params });
if (res.success) {
res.data = res.data.map((d, idx) => ({ ...d, rowId: idx }));
return res;
}
return res;
};
}
export default new OpstBizService();

@ -3,3 +3,4 @@
export const LOGIN_URL = '/api/v1/ctgy/account/login'; export const LOGIN_URL = '/api/v1/ctgy/account/login';
export const CMM_CODE_LIST_URL = '/api/v1/biz/cmm/combo'; export const CMM_CODE_LIST_URL = '/api/v1/biz/cmm/combo';
export const BOARD_LIST_URL = '/api/v1/ctgy/board'; export const BOARD_LIST_URL = '/api/v1/ctgy/board';
export const SIMSA_LIST_URL = '/api/v1/ctgy/simsa/review';

@ -76,7 +76,7 @@ const XitCmm = {
* validation check error message * validation check error message
* @param message * @param message
*/ */
alertParam: (message: string) => { alertParam: (message) => {
Swal.fire({ Swal.fire({
icon: 'warning', icon: 'warning',
html: message, html: message,
@ -90,7 +90,7 @@ const XitCmm = {
* API Error message * API Error message
* @param message * @param message
*/ */
alertError: (message: string) => { alertError: (message) => {
Swal.fire({ Swal.fire({
icon: 'error', icon: 'error',
title: 'API error', title: 'API error',
@ -108,10 +108,7 @@ const XitCmm = {
* @param validator * @param validator
*/ */
useInput<T>( useInput(initalValue, validator) {
initalValue: T,
validator?: (value: string) => boolean
): [T, Dispatch<SetStateAction<T>>, (e?: React.ChangeEvent<HTMLInputElement>) => void] {
const [value, setValue] = useState < typeof initalValue > initalValue; const [value, setValue] = useState < typeof initalValue > initalValue;
const changer = useCallback( const changer = useCallback(
(e) => { (e) => {
@ -171,8 +168,8 @@ const XitCmm = {
* @param name * @param name
* @returns string * @returns string
*/ */
getParameterByName: (name: string) => { getParameterByName: (name) => {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); name = name.replace(/[\\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp(`[\\?&]${name}=([^&#]*)`); const regex = new RegExp(`[\\?&]${name}=([^&#]*)`);
const results = regex.exec(window.location.search); const results = regex.exec(window.location.search);
return results == null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')); return results == null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
@ -198,7 +195,7 @@ const XitCmm = {
* @param name * @param name
* @returns {string|number|null} * @returns {string|number|null}
*/ */
urlParam: (name: string) => { urlParam: (name) => {
// var url = decodeURIComponent(window.location.href); // var url = decodeURIComponent(window.location.href);
const results = new RegExp(`[?&]${name}=([^&#]*)`).exec(window.location.href); const results = new RegExp(`[?&]${name}=([^&#]*)`).exec(window.location.href);
if (results == null) { if (results == null) {
@ -212,7 +209,7 @@ const XitCmm = {
* @param paramName * @param paramName
* @returns {string} * @returns {string}
*/ */
getParameters: (paramName: string) => { getParameters: (paramName) => {
// 리턴값을 위한 변수 선언 // 리턴값을 위한 변수 선언
let returnValue; let returnValue;
@ -240,7 +237,7 @@ const XitCmm = {
* @param value - The value of the cookie * @param value - The value of the cookie
* @param expireDays * @param expireDays
*/ */
setCookie: (name: string, value: string, expireDays: number = 1) => { setCookie: (name, value, expireDays = 1) => {
const todayDate = new Date(); const todayDate = new Date();
todayDate.setTime(todayDate.getTime() + expireDays * 24 * 60 * 60 * 1000); todayDate.setTime(todayDate.getTime() + expireDays * 24 * 60 * 60 * 1000);
const expired = todayDate.toISOString(); // .toGMTString(); const expired = todayDate.toISOString(); // .toGMTString();
@ -251,14 +248,14 @@ const XitCmm = {
* *
* @param name * @param name
*/ */
getCookie: (name: string) => document.cookie.split(';').some((c) => c.trim().startsWith(`${name}=`)), getCookie: (name) => document.cookie.split(';').some((c) => c.trim().startsWith(`${name}=`)),
/** /**
* *
* @param name - The name of the cookie to be set * @param name - The name of the cookie to be set
* @param value - The value of the cookie * @param value - The value of the cookie
*/ */
removeCookie: (name: string, value?: string) => { removeCookie: (name, value) => {
if (!value) document.cookie = `${name}=Max-Age=-99999999;`; if (!value) document.cookie = `${name}=Max-Age=-99999999;`;
else document.cookie = `${name}=${encodeURIComponent(value)}${{ expires: 'Sun, 01-May-2019 14:00:00 UTC' }}`; else document.cookie = `${name}=${encodeURIComponent(value)}${{ expires: 'Sun, 01-May-2019 14:00:00 UTC' }}`;
} }

@ -13,7 +13,7 @@ const config = {
fontFamily: `'Roboto', sans-serif`, fontFamily: `'Roboto', sans-serif`,
borderRadius: 4, borderRadius: 4,
outlinedFilled: true, outlinedFilled: true,
navType: 'light', // light, dark navType: 'dark', // light, dark
presetColor: 'default', // default, theme1, theme2, theme3, theme4, theme5, theme6 presetColor: 'default', // default, theme1, theme2, theme3, theme4, theme5, theme6
locale: 'ko', // 'en' - English, 'fr' - French, 'ro' - Romanian, 'zh' - Chinese locale: 'ko', // 'en' - English, 'fr' - French, 'ro' - Romanian, 'zh' - Chinese
rtlLayout: false, rtlLayout: false,

@ -64,6 +64,7 @@ export const JWTProvider = ({ children }) => {
setLocalStorage(ACCESS_TOKEN_NAME, accessToken); setLocalStorage(ACCESS_TOKEN_NAME, accessToken);
const response = await axios.get('/api/v1/ctgy/user'); const response = await axios.get('/api/v1/ctgy/user');
console.log(response); console.log(response);
// TODO : 적용 필요
const { userid, email, name } = response.data; const { userid, email, name } = response.data;
dispatch({ dispatch({
type: LOGIN, type: LOGIN,

@ -131,7 +131,7 @@ const opst = {
id: 'opst-4-1', id: 'opst-4-1',
title: <FormattedMessage id="opst-3-1" />, title: <FormattedMessage id="opst-3-1" />,
type: 'item', type: 'item',
url: '/disabled/dataMgt', url: '/disabled/data',
icon: icons.IconDatabase icon: icons.IconDatabase
}, },
{ {

@ -11,8 +11,10 @@ const DashboardDefault = Loadable(lazy(() => import('views/dashboard/Default')))
const DashboardAnalytics = Loadable(lazy(() => import('views/dashboard/Analytics'))); const DashboardAnalytics = Loadable(lazy(() => import('views/dashboard/Analytics')));
// opst page routing // opst page routing
const PublicBoard = Loadable(lazy(() => import('views/biz/board/public'))); const PublicBoard = Loadable(lazy(() => import('views/biz/board/PublicBoard')));
const Board = Loadable(lazy(() => import('views/biz/board'))); const Board = Loadable(lazy(() => import('views/biz/board/Board')));
const ParkingReview = Loadable(lazy(() => import('views/biz/parking/Review')));
const ParkingRegister = Loadable(lazy(() => import('views/biz/parking/Regist')));
// ==============================|| MAIN ROUTING ||============================== // // ==============================|| MAIN ROUTING ||============================== //
@ -41,11 +43,11 @@ const MainRoutes = {
/* 주정차 */ /* 주정차 */
{ {
path: '/parking/review', path: '/parking/review',
element: <Board /> element: <ParkingReview />
}, },
{ {
path: '/parking/register', path: '/parking/register',
element: <Board /> element: <ParkingRegister />
}, },
/* 거주자 */ /* 거주자 */
{ {

@ -8,14 +8,14 @@ import { useTheme } from '@mui/material/styles';
// project imports // project imports
const MuiGridList = ({ columns, rowsState, totalCount, setRowsState }) => { const MuiGridList = ({ setRowId, columns, rowsState, totalCount, setRowsState }) => {
// const { columns, rowsState, totalCount, setRowsState } = props; // const { columns, rowsState, totalCount, setRowsState } = props;
const theme = useTheme(); const theme = useTheme();
return ( return (
<Box <Box
sx={{ sx={{
height: 700, height: 670,
width: '100%', width: '100%',
'& .MuiDataGrid-root': { '& .MuiDataGrid-root': {
border: 'none', border: 'none',
@ -34,7 +34,7 @@ const MuiGridList = ({ columns, rowsState, totalCount, setRowsState }) => {
> >
<DataGrid <DataGrid
paginationMode="server" paginationMode="server"
getRowId={(row) => row.ciContentno} getRowId={(row) => row.rowId}
rowCount={totalCount} rowCount={totalCount}
checkboxSelection checkboxSelection
disableSelectionOnClick disableSelectionOnClick
@ -45,7 +45,7 @@ const MuiGridList = ({ columns, rowsState, totalCount, setRowsState }) => {
{...rowsState} {...rowsState}
onPageChange={(page) => setRowsState((prev) => ({ ...prev, page }))} onPageChange={(page) => setRowsState((prev) => ({ ...prev, page }))}
onPageSizeChange={(pageSize) => setRowsState((prev) => ({ ...prev, page: 0, pageSize }))} onPageSizeChange={(pageSize) => setRowsState((prev) => ({ ...prev, page: 0, pageSize }))}
rowsPerPageOptions={[5, 10, 50, 100]} rowsPerPageOptions={[10, 50, 100]}
pagination pagination
/> />
</Box> </Box>
@ -53,6 +53,7 @@ const MuiGridList = ({ columns, rowsState, totalCount, setRowsState }) => {
}; };
MuiGridList.propTypes = { MuiGridList.propTypes = {
setRowId: PropTypes.func,
columns: PropTypes.array, columns: PropTypes.array,
rowsState: PropTypes.any, rowsState: PropTypes.any,
totalCount: PropTypes.number, totalCount: PropTypes.number,

@ -1,7 +1,20 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
// material-ui // material-ui
import { Button, Divider, FormControl, FormControlLabel, Grid, OutlinedInput, Radio, RadioGroup } from '@mui/material'; import {
Button,
Divider,
FormControl,
FormControlLabel,
FormLabel,
Grid,
InputAdornment,
MenuItem,
OutlinedInput,
Radio,
RadioGroup,
Select
} from '@mui/material';
// assets // assets
import { IconSearch } from '@tabler/icons'; import { IconSearch } from '@tabler/icons';
@ -11,16 +24,16 @@ import MainCard from 'ui-component/cards/MainCard';
// project imports // project imports
import MuiGridList from 'ui-component/MuiGridList'; import MuiGridList from 'ui-component/MuiGridList';
import boardService from 'apis/BoardService'; import opstBizService from 'apis/OpstBizService';
const Index = () => { const Board = () => {
const [category, setCategory] = useState('ciTitle'); const [category, setCategory] = useState('ciTitle');
const [searchTxt, setSearchTxt] = useState(''); const [searchTxt, setSearchTxt] = useState('');
const [totalCount, setTotalCount] = useState(0); const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({ const [rowsState, setRowsState] = useState({
page: 0, page: 0,
pageSize: 5, pageSize: 10,
rows: [] rows: []
// loading: false // loading: false
}); });
@ -39,23 +52,9 @@ const Index = () => {
{ headerName: 'step', field: 'ciStep' }, { headerName: 'step', field: 'ciStep' },
{ headerName: 'level', field: 'ciRevel' }, { headerName: 'level', field: 'ciRevel' },
{ headerName: '비번', field: 'ciPass' }, { headerName: '비번', field: 'ciPass' },
{ { headerName: 'email', field: 'ciEmail' },
headerName: 'email',
field: 'ciEmail',
renderCell: (params) => (
<strong>
{params.value}
<Button variant="contained" color="primary" size="small">
Open
</Button>
</strong>
)
},
{ headerName: '내용', field: 'ciContents' }, { headerName: '내용', field: 'ciContents' },
{ { headerName: 'IP', field: `ciIp` }
headerName: 'IP',
field: `ciIp`
}
]; ];
const handleSearch = async (event) => { const handleSearch = async (event) => {
if (event.type === 'keydown' && event.key === 'Enter') { if (event.type === 'keydown' && event.key === 'Enter') {
@ -66,7 +65,7 @@ const Index = () => {
useEffect(() => { useEffect(() => {
let params = { let params = {
page: rowsState.page + 1, page: rowsState.page,
size: rowsState.pageSize size: rowsState.pageSize
}; };
@ -77,14 +76,14 @@ const Index = () => {
}; };
} }
boardService.getBoardList(params).then((response) => { opstBizService.getBoardList(params).then((response) => {
console.log(response); console.log(response);
if (response && response.data) { if (response && response.data) {
setTotalCount(response.count); setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data })); setRowsState((prevState) => ({ ...prevState, rows: response.data }));
} }
}); });
}, [rowsState.page, rowsState.pageSize, searchTxt]); // rowsState.page, rowsState.pageSize, rowsState.rows]); }, [rowsState.page, rowsState.pageSize, category, searchTxt]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return ( return (
<MainCard> <MainCard>
@ -92,23 +91,31 @@ const Index = () => {
<Grid item xs={12} lg={6}> <Grid item xs={12} lg={6}>
<Grid container spacing={1}> <Grid container spacing={1}>
<Grid item> <Grid item>
<FormControl> <Select id="category" name="reviewYear" defaultValue="ciTitle" onChange={(e) => setCategory(e.target.value)}>
<RadioGroup <MenuItem key="1" value="ciTitle">
row 제목
aria-label="category" </MenuItem>
name="row-radio-buttons-group" <MenuItem key="2" value="ciName">
value={category} 이름
onChange={(e) => setCategory(e.target.value)} </MenuItem>
> <MenuItem key="3" value="ciContents">
<FormControlLabel value="ciTitle" control={<Radio />} label="제목" /> 내용
<FormControlLabel value="ciName" control={<Radio />} label="이름" /> </MenuItem>
<FormControlLabel value="ciContents" control={<Radio />} label="내용" /> </Select>
</RadioGroup>
</FormControl>
</Grid> </Grid>
<Grid item> <Grid item>
<OutlinedInput id="input-search-list-style1" placeholder="Search" onKeyDown={handleSearch} size="small" autoFocus /> <OutlinedInput
placeholder="Search"
onKeyDown={handleSearch}
size="small"
autoFocus
endAdornment={
<InputAdornment position="end">
<IconSearch stroke={1.5} size="1rem" /> <IconSearch stroke={1.5} size="1rem" />
</InputAdornment>
}
/>
{/* <IconSearch stroke={1.5} size="1rem" /> */}
</Grid> </Grid>
</Grid> </Grid>
</Grid> </Grid>
@ -123,4 +130,4 @@ const Index = () => {
</MainCard> </MainCard>
); );
}; };
export default Index; export default Board;

@ -1,121 +0,0 @@
import React, { useState, useEffect } from 'react';
import { NavLink } from 'react-router-dom';
// material-ui
import { DataGrid } from '@mui/x-data-grid';
import { Box, Button } from '@mui/material';
import ListItemText from '@mui/material/ListItemText';
import { useTheme } from '@mui/material/styles';
// project imports
import CmmService from 'apis/CmmService';
import boardService from '../../../apis/BoardService';
const columns = [
{ headerName: '게시판코드', field: 'ciCode' },
{ headerName: '글번호', field: 'ciContentno' },
{ headerName: '제목', field: 'ciTitle', editable: true },
{ headerName: '사용자ID', field: 'ciId' },
{ headerName: '사용자 비번', field: 'ciPwd' },
{ headerName: '사용자 이름', field: 'ciName' },
{ headerName: '등록일', field: 'ciNalja' },
{ headerName: '등록시간', field: 'ciTime' },
{ headerName: '조회수', field: 'ciHit' },
{ headerName: 'ref', field: 'ciRef' },
{ headerName: 'step', field: 'ciStep' },
{ headerName: 'level', field: 'ciRevel' },
{ headerName: '비번', field: 'ciPass' },
{
headerName: 'email',
field: 'ciEmail',
renderCell: (params) => (
<strong>
{params.value}
<Button variant="contained" color="primary" size="small">
Open
</Button>
</strong>
)
},
{ headerName: '내용', field: 'ciContents' },
{
headerName: 'IP',
field: `ciIp`
}
];
const cmmService = new CmmService();
const BoardList = () => {
const theme = useTheme();
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 5,
rows: []
// loading: false
});
const [ciDiv, setCiDiv] = useState();
useEffect(() => {
const params = {
page: rowsState.page + 1,
size: rowsState.pageSize
};
boardService.getBoardList(params).then((response) => {
console.log(response);
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
}, [rowsState.page, rowsState.pageSize]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
// <CardContent>
// <div style={{ height: 700, width: '100%' }}>
<Box
sx={{
height: 700,
width: '100%',
'& .MuiDataGrid-root': {
border: 'none',
'& .MuiDataGrid-cell': {
borderColor: theme.palette.mode === 'dark' ? theme.palette.text.primary + 15 : 'grey.200'
},
'& .MuiDataGrid-columnsContainer': {
color: theme.palette.mode === 'dark' ? 'grey.600' : 'grey.900',
borderColor: theme.palette.mode === 'dark' ? theme.palette.text.primary + 15 : 'grey.200'
},
'& .MuiDataGrid-columnSeparator': {
color: theme.palette.mode === 'dark' ? theme.palette.text.primary + 15 : 'grey.200'
}
}
}}
>
<DataGrid
paginationMode="server"
getRowId={(row) => row.ciContentno}
rowCount={totalCount}
checkboxSelection
disableSelectionOnClick
// isRowSelectable={(params: any) => params.row.id > 0}
// rows={tableData}
columns={columns}
// pageSize={pageSize}
{...rowsState}
onPageChange={(page) => setRowsState((prev) => ({ ...prev, page }))}
onPageSizeChange={(pageSize) => setRowsState((prev) => ({ ...prev, pageSize }))}
rowsPerPageOptions={[5, 10, 50, 100]}
pagination
/>
<NavLink to="/writeBoard">
<ListItemText>글쓰기</ListItemText>
</NavLink>
</Box>
);
};
export default BoardList;

@ -0,0 +1,74 @@
import { useEffect, useState } from 'react';
// material-ui
import { Button, Divider, FormControlLabel, Grid, InputAdornment, Radio, RadioGroup, TextField } from '@mui/material';
// assets
import LinkTwoToneIcon from '@mui/icons-material/LinkTwoTone';
import LockTwoToneIcon from '@mui/icons-material/LockTwoTone';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import InputLabel from 'ui-component/extended/Form/InputLabel';
import opstBizService from '../../../apis/OpstBizService';
const PublicBoard = () => {
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 10,
rows: []
// loading: false
});
const columns = [
{ headerName: '게시판코드', field: 'ciCode' },
{ headerName: '글번호', field: 'ciContentno' },
{ headerName: '제목', field: 'ciTitle', editable: true },
{ headerName: '사용자ID', field: 'ciId' },
{ headerName: '사용자 비번', field: 'ciPwd' },
{ headerName: '사용자 이름', field: 'ciName' },
{ headerName: '등록일', field: 'ciNalja' },
{ headerName: '등록시간', field: 'ciTime' },
{ headerName: '조회수', field: 'ciHit' },
{ headerName: 'ref', field: 'ciRef' },
{ headerName: 'step', field: 'ciStep' },
{ headerName: 'level', field: 'ciRevel' },
{ headerName: '비번', field: 'ciPass' },
{ headerName: 'email', field: 'ciEmail' },
{ headerName: '내용', field: 'ciContents' },
{ headerName: 'IP', field: `ciIp` }
];
useEffect(() => {
const params = {
page: rowsState.page,
size: rowsState.pageSize
};
opstBizService.getBoardList(params).then((response) => {
console.log(response);
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
}, [rowsState.page, rowsState.pageSize]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
<MainCard>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiGridList columns={columns} rowsState={rowsState} totalCount={totalCount} setRowsState={setRowsState} />
</MainCard>
);
};
export default PublicBoard;

@ -1,211 +0,0 @@
import { useEffect, useState } from 'react';
// material-ui
import { Button, Divider, FormControlLabel, Grid, InputAdornment, Radio, RadioGroup, TextField } from '@mui/material';
// assets
import LinkTwoToneIcon from '@mui/icons-material/LinkTwoTone';
import LockTwoToneIcon from '@mui/icons-material/LockTwoTone';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import InputLabel from 'ui-component/extended/Form/InputLabel';
import boardService from '../../../apis/BoardService';
const Index = () => {
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 5,
rows: []
// loading: false
});
const columns = [
{ headerName: '게시판코드', field: 'ciCode' },
{ headerName: '글번호', field: 'ciContentno' },
{ headerName: '제목', field: 'ciTitle', editable: true },
{ headerName: '사용자ID', field: 'ciId' },
{ headerName: '사용자 비번', field: 'ciPwd' },
{ headerName: '사용자 이름', field: 'ciName' },
{ headerName: '등록일', field: 'ciNalja' },
{ headerName: '등록시간', field: 'ciTime' },
{ headerName: '조회수', field: 'ciHit' },
{ headerName: 'ref', field: 'ciRef' },
{ headerName: 'step', field: 'ciStep' },
{ headerName: 'level', field: 'ciRevel' },
{ headerName: '비번', field: 'ciPass' },
{
headerName: 'email',
field: 'ciEmail',
renderCell: (params) => (
<strong>
{params.value}
<Button variant="contained" color="primary" size="small">
Open
</Button>
</strong>
)
},
{ headerName: '내용', field: 'ciContents' },
{
headerName: 'IP',
field: `ciIp`
}
];
useEffect(() => {
const params = {
page: rowsState.page + 1,
size: rowsState.pageSize
};
boardService.getBoardList(params).then((response) => {
console.log(response);
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
}, [rowsState.page, rowsState.pageSize]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
<MainCard>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Name :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField fullWidth placeholder="Enter full name" />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Email :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField fullWidth placeholder="Enter email" />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Password :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField
type="password"
fullWidth
placeholder="Enter Password"
InputProps={{
startAdornment: (
<InputAdornment position="start">
<LockTwoToneIcon />
</InputAdornment>
)
}}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Contact :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField fullWidth placeholder="Enter contact number" />
{/* <FormHelperText>Please enter your contact</FormHelperText> */}
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Profile URL :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField
fullWidth
placeholder="Please enter your Profile URL"
InputProps={{
endAdornment: (
<InputAdornment position="end">
<LinkTwoToneIcon />
</InputAdornment>
)
}}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Pincode :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField fullWidth placeholder="Enter your postcode" />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
Address :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<TextField fullWidth placeholder="Enter your address" />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4}>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} sm={3} lg={4} sx={{ pt: { xs: 2, sm: '0 !important' } }}>
<InputLabel horizontal sx={{ textAlign: { xs: 'left', sm: 'right' } }}>
User Type :
</InputLabel>
</Grid>
<Grid item xs={12} sm={9} lg={8}>
<RadioGroup aria-label="gender" name="controlled-radio-buttons-group">
<FormControlLabel value="female" control={<Radio />} label="Administrator" />
<FormControlLabel value="male" control={<Radio />} label="Author" />
</RadioGroup>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiGridList columns={columns} rowsState={rowsState} totalCount={totalCount} setRowsState={setRowsState} />
</MainCard>
);
};
export default Index;

@ -0,0 +1,131 @@
import { useEffect, useState } from 'react';
import _ from 'lodash';
// material-ui
import {
Button,
Divider,
FormControl,
FormControlLabel,
FormLabel,
Grid,
InputAdornment,
MenuItem,
OutlinedInput,
Radio,
RadioGroup,
Select
} from '@mui/material';
import MuiTooltip from '@mui/material/Tooltip';
// assets
import { IconSearch } from '@tabler/icons';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import { useTheme } from '@mui/material/styles';
import AnimateButton from '../../../ui-component/extended/AnimateButton';
import InputLabel from '../../../ui-component/extended/Form/InputLabel';
const ParkingRegister = () => {
const theme = useTheme();
const [category, setCategory] = useState('N');
const [searchTxt, setSearchTxt] = useState('');
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 10,
rows: []
// loading: false
});
const [spacing, setSpacing] = useState(2);
const columns = [
{ headerName: '심의차수', field: 'ciCode' },
{ headerName: '심사건수', field: 'ciContentno' },
{ headerName: '심사기간', field: 'ciTitle', editable: true },
{ headerName: '심사마감일시', field: 'ciId' },
{ headerName: '상태', field: 'ciPwd' },
{
headerName: '삭제하기',
field: 'ciEmail',
renderCell: (params) => (
<>
{params.value}
<Button
variant="contained"
color="primary"
size="small"
sx={{ background: theme.palette.error.main, '&:hover': { background: theme.palette.error.dark } }}
>
삭제
</Button>
</>
)
}
];
const handleSearch = async (event) => {};
useEffect(() => {
const params = {
page: rowsState.page,
size: rowsState.pageSize
};
}, [rowsState.page, rowsState.pageSize]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
<MainCard>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} lg={6}>
<Grid container spacing={1}>
<Grid item>
<FormControl component="fieldset">
<FormLabel component="legend" required>
자료등록여부
</FormLabel>
<RadioGroup
row
aria-label="category"
name="row-radio-buttons-group"
value={category}
onChange={(e) => setCategory(e.target.value)}
>
<FormControlLabel value="N" control={<Radio />} label="미등록" />
<FormControlLabel value="Y" control={<Radio />} label="등록" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item>
<OutlinedInput placeholder="접수번호" onKeyDown={handleSearch} size="small" autoFocus />
&nbsp;-&nbsp;
<OutlinedInput placeholder="접수번호" onKeyDown={handleSearch} size="small" />
{/* <TextField fullWidth label="Last Name" defaultValue="Schorl" /> */}
</Grid>
<Grid item>
<AnimateButton>
<Button variant="contained" color="primary" size="small" onClick={handleSearch}>
검색
</Button>
</AnimateButton>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiGridList columns={columns} rowsState={rowsState} totalCount={totalCount} setRowsState={setRowsState} />
</MainCard>
);
};
export default ParkingRegister;

@ -0,0 +1,180 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import _ from 'lodash';
import format from 'date-fns/format';
import getYear from 'date-fns/getYear';
// material-ui
import { GridActionsCellItem } from '@mui/x-data-grid';
import { Divider, Grid, InputAdornment, Link, MenuItem, OutlinedInput, Select } from '@mui/material';
import DeleteIcon from '@mui/icons-material/Delete';
import MuiTooltip from '@mui/material/Tooltip';
// assets
import { IconSearch } from '@tabler/icons';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import opstBizService from 'apis/OpstBizService';
import { useTheme } from '@mui/material/styles';
import xitCmm from 'commons/XitCmm';
const ParkingReview = () => {
const isInit = useRef(true);
const theme = useTheme();
const year = getYear(new Date());
console.log(typeof year);
const years = _.range(year, year - 14, -1);
const [selectedYear, setSelectedYear] = useState(year);
const [searchTxt, setSearchTxt] = useState('');
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 10,
rows: []
// loading: false
});
const [rows, setRows] = useState();
const removeSimsa = useCallback(
(row) => () => {
// setTimeout(() => {
// setRowsState((prevRows) => prevRows.filter((row) => row.rowId !== rowId));
// });
xitCmm.alertParam(`삭제대상<br>${JSON.stringify(row)}`);
console.log(row);
},
[]
);
/*
ms_maincode : '민원코드'
ms_seq : '접수번호'
ms_carnum : '차량번호'
ms_year : '년도'
ms_chasu : '차수'
ms_sdate : '심사시작일시'
ms_startsi : '심사시작시간'
ms_edate : '심사종료일시'
ms_cdate : '심사마감일시'
ms_closesi : '심사마감시간'
ms_wdate : '단속일자'
ms_pos : '단속장소'
ms_result : '결과코드'
ms_jbtime : '단속시간'
*/
const columns = [
// { headerName: 'rowId', field: 'rowId' },
{ headerName: '심의차수', field: 'msChasu' },
{ headerName: '심사건수', field: 'cnt' },
{
headerName: '심사기간',
field: 'msDate',
minWidth: 180,
description: 'dddddd',
valueGetter: (params) => `${params.row.msSdate} ~ ${params.row.msEdate}`,
renderCell: (params) => (
<Link underline="hover" href="/board" target="_blank">
{params.value}
</Link>
)
},
{
headerName: '심사마감일시',
field: 'msCdate',
type: 'dateTime',
minWidth: 125,
valueGetter: (params) => `${params.row.msCdate} ${params.row.msClosesi}`
},
{ headerName: '상태', field: 'msResult', renderCell: (params) => <>{params.value === '1' ? '진행중' : '심사완료'}</> },
{
headerName: '삭제',
field: 'actions',
type: 'actions',
width: 80,
getActions: (params) => [<GridActionsCellItem icon={<DeleteIcon />} label="Delete" onClick={removeSimsa(params.row)} />]
}
];
const search = () => {
const params = {
page: rowsState.page,
size: rowsState.pageSize
};
opstBizService.getSimsa680GroupList({ ...params, msYear: selectedYear, msChasu: searchTxt }).then((response) => {
console.log(response);
if (response && response.data) {
setTotalCount(response.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
};
const handleSearch = async (event) => {
if (!selectedYear) return;
if (event.type === 'keydown' && event.key === 'Enter') {
const newString = event?.target.value;
setSearchTxt(newString);
search();
}
};
useEffect(() => {
if (isInit.current) {
isInit.current = false;
return;
}
search();
}, [rowsState.page, rowsState.pageSize, selectedYear, searchTxt]);
return (
<MainCard>
<Grid container spacing={2} alignItems="center">
<Grid item xs={12} lg={6}>
<Grid container spacing={1}>
<Grid item>
<MuiTooltip title="의견진술 심의 년도">
<Select id="reviewYear" name="reviewYear" defaultValue={year} onChange={(e) => setSelectedYear(e.target.value)}>
{years.map((year, idx) => (
<MenuItem key={idx} value={year}>
{year}
</MenuItem>
))}
</Select>
</MuiTooltip>
</Grid>
<Grid item>
<OutlinedInput
placeholder="심의차수"
onKeyDown={handleSearch}
size="small"
autoFocus
endAdornment={
<InputAdornment position="end">
<IconSearch stroke={1.5} size="1rem" />
</InputAdornment>
}
/>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<Grid item xs={12}>
<Divider />
</Grid>
<MuiGridList columns={columns} rowsState={rowsState} totalCount={totalCount} setRowsState={setRowsState} />
</MainCard>
);
};
export default ParkingReview;
Loading…
Cancel
Save