feat: init

main
Lim Jonguk 3 years ago
parent 4f62ca2f21
commit 969ad21b45

@ -75,7 +75,9 @@
"remark-gfm": "^3.0.1", "remark-gfm": "^3.0.1",
"slick-carousel": "^1.8.1", "slick-carousel": "^1.8.1",
"stylis-plugin-rtl": "^2.1.1", "stylis-plugin-rtl": "^2.1.1",
"typescript": "^4.5.5", "sweetalert2": "^11.4.4",
"sweetalert2-react-content": "^4.2.0",
"typescript": "4.4.4",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"web-vitals": "^2.1.4", "web-vitals": "^2.1.4",
"yup": "^0.32.11" "yup": "^0.32.11"

@ -9,10 +9,7 @@ import Snackbar from 'ui-component/extended/Snackbar';
import ThemeCustomization from 'themes'; import ThemeCustomization from 'themes';
// auth provider // auth provider
import { FirebaseProvider as AuthProvider } from 'contexts/FirebaseContext'; import { JWTProvider as AuthProvider } from 'contexts/JWTContext';
// import { AWSCognitoProvider as AuthProvider } from 'contexts/AWSCognitoContext';
// import { JWTProvider as AuthProvider } from 'contexts/JWTContext';
// import { Auth0Provider as AuthProvider } from 'contexts/Auth0Context';
// ==============================|| APP ||============================== // // ==============================|| APP ||============================== //

@ -0,0 +1,133 @@
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;

@ -0,0 +1,16 @@
import { IUser } from 'types/auth';
import reqApi from './ApiService';
import { LOGIN_URL } from 'commons/ApiUrl';
import { IApiResponse } from 'types/api';
class AuthService {
login = (user) => reqApi.post(LOGIN_URL, user);
// getCurrentUser = async () => {
// if (!localStorage.getItem(ACCESS_TOKEN_NAME)) {
// return Promise.reject('No access token set.');
// }
// };
}
export default new AuthService();

@ -0,0 +1,14 @@
import { BOARD_LIST_URL } from 'commons/ApiUrl';
import axios from 'utils/axios';
import { IBoard, IParam } from 'types/Data';
import { IApiPageResponse } from 'types/api';
import { AxiosResponse } from 'axios';
class BoardService {
getBoardList = (params) =>
axios.get(BOARD_LIST_URL, { params }).then((r) => {
console.log(r);
return r;
});
}
export default new BoardService();

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

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19 9H9C7.89543 9 7 9.89543 7 11V17C7 18.1046 7.89543 19 9 19H19C20.1046 19 21 18.1046 21 17V11C21 9.89543 20.1046 9 19 9Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M14 16C15.1046 16 16 15.1046 16 14C16 12.8954 15.1046 12 14 12C12.8954 12 12 12.8954 12 14C12 15.1046 12.8954 16 14 16Z" fill="#90CAF9"/>
<path d="M17 9V7C17 6.46957 16.7893 5.96086 16.4142 5.58579C16.0391 5.21071 15.5304 5 15 5H5C4.46957 5 3.96086 5.21071 3.58579 5.58579C3.21071 5.96086 3 6.46957 3 7V13C3 13.5304 3.21071 14.0391 3.58579 14.4142C3.96086 14.7893 4.46957 15 5 15H7" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 794 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 817 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 809 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

@ -0,0 +1,5 @@
// const URL = process.env.NODE_ENV !== 'development' ? process.env.REACT_APP_API_URL : ''
// console.log(process.env.NODE_ENV)
export const LOGIN_URL = '/api/v1/ctgy/account/login';
export const CMM_CODE_LIST_URL = '/api/v1/biz/cmm/combo';
export const BOARD_LIST_URL = '/api/v1/ctgy/board';

@ -0,0 +1,266 @@
import Swal from 'sweetalert2';
import React, { Dispatch, SetStateAction, useCallback, useState } from 'react';
// import axios, {AxiosResponse} from 'axios';
// import {IApiResponse} from 'types/ApiModel';
// import Alert from 'react-s-alert';
const XitCmm = {
// const request: (options: object) => {
// const headers = new Headers({
// 'Content-Type': 'application/json',
// })
//
// if(localStorage.getItem(ACCESS_TOKEN)) {
// headers.append('Authorization', 'Bearer ' + localStorage.getItem(ACCESS_TOKEN))
// }
//
// const defaults = {headers: headers};
// options = Object.assign({}, defaults, options);
//
// return fetch(options.url, options)
// .then(response =>
// response.json().then(json => {
// if(!response.ok) {
// return Promise.reject(json);
// }
// return json;
// })
// );
// },
// requestApi: async (
// methodType: string,
// url: string,
// params: any,
// headers: any
// ): Promise<AxiosResponse<IApiResponse>> => {
// console.log(`process.env.NODE_ENV`, process.env.NODE_ENV);
// console.table(params);
//
// headers = Object.assign({'Content-Type': 'application/json;charset=UTF-8'}, headers); //, "Authorization": session.get('token')};
// let options: any = {
// url: process.env.NODE_ENV === 'development' ? url : process.env.REACT_APP_API + url,
// method: methodType,
// headers: headers
// };
// // get 요청은 body 없이 call
// if (methodType.toLocaleLowerCase() === 'get') options = {...options, params};
// else options = {...options, data: params};
//
// // 요청 처리
// let res: IApiResponse;
// try {
// res = await axios(options); //{...config, ...options});//.then(res => {
// } catch (e) {
// console.log(`@@@@@@@@@@@ requestApi EXCEPTION @@@@@@@@@@@@@`);
// Alert.error(`<p>${e}</p>`);
// } finally {
// }
// if (res !== undefined && res.success && res.data.success) {
// console.log(JSON.stringify(res.data));
// Alert.success(`처리되었습니다`);
// } else {
// console.log(`@@@@@@@@@@@ requestApi ERROR @@@@@@@@@@@@@`);
// let code = res.data.code != null ? `[${res.data.code}]` : '';
// await SweetAlert.fire({
// title: `Inpix Administrator`,
// html: `<p>${res.data.message} ${code}</p>`,
// //footer: 'Copyright 2018',
// timer: 3000
// });
// }
// return res.data;
// },
/**
* validation check error message
* @param message
*/
alertParam: (message: string) => {
Swal.fire({
icon: 'warning',
html: message,
// imageUrl:
timer: 3000
}).then((r) => {});
return false;
},
/**
* API Error message
* @param message
*/
alertError: (message: string) => {
Swal.fire({
icon: 'error',
title: 'API error',
html: message,
// imageUrl:
timer: 3000
}).then((r) => {});
return false;
},
/**
*
* @return [value: string, onchange: (e: ChangeEvent<HTMLInputElement>) => void]
* @param initalValue
* @param validator
*/
useInput<T>(
initalValue: T,
validator?: (value: string) => boolean
): [T, Dispatch<SetStateAction<T>>, (e?: React.ChangeEvent<HTMLInputElement>) => void] {
const [value, setValue] = useState<typeof initalValue>(initalValue);
const changer = useCallback(
(e) => {
const v = e.target.value;
if (validator === undefined || validator(v)) {
setValue(v);
}
},
[validator]
);
return [value, setValue, changer];
},
// ag-grid 페이지 변경
onPageSizeChanged() {
/*
Page Size:
<Select
defaultInputValue={'2'}
defaultvalue={selectedPageSize}
onChange={onPageSizeChanged}
options={options}
/>
*/
// var value = (document.getElementById('page-size') as HTMLInputElement).value;
// gridOptions.api!.paginationSetPageSize(Number(value));
// onPageSizeChnged: useCallback((gridRef: AgGridReact, pageSize: number) => {
// gridRef.current.api.paginationSetPageSize(Number(value));
// }, []),
// MutableRefObject
// onPageSizeChnged: useCallback((gridRef: RefObject<AgGridReact>, pageSizeStr: string) => {
// gridRef.current.api.paginationSetPageSize(Number(pageSizeStr));
// }, []),
},
/**
* form 데이타를 JSON 으로 return
* @param frm
* @returns {string}
*/
// getJsonFromForm: (frm: HTMLFormElement): string => {
// let formData = new FormData(frm),
// result = {};
//
// // Iteration protocols : for of을 사용하여야만 한다
// // Iteration 객체의 특징
// for (let entry of formData.entries()) {
// result[entry[0]] = entry[1];
// }
// formData.forEach((v, k) => (result[k] = v));
// return JSON.stringify(result);
// },
/**
* 현재 URL에서 파라메터 GET
* @param name
* @returns string
*/
getParameterByName: (name: string) => {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
const regex = new RegExp(`[\\?&]${name}=([^&#]*)`);
const results = regex.exec(window.location.search);
return results == null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
},
/**
* 현재 URL의 파라메터를 JSON으로 return
* @returns {}
*/
// getQuery: () => {
// let url: string = document.location.href;
// let qs: Array<any> = url.substring(url.indexOf('?') + 1).split('&');
// let result: Object = {}
// for (let i = 0; i < qs.length; i++) {
// qs[i] = qs[i].split('=');
// result[qs[i][0]] = decodeURIComponent(qs[i][1]);
// }
// return result;
// },
/**
* 현재 URL에서 파라메터 GET
* @param name
* @returns {string|number|null}
*/
urlParam: (name: string) => {
// var url = decodeURIComponent(window.location.href);
const results = new RegExp(`[?&]${name}=([^&#]*)`).exec(window.location.href);
if (results == null) {
return null;
}
return results[1] || 0;
},
/**
* 현재 URL에서 파라메터 GET
* @param paramName
* @returns {string}
*/
getParameters: (paramName: string) => {
// 리턴값을 위한 변수 선언
let returnValue;
// 현재 URL 가져오기
const url = window.location.href;
// get 파라미터 값을 가져올 수 있는 ? 를 기점으로 slice 한 후 split 으로 나눔
const parameters = url.slice(url.indexOf('?') + 1, url.length).split('&');
// 나누어진 값의 비교를 통해 paramName 으로 요청된 데이터의 값만 return
// eslint-disable-next-line no-plusplus
for (let i = 0; i < parameters.length; i++) {
const varName = parameters[i].split('=')[0];
if (varName.toUpperCase() === paramName.toUpperCase()) {
returnValue = parameters[i].split('=')[1];
return decodeURIComponent(returnValue);
}
}
return returnValue;
},
/**
*
* @param name - The name of the cookie to be set
* @param value - The value of the cookie
* @param expireDays
*/
setCookie: (name: string, value: string, expireDays: number = 1) => {
const todayDate = new Date();
todayDate.setTime(todayDate.getTime() + expireDays * 24 * 60 * 60 * 1000);
const expired = todayDate.toISOString(); // .toGMTString();
document.cookie = `${name} = ${escape(value)}; expires = ${expired}; path=/`;
},
/**
*
* @param name
*/
getCookie: (name: string) => document.cookie.split(';').some((c) => c.trim().startsWith(`${name}=`)),
/**
*
* @param name - The name of the cookie to be set
* @param value - The value of the cookie
*/
removeCookie: (name: string, value?: string) => {
if (!value) document.cookie = `${name}=Max-Age=-99999999;`;
else document.cookie = `${name}=${encodeURIComponent(value)}${{ expires: 'Sun, 01-May-2019 14:00:00 UTC' }}`;
}
};
export default XitCmm;

@ -3,39 +3,20 @@ export const JWT_API = {
timeout: '1 days' timeout: '1 days'
}; };
export const FIREBASE_API = {
apiKey: 'AIzaSyBernKzdSojh_vWXBHt0aRhf5SC9VLChbM',
authDomain: 'berry-material-react.firebaseapp.com',
projectId: 'berry-material-react',
storageBucket: 'berry-material-react.appspot.com',
messagingSenderId: '901111229354',
appId: '1:901111229354:web:a5ae5aa95486297d69d9d3',
measurementId: 'G-MGJHSL8XW3'
};
export const AUTH0_API = {
client_id: '7T4IlWis4DKHSbG8JAye4Ipk0rvXkH9V',
domain: 'dev-w0-vxep3.us.auth0.com'
};
export const AWS_API = {
poolId: 'us-east-1_AOfOTXLvD',
appClientId: '3eau2osduslvb7vks3vsh9t7b0'
};
// basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead, // basename: only at build time to set, and Don't add '/' at end off BASENAME for breadcrumbs, also Don't put only '/' use blank('') instead,
// like '/berry-material-react/react/default' // like '/berry-material-react/react/default'
export const BASE_PATH = ''; export const BASE_PATH = '';
export const DASHBOARD_PATH = '/sample-page'; export const DASHBOARD_PATH = '/dashboard/default';
const config = { const config = {
fontFamily: `'Roboto', sans-serif`, fontFamily: `'Roboto', sans-serif`,
borderRadius: 8, borderRadius: 4,
outlinedFilled: true, outlinedFilled: true,
navType: 'light', // light, dark navType: 'light', // light, dark
presetColor: 'default', // default, theme1, theme2, theme3, theme4, theme5, theme6 presetColor: 'default', // default, theme1, theme2, theme3, theme4, theme5, theme6
locale: 'en', // 'en' - English, 'fr' - French, 'ro' - Romanian, 'zh' - Chinese locale: 'ko', // 'en' - English, 'fr' - French, 'ro' - Romanian, 'zh' - Chinese
rtlLayout: false, rtlLayout: false,
container: false container: false
}; };

@ -1,153 +0,0 @@
import PropTypes from 'prop-types';
import React, { createContext, useEffect, useReducer } from 'react';
// third-party
import { CognitoUser, CognitoUserPool, CognitoUserAttribute, AuthenticationDetails } from 'amazon-cognito-identity-js';
// reducer - state management
import { LOGIN, LOGOUT } from 'store/actions';
import accountReducer from 'store/accountReducer';
// project imports
import Loader from 'ui-component/Loader';
import { AWS_API } from 'config';
// constant
const initialState = {
isLoggedIn: false,
isInitialized: false,
user: null
};
export const userPool = new CognitoUserPool({
UserPoolId: AWS_API.poolId || '',
ClientId: AWS_API.appClientId || ''
});
const setSession = (serviceToken? | null) => {
if (serviceToken) {
localStorage.setItem('serviceToken', serviceToken);
} else {
localStorage.removeItem('serviceToken');
}
};
// ==============================|| AWS Cognito CONTEXT & PROVIDER ||============================== //
const AWSCognitoContext = createContext(null);
export const AWSCognitoProvider = ({ children }) => {
const [state, dispatch] = useReducer(accountReducer, initialState);
useEffect(() => {
const init = async () => {
try {
const serviceToken = window.localStorage.getItem('serviceToken');
if (serviceToken) {
setSession(serviceToken);
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
name: 'Betty'
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
} catch (err) {
console.error(err);
dispatch({
type: LOGOUT
});
}
};
init();
}, []);
const login = async (email, password) => {
const usr = new CognitoUser({
Username: email,
Pool: userPool
});
const authData = new AuthenticationDetails({
Username: email,
Password: password
});
usr.authenticateUser(authData, {
onSuccess: (session) => {
setSession(session.getAccessToken().getJwtToken());
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
email: authData.getUsername(),
name: 'John Doe'
}
}
});
},
onFailure: (_err) => {},
newPasswordRequired: (userAttributes, requiredAttributes) => {
// // User was signed up by an admin and must provide new
// // password and required attributes, if any, to complete
// // authentication.
// // the api doesn't accept this field back
// delete userAttributes.email_verified;
// // unsure about this field, but I don't send this back
// delete userAttributes.phone_number_verified;
// // Get these details and call
// usr.completeNewPasswordChallenge(password, userAttributes, requiredAttributes);
}
});
};
const register = (email, password, firstName, lastName) =>
new Promise((success, rej) => {
userPool.signUp(
email,
password,
[
new CognitoUserAttribute({ Name: 'email', Value: email }),
new CognitoUserAttribute({ Name: 'name', Value: `${firstName} ${lastName}` })
],
[],
async (err, result) => {
if (err) {
rej(err);
return;
}
success(result);
}
);
});
const logout = () => {
const loggedInUser = userPool.getCurrentUser();
if (loggedInUser) {
setSession(null);
loggedInUser.signOut();
dispatch({ type: LOGOUT });
}
};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return <AWSCognitoContext.Provider value={{ ...state, login, logout, register }}>{children}</AWSCognitoContext.Provider>;
};
AWSCognitoProvider.propTypes = {
children: PropTypes.node
};
export default AWSCognitoContext;

@ -1,115 +0,0 @@
import PropTypes from 'prop-types';
import { createContext, useEffect, useReducer } from 'react';
// third-party
import { Auth0Client } from '@auth0/auth0-spa-js';
// reducer - state management
import { LOGIN, LOGOUT } from 'store/actions';
import accountReducer from 'store/accountReducer';
// project imports
import Loader from 'ui-component/Loader';
import { AUTH0_API } from 'config';
// constant
let auth0Client;
const initialState = {
isLoggedIn: false,
isInitialized: false,
user: null
};
// ==============================|| AUTH0 CONTEXT & PROVIDER ||============================== //
const Auth0Context = createContext(null);
export const Auth0Provider = ({ children }) => {
const [state, dispatch] = useReducer(accountReducer, initialState);
useEffect(() => {
const init = async () => {
try {
auth0Client = new Auth0Client({
redirect_uri: window.location.origin,
...AUTH0_API
});
await auth0Client.checkSession();
const isLoggedIn = await auth0Client.isAuthenticated();
if (isLoggedIn) {
const user = await auth0Client.getUser();
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user?.sub,
email: user?.email
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
} catch (err) {
dispatch({
type: LOGOUT
});
}
};
init();
}, []);
const login = async (options) => {
await auth0Client.loginWithPopup(options);
const isLoggedIn = await auth0Client.isAuthenticated();
if (isLoggedIn) {
const user = await auth0Client.getUser();
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user?.sub,
avatar: user?.picture,
email: user?.email,
name: user?.name,
tier: 'Premium'
}
}
});
}
};
const logout = () => {
auth0Client.logout();
dispatch({
type: LOGOUT
});
};
const resetPassword = (email) => {};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return <Auth0Context.Provider value={{ ...state, login, logout, resetPassword, updateProfile }}>{children}</Auth0Context.Provider>;
};
Auth0Provider.propTypes = {
children: PropTypes.node
};
export default Auth0Context;

@ -1,103 +0,0 @@
import PropTypes from 'prop-types';
import { createContext, useEffect, useReducer } from 'react';
// third-party
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
// action - state management
import { LOGIN, LOGOUT } from 'store/actions';
import accountReducer from 'store/accountReducer';
// project imports
import Loader from 'ui-component/Loader';
import { FIREBASE_API } from 'config';
// firebase initialize
if (!firebase.apps.length) {
firebase.initializeApp(FIREBASE_API);
}
// const
const initialState = {
isLoggedIn: false,
isInitialized: false,
user: null
};
// ==============================|| FIREBASE CONTEXT & PROVIDER ||============================== //
const FirebaseContext = createContext(null);
export const FirebaseProvider = ({ children }) => {
const [state, dispatch] = useReducer(accountReducer, initialState);
useEffect(
() =>
firebase.auth().onAuthStateChanged((user) => {
if (user) {
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user: {
id: user.uid,
email: user.email,
name: user.displayName || 'John Doe'
}
}
});
} else {
dispatch({
type: LOGOUT
});
}
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[dispatch]
);
const firebaseEmailPasswordSignIn = (email, password) => firebase.auth().signInWithEmailAndPassword(email, password);
const firebaseGoogleSignIn = () => {
const provider = new firebase.auth.GoogleAuthProvider();
return firebase.auth().signInWithPopup(provider);
};
const firebaseRegister = async (email, password) => firebase.auth().createUserWithEmailAndPassword(email, password);
const logout = () => firebase.auth().signOut();
const resetPassword = async (email) => {
await firebase.auth().sendPasswordResetEmail(email);
};
const updateProfile = () => {};
if (state.isInitialized !== undefined && !state.isInitialized) {
return <Loader />;
}
return (
<FirebaseContext.Provider
value={{
...state,
firebaseRegister,
firebaseEmailPasswordSignIn,
login: () => {},
firebaseGoogleSignIn,
logout,
resetPassword,
updateProfile
}}
>
{children}
</FirebaseContext.Provider>
);
};
FirebaseProvider.propTypes = {
children: PropTypes.node
};
export default FirebaseContext;

@ -22,27 +22,28 @@ const initialState = {
user: null user: null
}; };
const verifyToken = (serviceToken) => { const verifyToken = (accessToken) => {
if (!serviceToken) { if (!accessToken) {
return false; return false;
} }
const decoded = jwtDecode(serviceToken); const decoded = jwtDecode(accessToken);
/** /**
* Property 'exp' does not exist on type '<T = unknown>(token, options?: JwtDecodeOptions | undefined) => T'. * Property 'exp' does not exist on type '<T = unknown>(token, options?: JwtDecodeOptions | undefined) => T'.
*/ */
return decoded.exp > Date.now() / 1000; return decoded.exp > Date.now() / 1000;
}; };
const setSession = (serviceToken) => { const setLocalStorage = (tokenName, token) => {
if (serviceToken) { if (token) {
localStorage.setItem('serviceToken', serviceToken); localStorage.setItem(tokenName === ACCESS_TOKEN_NAME ? ACCESS_TOKEN_NAME : REFRESH_TOKEN_NAME, token);
axios.defaults.headers.common.Authorization = `Bearer ${serviceToken}`; if (tokenName === ACCESS_TOKEN_NAME) axios.defaults.headers.common.Authorization = `Bearer ${token}`;
} else { } else {
localStorage.removeItem('serviceToken'); localStorage.removeItem(tokenName === ACCESS_TOKEN_NAME ? ACCESS_TOKEN_NAME : REFRESH_TOKEN_NAME);
delete axios.defaults.headers.common.Authorization; if (tokenName === ACCESS_TOKEN_NAME) delete axios.defaults.headers.common.Authorization;
} }
}; };
// ==============================|| JWT CONTEXT & PROVIDER ||============================== // // ==============================|| JWT CONTEXT & PROVIDER ||============================== //
const JWTContext = createContext(null); const JWTContext = createContext(null);
@ -52,16 +53,22 @@ export const JWTProvider = ({ children }) => {
useEffect(() => { useEffect(() => {
const init = async () => { const init = async () => {
try { try {
const serviceToken = window.localStorage.getItem('serviceToken'); const accessToken = window.localStorage.getItem(ACCESS_TOKEN_NAME);
if (serviceToken && verifyToken(serviceToken)) {
setSession(serviceToken); // TODO: verifyToken ??
const response = await axios.get('/api/account/me'); if (accessToken && verifyToken(accessToken)) {
const { user } = response.data; setLocalStorage(ACCESS_TOKEN_NAME, accessToken);
const response = await axios.get('/api/v1/ctgy/user');
const { userid, email, name } = response.data;
dispatch({ dispatch({
type: LOGIN, type: LOGIN,
payload: { payload: {
isLoggedIn: true, isLoggedIn: true,
user user: {
id: userid,
email,
name
}
} }
}); });
} else { } else {
@ -80,17 +87,26 @@ export const JWTProvider = ({ children }) => {
init(); init();
}, []); }, []);
const login = async (email, password) => { const login = async (email, passwd) => {
const response = await axios.post('/api/account/login', { email, password }); const response = await axios.post(LOGIN_URL, { providerType: ProviderType.LOCAL, userid, passwd });
const { serviceToken, user } = response.data; // console.log(response);
setSession(serviceToken); if (response && response.data) {
dispatch({ const { accessToken, refreshToken, user } = response.data;
type: LOGIN, setLocalStorage(ACCESS_TOKEN_NAME, accessToken);
payload: { if (refreshToken) setLocalStorage(REFRESH_TOKEN_NAME, refreshToken);
isLoggedIn: true,
user // if (res.data.accessToken) localStorage.setItem(ACCESS_TOKEN_NAME, `${res.data.grantType} ${res.data.accessToken}`);
} // if (res.data.refreshToken) XitCmm.setCookie(REFRESH_TOKEN_NAME, res.data.refreshToken);
}); // if (res.data.refreshToken) XitCmm.setCookie(REFRESH_TOKEN_NAME, res.data.refreshToken);
dispatch({
type: LOGIN,
payload: {
isLoggedIn: true,
user
}
});
}
}; };
const register = async (email, password, firstName, lastName) => { const register = async (email, password, firstName, lastName) => {

@ -1,10 +1,7 @@
import { useContext } from 'react'; import { useContext } from 'react';
// auth provider // auth provider
import AuthContext from 'contexts/FirebaseContext';
// import AuthContext from 'contexts/Auth0Context';
// import AuthContext from 'contexts/JWTContext'; // import AuthContext from 'contexts/JWTContext';
// import AuthContext from 'contexts/AWSCognitoContext';
// ==============================|| AUTH HOOKS ||============================== // // ==============================|| AUTH HOOKS ||============================== //

@ -141,6 +141,18 @@ const LocalizationSection = () => {
} }
}} }}
> >
<ListItemButton selected={language === 'ko'} onClick={(event) => handleListItemClick(event, 'ko')}>
<ListItemText
primary={
<Grid container>
<Typography color="textPrimary">Korean</Typography>
<Typography variant="caption" color="textSecondary" sx={{ ml: '8px' }}>
(Korea)
</Typography>
</Grid>
}
/>
</ListItemButton>
<ListItemButton selected={language === 'en'} onClick={(event) => handleListItemClick(event, 'en')}> <ListItemButton selected={language === 'en'} onClick={(event) => handleListItemClick(event, 'en')}>
<ListItemText <ListItemText
primary={ primary={

@ -0,0 +1,39 @@
// third-party
import { FormattedMessage } from 'react-intl';
// assets
import { IconDashboard, IconDeviceAnalytics } from '@tabler/icons';
// constant
const icons = {
IconDashboard,
IconDeviceAnalytics
};
// ==============================|| DASHBOARD MENU ITEMS ||============================== //
const dashboard = {
id: 'dashboard',
title: <FormattedMessage id="dashboard" />,
type: 'group',
children: [
{
id: 'default',
title: <FormattedMessage id="default" />,
type: 'item',
url: '/dashboard/default',
icon: icons.IconDashboard,
breadcrumbs: true
},
{
id: 'analytics',
title: <FormattedMessage id="analytics" />,
type: 'item',
url: '/dashboard/analytics',
icon: icons.IconDeviceAnalytics,
breadcrumbs: true
}
]
};
export default dashboard;

@ -1,9 +1,11 @@
import other from './other'; import other from './other';
import opst from './opst';
import dashboard from './dashboard';
// ==============================|| MENU ITEMS ||============================== // // ==============================|| MENU ITEMS ||============================== //
const menuItems = { const menuItems = {
items: [other] items: [dashboard, opst, other]
}; };
export default menuItems; export default menuItems;

@ -0,0 +1,195 @@
// third-party
import { FormattedMessage } from 'react-intl';
// assets
import {
IconKey,
IconReceipt2,
IconBug,
IconBellRinging,
IconPhoneCall,
IconQuestionMark,
IconShieldLock,
IconUserCheck,
IconMessage,
IconClipboardList,
IconClipboardCheck,
IconDisabled,
IconUser,
IconParking
} from '@tabler/icons';
// constant
const icons = {
IconKey,
IconReceipt2,
IconBug,
IconBellRinging,
IconPhoneCall,
IconQuestionMark,
IconShieldLock,
IconUserCheck,
IconMessage,
IconClipboardList,
IconClipboardCheck,
IconUser,
IconDisabled,
IconParking
};
// ==============================|| OPST MENU ITEMS ||============================== //
const opst = {
id: 'opst',
title: <FormattedMessage id="opst" />,
type: 'group',
children: [
{
/* 공지 사항 */
id: 'opst-1',
title: <FormattedMessage id="opst-1" />,
type: 'item',
icon: icons.IconClipboardCheck,
url: '/publicBoard',
breadcrumbs: true
},
{
/* 주정차 의견 진술 */
id: 'opst-2',
title: <FormattedMessage id="opst-2" />,
type: 'collapse',
icon: icons.IconParking,
children: [
{
/* 심의 목록 */
id: 'opst-2-1',
title: <FormattedMessage id="opst-2-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
},
{
/* 심의 등록 */
id: 'opst-2-2',
title: <FormattedMessage id="opst-2-2" />,
type: 'item',
url: '/board',
icon: icons.IconKey
}
]
},
{
/* 거주자 의견 진술 */
id: 'opst-3',
title: <FormattedMessage id="opst-3" />,
type: 'collapse',
icon: icons.IconUser,
children: [
{
/* 자료 관리 */
id: 'opst-3-1',
title: <FormattedMessage id="opst-3-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
},
{
/* 심의 목록 */
id: 'opst-3-2',
title: <FormattedMessage id="opst-2-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
},
{
/* 심의 등록 */
id: 'opst-3-3',
title: <FormattedMessage id="opst-2-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
}
]
},
{
/* 장애인 의견 진술 */
id: 'opst-4',
title: <FormattedMessage id="opst-4" />,
type: 'collapse',
icon: icons.IconDisabled,
children: [
{
/* 자료 관리 */
id: 'opst-4-1',
title: <FormattedMessage id="opst-3-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
},
{
/* 심의 목록 */
id: 'opst-4-2',
title: <FormattedMessage id="opst-2-1" />,
type: 'item',
url: '/board',
icon: icons.IconKey
},
{
/* 심의 등록 */
id: 'opst-4-3',
title: <FormattedMessage id="opst-2-2" />,
type: 'item',
url: '/board',
icon: icons.IconKey
}
]
},
{
/* 사용자 관리 */
id: 'opst-5',
title: <FormattedMessage id="opst-5" />,
type: 'collapse',
icon: icons.IconUserCheck,
children: [
{
/* 사용자 관리 */
id: 'opst-5-1',
title: <FormattedMessage id="opst-5" />,
type: 'item',
url: '/board',
icon: icons.IconKey,
target: true
},
{
/* 심사위원 평가 */
id: 'opst-5-2',
title: <FormattedMessage id="opst-5-2" />,
type: 'item',
url: '/board',
icon: icons.IconKey,
target: true
}
]
},
{
/* SMS 관리 */
id: 'opst-6',
title: <FormattedMessage id="opst-6" />,
type: 'item',
url: '/board',
icon: icons.IconMessage,
target: true
},
{
/* 게시판 관리 */
id: 'opst-7',
title: <FormattedMessage id="opst-7" />,
type: 'item',
url: '/board',
icon: icons.IconClipboardList
}
]
};
export default opst;

@ -8,6 +8,8 @@ import useConfig from 'hooks/useConfig';
// load locales files // load locales files
const loadLocaleData = (locale) => { const loadLocaleData = (locale) => {
switch (locale) { switch (locale) {
case 'en':
return import('utils/locales/en.json');
case 'fr': case 'fr':
return import('utils/locales/fr.json'); return import('utils/locales/fr.json');
case 'ro': case 'ro':
@ -15,7 +17,7 @@ const loadLocaleData = (locale) => {
case 'zh': case 'zh':
return import('utils/locales/zh.json'); return import('utils/locales/zh.json');
default: default:
return import('utils/locales/en.json'); return import('utils/locales/ko.json');
} }
}; };

@ -0,0 +1,52 @@
// material-ui
import { DataGrid } from '@mui/x-data-grid';
import { Box } from '@mui/material';
import { useTheme } from '@mui/material/styles';
// project imports
const MuiGridList = (props) => {
const { columns, rowsState, totalCount, setRowsState } = props;
const theme = useTheme();
return (
<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: any) => ({ ...prev, page }))}
onPageSizeChange={(pageSize) => setRowsState((prev: any) => ({ ...prev, page: 0, pageSize }))}
rowsPerPageOptions={[5, 10, 50, 100]}
pagination
/>
</Box>
);
};
export default MuiGridList;

@ -3,13 +3,121 @@
*/ */
import axios from 'axios'; import axios from 'axios';
import Swal from 'sweetalert2';
const axiosServices = axios.create();
const axiosService = axios.create({
baseURL: process.env.NODE_ENV === 'development' ? process.env.REACT_APP_API_URL : '',
withCredentials: process.env.NODE_ENV === 'development', // 개발시만 사용 : crossdomain
timeout: Number(process.env.REACT_APP_SERVER_TIMEOUT)
// headers: {
// 'Content-Type': 'application/json'
// // AUTH_HEADER_NAME: accessToken
// }
// params: {key: key}
});
/**
* before axios request
*/
axiosService.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);
}
);
/*
// interceptor for http // interceptor for http
axiosServices.interceptors.response.use( axiosServices.interceptors.response.use(
(response) => response, (response) => response,
(error) => Promise.reject((error.response && error.response.data) || 'Wrong Services') (error) => Promise.reject((error.response && error.response.data) || 'Wrong Services')
); );
*/
/**
* after axios response
*/
axiosService.interceptors.response.use(
(response) => {
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) => {
const alertError = (config, request, response, error) => {
if (error.isAxiosError) {
let errMsg = `시스템 에러~~~~~~~~~~~~`;
if (error.code === 'ECONNABORTED') errMsg = 'Server time out';
Swal.fire({
icon: 'error',
title: 'Api Error',
html: errMsg,
// imageUrl:
timer: 5000
}).then((r) => {});
return;
}
if (response) {
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 axiosServices; export default axiosServices;

@ -1,4 +1,17 @@
{ {
"opst": "Opst",
"opst-1": "Public board",
"opst-2": "주정차 의견 진술",
"opst-2-1": "심의 목록",
"opst-2-2": "심의 등록",
"opst-3": "거주자 의견 진술",
"opst-3-1": "자료 관리",
"opst-4": "장애인 의견 진술",
"opst-5": "사용자 관리",
"opst-5-2": "심사위원 평가",
"opst-6": "SMS 관리",
"opst-7": "Board",
"dashboard": "Dashboard", "dashboard": "Dashboard",
"default": "Default", "default": "Default",
"analytics": "Analytics", "analytics": "Analytics",

@ -0,0 +1,159 @@
{
"opst": "의견 진술 관리",
"opst-1": "공지 사항",
"opst-2": "주정차 의견 진술",
"opst-2-1": "심의 목록",
"opst-2-2": "심의 등록",
"opst-3": "거주자 의견 진술",
"opst-3-1": "자료 관리",
"opst-4": "장애인 의견 진술",
"opst-5": "사용자 관리",
"opst-5-2": "심사위원 평가",
"opst-6": "SMS 관리",
"opst-7": "게시판 관리",
"dashboard": "Dashboard",
"default": "Default",
"analytics": "Analytics",
"widget": "Widget",
"statistics": "Statistics",
"data": "Data",
"chart": "Chart",
"application": "Application",
"users": "Users",
"social-profile": "Social Profile",
"account-profile": "Account Profile",
"profile": "Profile",
"cards": "Cards",
"list": "List",
"style": "Style",
"customer": "Customer",
"customer-list": "Customer List",
"order-list": "Order List",
"create-invoice": "Create Invoice",
"order-details": "Order Details",
"product": "Product",
"product-review": "Product Review",
"chat": "Chat",
"mail": "Mail",
"contact": "Contact",
"calendar": "Calendar",
"e-commerce": "E-commerce",
"products": "Products",
"product-details": "Product Details",
"product-list": "Product List",
"checkout": "Checkout",
"forms": "Forms",
"components": "Components",
"autocomplete": "Autocomplete",
"button": "Button",
"checkbox": "Checkbox",
"date-time": "Date & Time",
"radio": "Radio",
"slider": "Slider",
"switch": "Switch",
"text-field": "Text Field",
"plugins": "Plugins",
"mask": "Mask",
"clipboard": "Clipboard",
"recaptcha": "reCaptcha",
"wysiwug-editor": "Wysiwug Editor",
"modal": "Modal",
"tooltip": "Tooltip",
"table": "Table",
"table-basic": "Basic Table",
"table-dense": "Dense Table",
"table-enhanced": "Enhanced Tables",
"table-data": "Data Table",
"table-customized": "Custom Table",
"table-sticky-header": "Fixed Header",
"table-collapse": "Collapse Table",
"charts": "Charts",
"apexchart": "Apexchart",
"organization-chart": "Organization Chart",
"forms-validation": "Forms Validation",
"forms-wizard": "Forms Wizard",
"layouts": "Layouts",
"multi-column-forms": "Multi Column Forms",
"action-bar": "Action Bar",
"sticky-action-bar": "Sticky Action Bar",
"ui-element": "UI Element",
"basic": "Basic",
"basic-caption": "8+ Basic Components",
"accordion": "Accordion",
"avatar": "Avatar",
"badges": "Badges",
"breadcrumb": "Breadcrumb",
"chip": "Chip",
"tabs": "Tabs",
"advance": "Advance",
"alert": "Alert",
"dialog": "Dialog",
"pagination": "Pagination",
"progress": "Progress",
"rating": "Rating",
"snackbar": "Snackbar",
"skeleton": "Skeleton",
"speeddial": "Speeddial",
"timeline": "Timeline",
"toggle-button": "Toggle Button",
"treeview": "Treeview",
"pages": "Pages",
"pages-caption": "Prebuild Pages",
"authentication": "Authentication",
"login": "Login",
"register": "Register",
"forgot-password": "Forgot Password",
"check-mail": "Check Mail",
"reset-password": "Reset Password",
"code-verification": "Code Verification",
"pricing": "Pricing",
"price": "Price",
"maintenance": "Maintenance",
"error-404": "Error 404",
"coming-soon": "Coming Soon",
"under-construction": "Under Construction",
"landing": "Landing",
"contact-us": "Contact US",
"faqs": "FAQs",
"privacy-policy": "Privacy Policy",
"utilities": "Utilities",
"typography": "Typography",
"color": "Color",
"shadow": "Shadow",
"icons": "Icons",
"tabler-icons": "Tabler Icons",
"material-icons": "Material Icons",
"animation": "Animation",
"grid": "Grid",
"others": "Others",
"menu-level": "Menu Levels",
"level": "Level",
"menu-level-subtitle": "Sub Caption Levels",
"menu-level-subtitle-caption": "Caption Collapse",
"menu-level-subtitle-item": "Caption Item",
"menu-level-subtitle-collapse": "Sub Collapse Caption",
"menu-level-subtitle-sub-item": "Sub Item Caption",
"disabled-menu": "Disabled Menu",
"oval-chip-menu": "Oval Chip",
"coded": "Coded",
"c": "C",
"outlined": "Outlined",
"sample-page": "Sample Page",
"documentation": "Documentation",
"roadmap": "Roadmap",
"title": "Multi Language",
"home": "Home",
"change": "Change Language",
"message": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiatnulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollitanim id est laborum."
}

@ -0,0 +1,119 @@
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';
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<[ICmmCode]>();
useEffect(() => {
// setRowsState(prev => ({...prev}))
fetch(
// `https://jsonplaceholder.typicode.com/comments?_start=${params.startRow}&_end=${params.endRow}`
`http://localhost:8090/api/v1/ctgy/board?page=${rowsState.page + 1}&size=${rowsState.pageSize}`
)
.then((r) => r.json())
.then((data) => {
setTotalCount(data.count);
// setTableData(data.data);
setRowsState((prev) => ({ ...prev, rows: data.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,319 @@
import React, { useEffect, useState } from 'react';
// material-ui
import {
Button,
Divider,
FormControl,
FormControlLabel,
FormHelperText,
FormLabel,
Grid,
InputAdornment,
OutlinedInput,
Radio,
RadioGroup,
TextField,
Typography
} from '@mui/material';
// assets
import { IconSearch } from '@tabler/icons';
import LinkTwoToneIcon from '@mui/icons-material/LinkTwoTone';
import LockTwoToneIcon from '@mui/icons-material/LockTwoTone';
// berry ui
import MainCard from 'ui-component/cards/MainCard';
import { gridSpacing } from 'store/constant';
import { useDispatch, useSelector } from 'store';
import { getDetailCards, filterDetailCards } from 'store/slices/user';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import { Dispatch } from 'redux';
import { GridColumns } from '@mui/x-data-grid';
import InputLabel from 'ui-component/extended/Form/InputLabel';
import SearchSection from 'layout/MainLayout/Header/SearchSection';
import boardService from 'apis/BoardService';
const Index = () => {
const dispatch = useDispatch();
const [category, setCategory] = useState('ciTitle');
const [searchTxt, setSearchTxt] = useState('');
// const handleSearch = async (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined) => {
// const newString = event?.target.value;
// setSearch(newString);
//
// if (newString) {
// dispatch(filterDetailCards(newString));
// } else {
// dispatch(getDetailCards());
// }
// };
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 5,
rows: []
// loading: false
});
const columns: GridColumns = [
{ 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 handleSearch = async (event: any) => {
console.log('~~~');
if (event.type === 'keydown' && event.key === 'Enter') {
const newString = event?.target.value;
setSearchTxt(newString);
// if (newString) {
// dispatch(filterDetailCards(newString));
// } else {
// dispatch(getDetailCards());
// }
}
};
useEffect(() => {
// setRowsState(prev => ({...prev}))
let params = {
page: rowsState.page + 1,
size: rowsState.pageSize
};
if (searchTxt) {
params = {
...params,
[category]: searchTxt
};
}
// eslint-disable-next-line @typescript-eslint/no-shadow
const res = boardService.getBoardList(params).then((response) => {
if (response && response.data) {
// setTotalCount(response?.count);
setRowsState((prevState) => ({ ...prevState, rows: response.data }));
}
});
// if (res) {
// setTotalCount(res.count);
// setRowsState((prev) => ({ ...prev, rows: res.data }));
// }
// fetch(
// // `https://jsonplaceholder.typicode.com/comments?_start=${params.startRow}&_end=${params.endRow}`
// `http://localhost:8090/api/v1/ctgy/board?page=${rowsState.page + 1}&size=${rowsState.pageSize}${search}`
// )
// .then((r) => r.json())
// .then((data) => {
// setTotalCount(data.count);
// // setTableData(data.data);
// setRowsState((prev) => ({ ...prev, rows: data.data }));
// });
}, [rowsState.page, rowsState.pageSize, searchTxt]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
<MainCard
// title={
// <Grid container alignItems="center" justifyContent="space-between" spacing={gridSpacing}>
// <Grid item>
// <Typography variant="h3">List</Typography>
// </Grid>
// <Grid item>
// <OutlinedInput
// id="input-search-list-style1"
// placeholder="Search"
// // onChange={handleSearch}
// onKeyDown={handleSearch}
// startAdornment={
// <InputAdornment position="start">
// <IconSearch stroke={1.5} size="1rem" />
// </InputAdornment>
// }
// size="small"
// />
// </Grid>
// </Grid>
// }
>
<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={6} lg={3}>
<Grid container spacing={1}>
<Grid item>
<FormControl>
<RadioGroup
row
aria-label="category"
name="row-radio-buttons-group"
value={category}
onChange={(e) => setCategory(e.target.value)}
>
<FormControlLabel value="ciTitle" control={<Radio />} label="제목" />
<FormControlLabel value="ciName" control={<Radio />} label="이름" />
<FormControlLabel value="ciContents" control={<Radio />} label="내용" />
</RadioGroup>
</FormControl>
</Grid>
<Grid item>
<OutlinedInput id="input-search-list-style1" placeholder="Search" onKeyDown={handleSearch} size="small" autoFocus />
<IconSearch stroke={1.5} size="1rem" />
</Grid>
</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,264 @@
import React, { 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';
import { useDispatch, useSelector } from 'store';
import { getDetailCards, filterDetailCards } from 'store/slices/user';
// project imports
import MuiGridList from 'ui-component/MuiGridList';
import { GridColumns } from '@mui/x-data-grid';
import InputLabel from 'ui-component/extended/Form/InputLabel';
const Index = () => {
const dispatch = useDispatch();
const [search, setSearch] = useState('');
// const handleSearch = async (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement> | undefined) => {
// const newString = event?.target.value;
// setSearch(newString);
//
// if (newString) {
// dispatch(filterDetailCards(newString));
// } else {
// dispatch(getDetailCards());
// }
// };
const [totalCount, setTotalCount] = useState(0);
const [rowsState, setRowsState] = useState({
page: 0,
pageSize: 5,
rows: []
// loading: false
});
const columns: GridColumns = [
{ 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 handleSearch = async (event) => {
console.log('~~~');
if (event.type === 'keydown' && event.key === 'Enter') {
const newString = event?.target.value;
setSearch(newString);
if (newString) {
dispatch(filterDetailCards(newString));
} else {
dispatch(getDetailCards());
}
}
};
useEffect(() => {
// setRowsState(prev => ({...prev}))
fetch(
// `https://jsonplaceholder.typicode.com/comments?_start=${params.startRow}&_end=${params.endRow}`
`http://localhost:8090/api/v1/ctgy/board?page=${rowsState.page + 1}&size=${rowsState.pageSize}`
)
.then((r) => r.json())
.then((data) => {
setTotalCount(data.count);
// setTableData(data.data);
setRowsState((prev) => ({ ...prev, rows: data.data }));
});
}, [rowsState.page, rowsState.pageSize]); // rowsState.page, rowsState.pageSize, rowsState.rows]);
return (
<MainCard
// title={
// <Grid container alignItems="center" justifyContent="space-between" spacing={gridSpacing}>
// <Grid item>
// <Typography variant="h3">List</Typography>
// </Grid>
// <Grid item>
// <OutlinedInput
// id="input-search-list-style1"
// placeholder="Search"
// // onChange={handleSearch}
// onKeyDown={handleSearch}
// startAdornment={
// <InputAdornment position="start">
// <IconSearch stroke={1.5} size="1rem" />
// </InputAdornment>
// }
// size="small"
// />
// </Grid>
// </Grid>
// }
>
{/* <Grid item xs={12}> */}
<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>
{/* </Grid> */}
{/* <Grid container> */}
<MuiGridList columns={columns} rowsState={rowsState} totalCount={totalCount} setRowsState={setRowsState} />
{/* </Grid> */}
</MainCard>
);
};
export default Index;

@ -0,0 +1,83 @@
import PropTypes from 'prop-types';
// material-ui
import { Button, CardActions, CardMedia, Divider, Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@mui/material';
// third party
import PerfectScrollbar from 'react-perfect-scrollbar';
// project imports
import MainCard from 'ui-component/cards/MainCard';
// assets
import Flag1 from 'assets/images/widget/AUSTRALIA.jpg';
import Flag2 from 'assets/images/widget/BRAZIL.jpg';
import Flag3 from 'assets/images/widget/GERMANY.jpg';
import Flag4 from 'assets/images/widget/UK.jpg';
import Flag5 from 'assets/images/widget/USA.jpg';
// table data
function createData(image, subject, dept, date) {
return { image, subject, dept, date };
}
const rows = [
createData(Flag1, 'Germany', 'Angelina Jolly', '56.23%'),
createData(Flag2, 'USA', 'John Deo', '25.23%'),
createData(Flag3, 'Australia', 'Jenifer Vintage', '12.45%'),
createData(Flag4, 'United Kingdom', 'Lori Moore', '8.65%'),
createData(Flag5, 'Brazil', 'Allianz Dacron', '3.56%'),
createData(Flag1, 'Australia', 'Jenifer Vintage', '12.45%'),
createData(Flag3, 'USA', 'John Deo', '25.23%'),
createData(Flag5, 'Australia', 'Jenifer Vintage', '12.45%'),
createData(Flag2, 'United Kingdom', 'Lori Moore', '8.65%')
];
// =========================|| DASHBOARD ANALYTICS - LATEST CUSTOMERS TABLE CARD ||========================= //
const LatestCustomerTableCard = ({ title }) => (
<MainCard title={title} content={false}>
<PerfectScrollbar style={{ height: 345, padding: 0 }}>
<TableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell sx={{ pl: 3 }}>#</TableCell>
<TableCell>Country</TableCell>
<TableCell>Name</TableCell>
<TableCell align="right" sx={{ pr: 3 }}>
Average
</TableCell>
</TableRow>
</TableHead>
<TableBody>
{rows.map((row, index) => (
<TableRow hover key={index}>
<TableCell sx={{ pl: 3 }}>
<CardMedia component="img" image={row.image} title="image" sx={{ width: 30, height: 'auto' }} />
</TableCell>
<TableCell>{row.subject}</TableCell>
<TableCell>{row.dept}</TableCell>
<TableCell align="right" sx={{ pr: 3 }}>
{row.date}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</PerfectScrollbar>
<Divider />
<CardActions sx={{ justifyContent: 'flex-end' }}>
<Button variant="text" size="small">
View all Latest Customers
</Button>
</CardActions>
</MainCard>
);
LatestCustomerTableCard.propTypes = {
title: PropTypes.string
};
export default LatestCustomerTableCard;

@ -0,0 +1,145 @@
import React from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Grid, Box, Typography } from '@mui/material';
// third party
import ApexCharts from 'apexcharts';
import Chart from 'react-apexcharts';
// project imports
import useConfig from 'hooks/useConfig';
import MainCard from 'ui-component/cards/MainCard';
import chartData from './chart-data/market-share-area-chart';
// assets
import { IconBrandFacebook, IconBrandYoutube, IconBrandTwitter } from '@tabler/icons';
import TrendingDownIcon from '@mui/icons-material/TrendingDown';
// ===========================|| DASHBOARD ANALYTICS - MARKET SHARE AREA CHART CARD ||=========================== //
const MarketShareAreaChartCard = () => {
const theme = useTheme();
const { navType } = useConfig();
const secondaryMain = theme.palette.secondary.main;
const errorMain = theme.palette.error.main;
const primaryDark = theme.palette.primary.dark;
React.useEffect(() => {
const newChartData = {
...chartData.options,
colors: [secondaryMain, errorMain, primaryDark],
tooltip: {
theme: navType === 'dark' ? 'dark' : 'light'
}
};
ApexCharts.exec(`market-share-area-chart`, 'updateOptions', newChartData);
}, [navType, secondaryMain, errorMain, primaryDark]);
return (
<MainCard sx={{ '&>div': { p: 0, pb: '0px !important' } }}>
<Box
sx={{
p: 3
}}
>
<Grid container direction="column" spacing={3}>
<Grid item container spacing={1} alignItems="center">
<Grid item>
<Typography variant="h3">Market Share</Typography>
</Grid>
<Grid item xs zeroMinWidth />
<Grid item>
<TrendingDownIcon fontSize="large" color="error" />
</Grid>
<Grid item>
<Typography variant="h3">27, 695.65</Typography>
</Grid>
</Grid>
<Grid item xs={12}>
<Typography sx={{ mt: -2.5, fontWeight: 400 }} color="inherit" variant="h5">
Department wise monthly sales report
</Typography>
</Grid>
<Grid item container justifyContent="space-around" alignItems="center" spacing={3}>
<Grid item>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<Typography
sx={{
width: 40,
height: 40,
color: theme.palette.secondary.main,
borderRadius: '12px',
padding: 1,
backgroundColor:
theme.palette.mode === 'dark'
? theme.palette.background.default
: theme.palette.secondary.light
}}
>
<IconBrandFacebook stroke={1.5} />
</Typography>
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h4">+45.36%</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<Typography
sx={{
width: 40,
height: 40,
color: theme.palette.primary.main,
borderRadius: '12px',
padding: 1,
backgroundColor:
theme.palette.mode === 'dark'
? theme.palette.background.default
: theme.palette.primary.light
}}
>
<IconBrandTwitter stroke={1.5} />
</Typography>
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h4">- 50.69%</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container alignItems="center" spacing={1}>
<Grid item>
<Typography
sx={{
width: 40,
height: 40,
color: theme.palette.error.main,
borderRadius: '12px',
padding: 1,
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.background.default : '#ffe9e9'
}}
>
<IconBrandYoutube stroke={2} />
</Typography>
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h4">+ 16.85%</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs zeroMinWidth />
</Grid>
</Grid>
</Box>
<Chart {...chartData} />
</MainCard>
);
};
export default MarketShareAreaChartCard;

@ -0,0 +1,186 @@
import PropTypes from 'prop-types';
// material-ui
import { Divider, List, ListItemButton, ListItemIcon, ListItemText, Stack, Typography } from '@mui/material';
// third party
import PerfectScrollbar from 'react-perfect-scrollbar';
// project imports
import MainCard from 'ui-component/cards/MainCard';
// assets
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import ArrowDropUpIcon from '@mui/icons-material/ArrowDropUp';
// ===========================|| DASHBOARD ANALYTICS - TOTAL REVENUE CARD ||=========================== //
const TotalRevenueCard = ({ title }) => {
const successSX = { color: 'success.dark' };
const errorSX = { color: 'error.main' };
return (
<MainCard title={title} content={false}>
<PerfectScrollbar style={{ height: 370 }}>
<List
component="nav"
aria-label="main mailbox folders"
sx={{
'& svg': {
width: 32,
my: -0.75,
ml: -0.75,
mr: 0.75
}
}}
>
<ListItemButton>
<ListItemIcon>
<ArrowDropUpIcon sx={successSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Bitcoin</span>
<Typography sx={successSX}>+ $145.85</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ethereum</span>
<Typography sx={errorSX}>- $6.368</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropUpIcon sx={successSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ripple</span>
<Typography sx={successSX}>+ $458.63</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Neo</span>
<Typography sx={errorSX}>- $5.631</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ethereum</span>
<Typography sx={errorSX}>- $6.368</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropUpIcon sx={successSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ripple</span>
<Typography sx={successSX}>+ $458.63</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Neo</span>
<Typography sx={errorSX}>- $5.631</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ethereum</span>
<Typography sx={errorSX}>- $6.368</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropUpIcon sx={successSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Ripple</span>
<Typography sx={successSX}>+ $458.63</Typography>
</Stack>
}
/>
</ListItemButton>
<Divider />
<ListItemButton>
<ListItemIcon>
<ArrowDropDownIcon sx={errorSX} />
</ListItemIcon>
<ListItemText
primary={
<Stack direction="row" justifyContent="space-between" alignItems="center">
<span>Neo</span>
<Typography sx={errorSX}>- $5.631</Typography>
</Stack>
}
/>
</ListItemButton>
</List>
</PerfectScrollbar>
</MainCard>
);
};
TotalRevenueCard.propTypes = {
title: PropTypes.string
};
export default TotalRevenueCard;

@ -0,0 +1,62 @@
// ==============================|| DASHBOARD - MARKET SHARE AREA CHART ||============================== //
const chartData = {
height: 200,
type: 'area',
options: {
chart: {
id: 'market-share-area-chart',
toolbar: {
show: false
},
zoom: {
enabled: false
},
sparkline: {
enabled: true
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 2
},
fill: {
type: 'gradient',
gradient: {
shadeIntensity: 1,
opacityFrom: 0.5,
opacityTo: 0,
stops: [0, 80, 100]
}
},
legend: {
show: false
},
yaxis: {
min: 1,
max: 100,
labels: {
show: false
}
}
},
series: [
{
name: 'Youtube',
data: [10, 90, 65, 85, 40, 80, 30]
},
{
name: 'Facebook',
data: [50, 30, 25, 15, 60, 10, 25]
},
{
name: 'Twitter',
data: [5, 50, 40, 55, 20, 40, 20]
}
]
};
export default chartData;

@ -0,0 +1,191 @@
// material-ui
import { useTheme } from '@mui/material/styles';
import { Grid, Typography, useMediaQuery } from '@mui/material';
// project imports
import MarketShareAreaChartCard from './MarketShareAreaChartCard';
import TotalRevenueCard from './TotalRevenueCard';
import LatestCustomerTableCard from './LatestCustomerTableCard';
import MainCard from 'ui-component/cards/MainCard';
import RevenueCard from 'ui-component/cards/RevenueCard';
import UserCountCard from 'ui-component/cards/UserCountCard';
import { gridSpacing } from 'store/constant';
// assets
import { IconShare, IconAccessPoint, IconCircles, IconCreditCard } from '@tabler/icons';
import MonetizationOnTwoToneIcon from '@mui/icons-material/MonetizationOnTwoTone';
import AccountCircleTwoTone from '@mui/icons-material/AccountCircleTwoTone';
import DescriptionTwoToneIcon from '@mui/icons-material/DescriptionTwoTone';
// ==============================|| ANALYTICS DASHBOARD ||============================== //
const Analytics = () => {
const theme = useTheme();
const matchDownXs = useMediaQuery(theme.breakpoints.down('sm'));
const blockSX = {
p: 2.5,
borderLeft: '1px solid ',
borderBottom: '1px solid ',
borderLeftColor: theme.palette.mode === 'dark' ? theme.palette.dark.main : theme.palette.grey[200],
borderBottomColor: theme.palette.mode === 'dark' ? theme.palette.dark.main : theme.palette.grey[200]
};
return (
<Grid container spacing={gridSpacing}>
<Grid item xs={12} lg={8} md={6}>
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<MarketShareAreaChartCard />
</Grid>
<Grid item xs={12} lg={6}>
<RevenueCard
primary="Revenue"
secondary="$42,562"
content="$50,032 Last Month"
iconPrimary={MonetizationOnTwoToneIcon}
color={theme.palette.secondary.main}
/>
</Grid>
<Grid item xs={12} lg={6}>
<RevenueCard
primary="Orders Received"
secondary="486"
content="20% Increase"
iconPrimary={AccountCircleTwoTone}
color={theme.palette.primary.main}
/>
</Grid>
<Grid item xs={12}>
<LatestCustomerTableCard title="Latest Customers" />
</Grid>
</Grid>
</Grid>
<Grid item xs={12} lg={4} md={6}>
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<MainCard
content={false}
sx={{
'& svg': {
width: 50,
height: 50,
color: theme.palette.secondary.main,
borderRadius: '14px',
p: 1.25,
bgcolor: theme.palette.mode === 'dark' ? theme.palette.background.default : 'primary.light'
}
}}
>
<Grid container alignItems="center" spacing={0}>
<Grid item xs={12} sm={6} sx={blockSX}>
<Grid
container
alignItems="center"
spacing={1}
justifyContent={matchDownXs ? 'space-between' : 'center'}
>
<Grid item>
<IconShare stroke={1.5} />
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h5" align="center">
1000
</Typography>
<Typography variant="subtitle2" align="center">
SHARES
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sm={6} sx={blockSX}>
<Grid
container
alignItems="center"
spacing={1}
justifyContent={matchDownXs ? 'space-between' : 'center'}
>
<Grid item>
<IconAccessPoint stroke={1.5} />
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h5" align="center">
600
</Typography>
<Typography variant="subtitle2" align="center">
NETWORK
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid container alignItems="center" spacing={0}>
<Grid item xs={12} sm={6} sx={blockSX}>
<Grid
container
alignItems="center"
spacing={1}
justifyContent={matchDownXs ? 'space-between' : 'center'}
>
<Grid item>
<IconCircles stroke={1.5} />
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h5" align="center">
3550
</Typography>
<Typography variant="subtitle2" align="center">
RETURNS
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sm={6} sx={blockSX}>
<Grid
container
alignItems="center"
spacing={1}
justifyContent={matchDownXs ? 'space-between' : 'center'}
>
<Grid item>
<IconCreditCard stroke={1.5} />
</Grid>
<Grid item sm zeroMinWidth>
<Typography variant="h5" align="center">
100%
</Typography>
<Typography variant="subtitle2" align="center">
ORDER
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</MainCard>
</Grid>
<Grid item xs={12}>
<TotalRevenueCard title="Total Revenue" />
</Grid>
<Grid item xs={12}>
<UserCountCard
primary="Daily user"
secondary="1,658"
iconPrimary={AccountCircleTwoTone}
color={theme.palette.secondary.main}
/>
</Grid>
<Grid item xs={12}>
<UserCountCard
primary="Daily page view"
secondary="1K"
iconPrimary={DescriptionTwoToneIcon}
color={theme.palette.primary.main}
/>
</Grid>
</Grid>
</Grid>
</Grid>
);
};
export default Analytics;

@ -0,0 +1,62 @@
import React from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Card, Grid, Typography } from '@mui/material';
// third-party
import ApexCharts from 'apexcharts';
import Chart from 'react-apexcharts';
// project imports
import useConfig from 'hooks/useConfig';
import chartData from './chart-data/bajaj-area-chart';
// ===========================|| DASHBOARD DEFAULT - BAJAJ AREA CHART CARD ||=========================== //
const BajajAreaChartCard = () => {
const theme = useTheme();
const { navType } = useConfig();
const orangeDark = theme.palette.secondary[800];
React.useEffect(() => {
const newSupportChart = {
...chartData.options,
colors: [orangeDark],
tooltip: {
theme: navType === 'dark' ? 'dark' : 'light'
}
};
ApexCharts.exec(`support-chart`, 'updateOptions', newSupportChart);
}, [navType, orangeDark]);
return (
<Card sx={{ bgcolor: 'secondary.light' }}>
<Grid container sx={{ p: 2, pb: 0, color: '#fff' }}>
<Grid item xs={12}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" sx={{ color: theme.palette.secondary.dark }}>
Bajaj Finery
</Typography>
</Grid>
<Grid item>
<Typography variant="h4" sx={{ color: theme.palette.grey[800] }}>
$1839.00
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Typography variant="subtitle2" sx={{ color: theme.palette.grey[800] }}>
10% Profit
</Typography>
</Grid>
</Grid>
<Chart {...chartData} />
</Card>
);
};
export default BajajAreaChartCard;

@ -0,0 +1,195 @@
import PropTypes from 'prop-types';
import React from 'react';
// material-ui
import { styled, useTheme } from '@mui/material/styles';
import { Avatar, Box, Grid, Menu, MenuItem, Typography } from '@mui/material';
// project imports
import MainCard from 'ui-component/cards/MainCard';
import SkeletonEarningCard from 'ui-component/cards/Skeleton/EarningCard';
// assets
import EarningIcon from 'assets/images/icons/earning.svg';
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
import GetAppTwoToneIcon from '@mui/icons-material/GetAppOutlined';
import FileCopyTwoToneIcon from '@mui/icons-material/FileCopyOutlined';
import PictureAsPdfTwoToneIcon from '@mui/icons-material/PictureAsPdfOutlined';
import ArchiveTwoToneIcon from '@mui/icons-material/ArchiveOutlined';
const CardWrapper = styled(MainCard)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.dark.dark : theme.palette.secondary.dark,
color: '#fff',
overflow: 'hidden',
position: 'relative',
'&:after': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background:
theme.palette.mode === 'dark'
? `linear-gradient(210.04deg, ${theme.palette.secondary.dark} -50.94%, rgba(144, 202, 249, 0) 95.49%)`
: theme.palette.secondary[800],
borderRadius: '50%',
top: -85,
right: -95,
[theme.breakpoints.down('sm')]: {
top: -105,
right: -140
}
},
'&:before': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background:
theme.palette.mode === 'dark'
? `linear-gradient(140.9deg, ${theme.palette.secondary.dark} -14.02%, rgba(144, 202, 249, 0) 85.50%)`
: theme.palette.secondary[800],
borderRadius: '50%',
top: -125,
right: -15,
opacity: 0.5,
[theme.breakpoints.down('sm')]: {
top: -155,
right: -70
}
}
}));
// ===========================|| DASHBOARD DEFAULT - EARNING CARD ||=========================== //
const EarningCard = ({ isLoading }) => {
const theme = useTheme();
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
{isLoading ? (
<SkeletonEarningCard />
) : (
<CardWrapper border={false} content={false}>
<Box sx={{ p: 2.25 }}>
<Grid container direction="column">
<Grid item>
<Grid container justifyContent="space-between">
<Grid item>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
backgroundColor:
theme.palette.mode === 'dark' ? theme.palette.dark.main : theme.palette.secondary[800],
mt: 1
}}
>
<img src={EarningIcon} alt="Notification" />
</Avatar>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.mediumAvatar,
backgroundColor:
theme.palette.mode === 'dark' ? theme.palette.dark.dark : theme.palette.secondary.dark,
color: theme.palette.secondary[200],
zIndex: 1
}}
aria-controls="menu-earning-card"
aria-haspopup="true"
onClick={handleClick}
>
<MoreHorizIcon fontSize="inherit" />
</Avatar>
<Menu
id="menu-earning-card"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
variant="selectedMenu"
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
>
<MenuItem onClick={handleClose}>
<GetAppTwoToneIcon sx={{ mr: 1.75 }} /> Import Card
</MenuItem>
<MenuItem onClick={handleClose}>
<FileCopyTwoToneIcon sx={{ mr: 1.75 }} /> Copy Data
</MenuItem>
<MenuItem onClick={handleClose}>
<PictureAsPdfTwoToneIcon sx={{ mr: 1.75 }} /> Export
</MenuItem>
<MenuItem onClick={handleClose}>
<ArchiveTwoToneIcon sx={{ mr: 1.75 }} /> Archive File
</MenuItem>
</Menu>
</Grid>
</Grid>
</Grid>
<Grid item>
<Grid container alignItems="center">
<Grid item>
<Typography sx={{ fontSize: '2.125rem', fontWeight: 500, mr: 1, mt: 1.75, mb: 0.75 }}>
$500.00
</Typography>
</Grid>
<Grid item>
<Avatar
sx={{
cursor: 'pointer',
...theme.typography.smallAvatar,
backgroundColor: theme.palette.secondary[200],
color: theme.palette.secondary.dark
}}
>
<ArrowUpwardIcon fontSize="inherit" sx={{ transform: 'rotate3d(1, 1, 1, 45deg)' }} />
</Avatar>
</Grid>
</Grid>
</Grid>
<Grid item sx={{ mb: 1.25 }}>
<Typography
sx={{
fontSize: '1rem',
fontWeight: 500,
color: theme.palette.mode === 'dark' ? theme.palette.text.secondary : theme.palette.secondary[200]
}}
>
Total Earning
</Typography>
</Grid>
</Grid>
</Box>
</CardWrapper>
)}
</>
);
};
EarningCard.propTypes = {
isLoading: PropTypes.bool
};
export default EarningCard;

@ -0,0 +1,309 @@
import PropTypes from 'prop-types';
import React from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Avatar, Button, CardActions, CardContent, Divider, Grid, Menu, MenuItem, Typography } from '@mui/material';
// project imports
import BajajAreaChartCard from './BajajAreaChartCard';
import MainCard from 'ui-component/cards/MainCard';
import SkeletonPopularCard from 'ui-component/cards/Skeleton/PopularCard';
import { gridSpacing } from 'store/constant';
// assets
import ChevronRightOutlinedIcon from '@mui/icons-material/ChevronRightOutlined';
import MoreHorizOutlinedIcon from '@mui/icons-material/MoreHorizOutlined';
import KeyboardArrowUpOutlinedIcon from '@mui/icons-material/KeyboardArrowUpOutlined';
import KeyboardArrowDownOutlinedIcon from '@mui/icons-material/KeyboardArrowDownOutlined';
// ==============================|| DASHBOARD DEFAULT - POPULAR CARD ||============================== //
const PopularCard = ({ isLoading }) => {
const theme = useTheme();
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
{isLoading ? (
<SkeletonPopularCard />
) : (
<MainCard content={false}>
<CardContent>
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<Grid container alignContent="center" justifyContent="space-between">
<Grid item>
<Typography variant="h4">Popular Stocks</Typography>
</Grid>
<Grid item>
<MoreHorizOutlinedIcon
fontSize="small"
sx={{
color: theme.palette.primary[200],
cursor: 'pointer'
}}
aria-controls="menu-popular-card"
aria-haspopup="true"
onClick={handleClick}
/>
<Menu
id="menu-popular-card"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
variant="selectedMenu"
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right'
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right'
}}
>
<MenuItem onClick={handleClose}> Today</MenuItem>
<MenuItem onClick={handleClose}> This Month</MenuItem>
<MenuItem onClick={handleClose}> This Year </MenuItem>
</Menu>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} sx={{ pt: '16px !important' }}>
<BajajAreaChartCard />
</Grid>
<Grid item xs={12}>
<Grid container direction="column">
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
Bajaj Finery
</Typography>
</Grid>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
$1839.00
</Typography>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
width: 16,
height: 16,
borderRadius: '5px',
backgroundColor: theme.palette.success.light,
color: theme.palette.success.dark,
ml: 2
}}
>
<KeyboardArrowUpOutlinedIcon fontSize="small" color="inherit" />
</Avatar>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle2" sx={{ color: 'success.dark' }}>
10% Profit
</Typography>
</Grid>
</Grid>
<Divider sx={{ my: 1.5 }} />
<Grid container direction="column">
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
TTML
</Typography>
</Grid>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
$100.00
</Typography>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
width: 16,
height: 16,
borderRadius: '5px',
backgroundColor: theme.palette.orange.light,
color: theme.palette.orange.dark,
marginLeft: 1.875
}}
>
<KeyboardArrowDownOutlinedIcon fontSize="small" color="inherit" />
</Avatar>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle2" sx={{ color: theme.palette.orange.dark }}>
10% loss
</Typography>
</Grid>
</Grid>
<Divider sx={{ my: 1.5 }} />
<Grid container direction="column">
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
Reliance
</Typography>
</Grid>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
$200.00
</Typography>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
width: 16,
height: 16,
borderRadius: '5px',
backgroundColor: theme.palette.success.light,
color: theme.palette.success.dark,
ml: 2
}}
>
<KeyboardArrowUpOutlinedIcon fontSize="small" color="inherit" />
</Avatar>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle2" sx={{ color: theme.palette.success.dark }}>
10% Profit
</Typography>
</Grid>
</Grid>
<Divider sx={{ my: 1.5 }} />
<Grid container direction="column">
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
TTML
</Typography>
</Grid>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
$189.00
</Typography>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
width: 16,
height: 16,
borderRadius: '5px',
backgroundColor: theme.palette.orange.light,
color: theme.palette.orange.dark,
ml: 2
}}
>
<KeyboardArrowDownOutlinedIcon fontSize="small" color="inherit" />
</Avatar>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle2" sx={{ color: theme.palette.orange.dark }}>
10% loss
</Typography>
</Grid>
</Grid>
<Divider sx={{ my: 1.5 }} />
<Grid container direction="column">
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
Stolon
</Typography>
</Grid>
<Grid item>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Typography variant="subtitle1" color="inherit">
$189.00
</Typography>
</Grid>
<Grid item>
<Avatar
variant="rounded"
sx={{
width: 16,
height: 16,
borderRadius: '5px',
backgroundColor: theme.palette.orange.light,
color: theme.palette.orange.dark,
ml: 2
}}
>
<KeyboardArrowDownOutlinedIcon fontSize="small" color="inherit" />
</Avatar>
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item>
<Typography variant="subtitle2" sx={{ color: theme.palette.orange.dark }}>
10% loss
</Typography>
</Grid>
</Grid>
</Grid>
</Grid>
</CardContent>
<CardActions sx={{ p: 1.25, pt: 0, justifyContent: 'center' }}>
<Button size="small" disableElevation>
View All
<ChevronRightOutlinedIcon />
</Button>
</CardActions>
</MainCard>
)}
</>
);
};
PopularCard.propTypes = {
isLoading: PropTypes.bool
};
export default PopularCard;

@ -0,0 +1,139 @@
import PropTypes from 'prop-types';
import React from 'react';
// material-ui
import { useTheme } from '@mui/material/styles';
import { Grid, MenuItem, TextField, Typography } from '@mui/material';
// third-party
import ApexCharts from 'apexcharts';
import Chart from 'react-apexcharts';
// project imports
import useConfig from 'hooks/useConfig';
import SkeletonTotalGrowthBarChart from 'ui-component/cards/Skeleton/TotalGrowthBarChart';
import MainCard from 'ui-component/cards/MainCard';
import { gridSpacing } from 'store/constant';
// chart data
import chartData from './chart-data/total-growth-bar-chart';
const status = [
{
value: 'today',
label: 'Today'
},
{
value: 'month',
label: 'This Month'
},
{
value: 'year',
label: 'This Year'
}
];
// ==============================|| DASHBOARD DEFAULT - TOTAL GROWTH BAR CHART ||============================== //
const TotalGrowthBarChart = ({ isLoading }) => {
const [value, setValue] = React.useState('today');
const theme = useTheme();
const { navType } = useConfig();
const { primary } = theme.palette.text;
const darkLight = theme.palette.dark.light;
const grey200 = theme.palette.grey[200];
const grey500 = theme.palette.grey[500];
const primary200 = theme.palette.primary[200];
const primaryDark = theme.palette.primary.dark;
const secondaryMain = theme.palette.secondary.main;
const secondaryLight = theme.palette.secondary.light;
React.useEffect(() => {
const newChartData = {
...chartData.options,
colors: [primary200, primaryDark, secondaryMain, secondaryLight],
xaxis: {
labels: {
style: {
colors: [primary, primary, primary, primary, primary, primary, primary, primary, primary, primary, primary, primary]
}
}
},
yaxis: {
labels: {
style: {
colors: [primary]
}
}
},
grid: {
borderColor: navType === 'dark' ? darkLight + 20 : grey200
},
tooltip: {
theme: navType === 'dark' ? 'dark' : 'light'
},
legend: {
labels: {
colors: grey500
}
}
};
// do not load chart when loading
if (!isLoading) {
ApexCharts.exec(`bar-chart`, 'updateOptions', newChartData);
}
}, [navType, primary200, primaryDark, secondaryMain, secondaryLight, primary, darkLight, grey200, isLoading, grey500]);
return (
<>
{isLoading ? (
<SkeletonTotalGrowthBarChart />
) : (
<MainCard>
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<Grid container alignItems="center" justifyContent="space-between">
<Grid item>
<Grid container direction="column" spacing={1}>
<Grid item>
<Typography variant="subtitle2">Total Growth</Typography>
</Grid>
<Grid item>
<Typography variant="h3">$2,324.00</Typography>
</Grid>
</Grid>
</Grid>
<Grid item>
<TextField
id="standard-select-currency"
select
value={value}
onChange={(e) => setValue(e.target.value)}
>
{status.map((option) => (
<MenuItem key={option.value} value={option.value}>
{option.label}
</MenuItem>
))}
</TextField>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Chart {...chartData} />
</Grid>
</Grid>
</MainCard>
)}
</>
);
};
TotalGrowthBarChart.propTypes = {
isLoading: PropTypes.bool
};
export default TotalGrowthBarChart;

@ -0,0 +1,98 @@
import PropTypes from 'prop-types';
// material-ui
import { styled, useTheme } from '@mui/material/styles';
import { Avatar, Box, List, ListItem, ListItemAvatar, ListItemText, Typography } from '@mui/material';
// project imports
import MainCard from 'ui-component/cards/MainCard';
import TotalIncomeCard from 'ui-component/cards/Skeleton/TotalIncomeCard';
// assets
import TableChartOutlinedIcon from '@mui/icons-material/TableChartOutlined';
// styles
const CardWrapper = styled(MainCard)(({ theme }) => ({
backgroundColor: theme.palette.primary.dark,
color: theme.palette.primary.light,
overflow: 'hidden',
position: 'relative',
'&:after': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background: `linear-gradient(210.04deg, ${theme.palette.primary[200]} -50.94%, rgba(144, 202, 249, 0) 83.49%)`,
borderRadius: '50%',
top: -30,
right: -180
},
'&:before': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background: `linear-gradient(140.9deg, ${theme.palette.primary[200]} -14.02%, rgba(144, 202, 249, 0) 77.58%)`,
borderRadius: '50%',
top: -160,
right: -130
}
}));
// ==============================|| DASHBOARD - TOTAL INCOME DARK CARD ||============================== //
const TotalIncomeDarkCard = ({ isLoading }) => {
const theme = useTheme();
return (
<>
{isLoading ? (
<TotalIncomeCard />
) : (
<CardWrapper border={false} content={false}>
<Box sx={{ p: 2 }}>
<List sx={{ py: 0 }}>
<ListItem alignItems="center" disableGutters sx={{ py: 0 }}>
<ListItemAvatar>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
backgroundColor: theme.palette.primary[800],
color: '#fff'
}}
>
<TableChartOutlinedIcon fontSize="inherit" />
</Avatar>
</ListItemAvatar>
<ListItemText
sx={{
py: 0,
mt: 0.45,
mb: 0.45
}}
primary={
<Typography variant="h4" sx={{ color: '#fff' }}>
$203k
</Typography>
}
secondary={
<Typography variant="subtitle2" sx={{ color: 'primary.light', mt: 0.25 }}>
Total Income
</Typography>
}
/>
</ListItem>
</List>
</Box>
</CardWrapper>
)}
</>
);
};
TotalIncomeDarkCard.propTypes = {
isLoading: PropTypes.bool
};
export default TotalIncomeDarkCard;

@ -0,0 +1,100 @@
import PropTypes from 'prop-types';
// material-ui
import { useTheme, styled } from '@mui/material/styles';
import { Avatar, Box, List, ListItem, ListItemAvatar, ListItemText, Typography } from '@mui/material';
// project imports
import MainCard from 'ui-component/cards/MainCard';
import TotalIncomeCard from 'ui-component/cards/Skeleton/TotalIncomeCard';
// assets
import StorefrontTwoToneIcon from '@mui/icons-material/StorefrontTwoTone';
// styles
const CardWrapper = styled(MainCard)(({ theme }) => ({
overflow: 'hidden',
position: 'relative',
'&:after': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background: `linear-gradient(210.04deg, ${theme.palette.warning.dark} -50.94%, rgba(144, 202, 249, 0) 83.49%)`,
borderRadius: '50%',
top: -30,
right: -180
},
'&:before': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background: `linear-gradient(140.9deg, ${theme.palette.warning.dark} -14.02%, rgba(144, 202, 249, 0) 70.50%)`,
borderRadius: '50%',
top: -160,
right: -130
}
}));
// ==============================|| DASHBOARD - TOTAL INCOME LIGHT CARD ||============================== //
const TotalIncomeLightCard = ({ isLoading }) => {
const theme = useTheme();
return (
<>
{isLoading ? (
<TotalIncomeCard />
) : (
<CardWrapper border={false} content={false}>
<Box sx={{ p: 2 }}>
<List sx={{ py: 0 }}>
<ListItem alignItems="center" disableGutters sx={{ py: 0 }}>
<ListItemAvatar>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
backgroundColor:
theme.palette.mode === 'dark' ? theme.palette.dark.main : theme.palette.warning.light,
color: theme.palette.mode === 'dark' ? theme.palette.warning.dark : theme.palette.warning.dark
}}
>
<StorefrontTwoToneIcon fontSize="inherit" />
</Avatar>
</ListItemAvatar>
<ListItemText
sx={{
py: 0,
mt: 0.45,
mb: 0.45
}}
primary={<Typography variant="h4">$203k</Typography>}
secondary={
<Typography
variant="subtitle2"
sx={{
color: theme.palette.grey[500],
mt: 0.5
}}
>
Total Income
</Typography>
}
/>
</ListItem>
</List>
</Box>
</CardWrapper>
)}
</>
);
};
TotalIncomeLightCard.propTypes = {
isLoading: PropTypes.bool
};
export default TotalIncomeLightCard;

@ -0,0 +1,187 @@
import PropTypes from 'prop-types';
import React from 'react';
// material-ui
import { useTheme, styled } from '@mui/material/styles';
import { Avatar, Box, Button, Grid, Typography } from '@mui/material';
// third-party
import Chart from 'react-apexcharts';
// project imports
import MainCard from 'ui-component/cards/MainCard';
import SkeletonTotalOrderCard from 'ui-component/cards/Skeleton/EarningCard';
import ChartDataMonth from './chart-data/total-order-month-line-chart';
import ChartDataYear from './chart-data/total-order-year-line-chart';
// assets
import LocalMallOutlinedIcon from '@mui/icons-material/LocalMallOutlined';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
const CardWrapper = styled(MainCard)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? theme.palette.dark.dark : theme.palette.primary.dark,
color: '#fff',
overflow: 'hidden',
position: 'relative',
'&>div': {
position: 'relative',
zIndex: 5
},
'&:after': {
content: '""',
position: 'absolute',
width: 210,
height: 210,
background:
theme.palette.mode === 'dark'
? `linear-gradient(210.04deg, ${theme.palette.primary.dark} -50.94%, rgba(144, 202, 249, 0) 95.49%)`
: theme.palette.primary[800],
borderRadius: '50%',
zIndex: 1,
top: -85,
right: -95,
[theme.breakpoints.down('sm')]: {
top: -105,
right: -140
}
},
'&:before': {
content: '""',
position: 'absolute',
zIndex: 1,
width: 210,
height: 210,
background:
theme.palette.mode === 'dark'
? `linear-gradient(140.9deg, ${theme.palette.primary.dark} -14.02%, rgba(144, 202, 249, 0) 82.50%)`
: theme.palette.primary[800],
borderRadius: '50%',
top: -125,
right: -15,
opacity: 0.5,
[theme.breakpoints.down('sm')]: {
top: -155,
right: -70
}
}
}));
// ==============================|| DASHBOARD - TOTAL ORDER LINE CHART CARD ||============================== //
const TotalOrderLineChartCard = ({ isLoading }) => {
const theme = useTheme();
const [timeValue, setTimeValue] = React.useState(false);
const handleChangeTime = (event, newValue) => {
setTimeValue(newValue);
};
return (
<>
{isLoading ? (
<SkeletonTotalOrderCard />
) : (
<CardWrapper border={false} content={false}>
<Box sx={{ p: 2.25 }}>
<Grid container direction="column">
<Grid item>
<Grid container justifyContent="space-between">
<Grid item>
<Avatar
variant="rounded"
sx={{
...theme.typography.commonAvatar,
...theme.typography.largeAvatar,
backgroundColor:
theme.palette.mode === 'dark' ? theme.palette.dark.main : theme.palette.primary[800],
color: '#fff',
mt: 1
}}
>
<LocalMallOutlinedIcon fontSize="inherit" />
</Avatar>
</Grid>
<Grid item>
<Button
disableElevation
variant={timeValue ? 'contained' : 'text'}
size="small"
sx={{ color: 'inherit' }}
onClick={(e) => handleChangeTime(e, true)}
>
Month
</Button>
<Button
disableElevation
variant={!timeValue ? 'contained' : 'text'}
size="small"
sx={{ color: 'inherit' }}
onClick={(e) => handleChangeTime(e, false)}
>
Year
</Button>
</Grid>
</Grid>
</Grid>
<Grid item sx={{ mb: 0.75 }}>
<Grid container alignItems="center">
<Grid item xs={6}>
<Grid container alignItems="center">
<Grid item>
{timeValue ? (
<Typography sx={{ fontSize: '2.125rem', fontWeight: 500, mr: 1, mt: 1.75, mb: 0.75 }}>
$108
</Typography>
) : (
<Typography sx={{ fontSize: '2.125rem', fontWeight: 500, mr: 1, mt: 1.75, mb: 0.75 }}>
$961
</Typography>
)}
</Grid>
<Grid item>
<Avatar
sx={{
...theme.typography.smallAvatar,
cursor: 'pointer',
backgroundColor: theme.palette.primary[200],
color: theme.palette.primary.dark
}}
>
<ArrowDownwardIcon fontSize="inherit" sx={{ transform: 'rotate3d(1, 1, 1, 45deg)' }} />
</Avatar>
</Grid>
<Grid item xs={12}>
<Typography
sx={{
fontSize: '1rem',
fontWeight: 500,
color:
theme.palette.mode === 'dark'
? theme.palette.text.secondary
: theme.palette.primary[200]
}}
>
Total Order
</Typography>
</Grid>
</Grid>
</Grid>
<Grid item xs={6}>
{timeValue ? <Chart {...ChartDataMonth} /> : <Chart {...ChartDataYear} />}
</Grid>
</Grid>
</Grid>
</Grid>
</Box>
</CardWrapper>
)}
</>
);
};
TotalOrderLineChartCard.propTypes = {
isLoading: PropTypes.bool
};
export default TotalOrderLineChartCard;

@ -0,0 +1,44 @@
// ==============================|| DASHBOARD - BAJAJ AREA CHART ||============================== //
const chartData = {
type: 'area',
height: 95,
options: {
chart: {
id: 'support-chart',
sparkline: {
enabled: true
}
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth',
width: 1
},
tooltip: {
fixed: {
enabled: false
},
x: {
show: false
},
y: {
title: {
formatter: () => 'Ticket '
}
},
marker: {
show: false
}
}
},
series: [
{
data: [0, 15, 10, 50, 30, 40, 25]
}
]
};
export default chartData;

@ -0,0 +1,86 @@
// ==============================|| DASHBOARD - TOTAL GROWTH BAR CHART ||============================== //
const chartData = {
height: 480,
type: 'bar',
options: {
chart: {
id: 'bar-chart',
stacked: true,
toolbar: {
show: true
},
zoom: {
enabled: true
}
},
responsive: [
{
breakpoint: 480,
options: {
legend: {
position: 'bottom',
offsetX: -10,
offsetY: 0
}
}
}
],
plotOptions: {
bar: {
horizontal: false,
columnWidth: '50%'
}
},
xaxis: {
type: 'category',
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
},
legend: {
show: true,
fontFamily: `'Roboto', sans-serif`,
position: 'bottom',
offsetX: 20,
labels: {
useSeriesColors: false
},
markers: {
width: 16,
height: 16,
radius: 5
},
itemMargin: {
horizontal: 15,
vertical: 8
}
},
fill: {
type: 'solid'
},
dataLabels: {
enabled: false
},
grid: {
show: true
}
},
series: [
{
name: 'Investment',
data: [35, 125, 35, 35, 35, 80, 35, 20, 35, 45, 15, 75]
},
{
name: 'Loss',
data: [35, 15, 15, 35, 65, 40, 80, 25, 15, 85, 25, 75]
},
{
name: 'Profit',
data: [35, 145, 35, 35, 20, 105, 100, 10, 65, 45, 30, 10]
},
{
name: 'Maintenance',
data: [0, 0, 75, 0, 0, 115, 0, 0, 0, 0, 150, 0]
}
]
};
export default chartData;

@ -0,0 +1,54 @@
// ==============================|| DASHBOARD - TOTAL ORDER MONTH CHART ||============================== //
const chartData = {
type: 'line',
height: 90,
options: {
chart: {
sparkline: {
enabled: true
}
},
dataLabels: {
enabled: false
},
colors: ['#fff'],
fill: {
type: 'solid',
opacity: 1
},
stroke: {
curve: 'smooth',
width: 3
},
yaxis: {
min: 0,
max: 100
},
tooltip: {
theme: 'dark',
fixed: {
enabled: false
},
x: {
show: false
},
y: {
title: {
formatter: () => 'Total Order'
}
},
marker: {
show: false
}
}
},
series: [
{
name: 'series1',
data: [45, 66, 41, 89, 25, 44, 9, 54]
}
]
};
export default chartData;

@ -0,0 +1,54 @@
// ==============================|| DASHBOARD - TOTAL ORDER YEAR CHART ||============================== //
const chartData = {
type: 'line',
height: 90,
options: {
chart: {
sparkline: {
enabled: true
}
},
dataLabels: {
enabled: false
},
colors: ['#fff'],
fill: {
type: 'solid',
opacity: 1
},
stroke: {
curve: 'smooth',
width: 3
},
yaxis: {
min: 0,
max: 100
},
tooltip: {
theme: 'dark',
fixed: {
enabled: false
},
x: {
show: false
},
y: {
title: {
formatter: () => 'Total Order'
}
},
marker: {
show: false
}
}
},
series: [
{
name: 'series1',
data: [35, 44, 9, 54, 45, 66, 41, 69]
}
]
};
export default chartData;

@ -0,0 +1,59 @@
import { useEffect, useState } from 'react';
// material-ui
import { Grid } from '@mui/material';
// project imports
import EarningCard from './EarningCard';
import PopularCard from './PopularCard';
import TotalOrderLineChartCard from './TotalOrderLineChartCard';
import TotalIncomeDarkCard from './TotalIncomeDarkCard';
import TotalIncomeLightCard from './TotalIncomeLightCard';
import TotalGrowthBarChart from './TotalGrowthBarChart';
import { gridSpacing } from 'store/constant';
// ==============================|| DEFAULT DASHBOARD ||============================== //
const Dashboard = () => {
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
}, []);
return (
<Grid container spacing={gridSpacing}>
<Grid item xs={12}>
<Grid container spacing={gridSpacing}>
<Grid item lg={4} md={6} sm={6} xs={12}>
<EarningCard isLoading={isLoading} />
</Grid>
<Grid item lg={4} md={6} sm={6} xs={12}>
<TotalOrderLineChartCard isLoading={isLoading} />
</Grid>
<Grid item lg={4} md={12} sm={12} xs={12}>
<Grid container spacing={gridSpacing}>
<Grid item sm={6} xs={12} md={6} lg={12}>
<TotalIncomeDarkCard isLoading={isLoading} />
</Grid>
<Grid item sm={6} xs={12} md={6} lg={12}>
<TotalIncomeLightCard isLoading={isLoading} />
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12}>
<Grid container spacing={gridSpacing}>
<Grid item xs={12} md={8}>
<TotalGrowthBarChart isLoading={isLoading} />
</Grid>
<Grid item xs={12} md={4}>
<PopularCard isLoading={isLoading} />
</Grid>
</Grid>
</Grid>
</Grid>
);
};
export default Dashboard;

14617
yarn.lock

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save