diff --git a/package.json b/package.json index 22d80c8..a1f0e3f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "react-bootstrap": "1.0.1", "react-dom": "^17.0.2", "react-js-pagination": "^3.0.3", + "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", "react-spinners": "^0.11.0", "sweetalert": "^2.1.2", diff --git a/public/assets/images/bg1.jpeg b/public/assets/images/bg1.jpeg new file mode 100644 index 0000000..98691c1 Binary files /dev/null and b/public/assets/images/bg1.jpeg differ diff --git a/public/img/bg1.jpeg b/public/img/bg1.jpeg new file mode 100644 index 0000000..918dd89 Binary files /dev/null and b/public/img/bg1.jpeg differ diff --git a/src/App.css b/src/App.css index d761597..bb65a2d 100644 --- a/src/App.css +++ b/src/App.css @@ -179,3 +179,100 @@ img.opacity_img_click.active { .pagination { float: right; } + +button.close { + color: red; + border: 1px solid red; + display: flex; + justify-content: center; + width: 35px; + height: 30px; + background-color: red; + color: white; +} + +button.close:hover { + color: white +} + +.disable-btn-upload { + color: #00000040; + border-color: #d9d9d9; + background: #f5f5f5; + text-shadow: none; + box-shadow: none; +} + +.disable-btn-upload:hover, .disable-btn-upload:focus { + color: #00000040; + border-color: #d9d9d9; + background: #f5f5f5; + text-shadow: none; + box-shadow: none; +} + +.login__page { + background: url("/public/assets/images/bg1.jpeg") top left repeat; + + background-color: #eee; + width: 100%; + background-size: cover; + bottom: 0; + background-repeat: cover; + background-position: center; + min-height: 100vh; + position: relative; + display: flex; + justify-content: center; + top: 0; +} +.login__page .login__form { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + min-height: 100vh; + position: relative; +} +.login__page .login__form .login-form-button { + width: 100%; +} +.login__page .login__form .login__text { + margin-bottom: 30px; + font-weight: 800; + font-size: 30px; + color: #0695e2; + text-align: center; +} +.login__page .login__form .login__sec { + background: #d3d3d3 de; + padding: 50px 50px 30px 50px; + border-radius: 10px; + width: 400px; +} +.login__page .login__form .login__sec h2::after { + content: " "; + width: 100px; + height: 5px; + background: #0695e2; + display: block; + margin-top: 5px; + border-radius: 3px; + margin-left: auto; + margin-right: auto; +} +.login__page .login__form .login__sec .login__error { + margin-bottom: 20px; +} +.login__page .login__form .input__Cus { + padding: 10px 15px !important; + outline: none !important; + border: 1px solid transparent !important; + border-radius: 8px !important; +} +.login__page .login__form .login-form-button { + background-color: #0695e2 !important; + border-radius: 10px !important; + height: 50px !important; + font-weight: bold !important; +} diff --git a/src/App.js b/src/App.js index 47550e7..d56de06 100644 --- a/src/App.js +++ b/src/App.js @@ -1,12 +1,21 @@ import React from 'react' import ListItem from './components/List/ListItem'; +import Login from './components/Login/Login'; import 'antd/dist/antd.css'; import "./App.css"; +import { BrowserRouter as Router, Navigate, Route, Routes } from 'react-router-dom'; + function App() { return (
- + + + } /> + } /> + } /> + +
); } diff --git a/src/components/List/ListItem.js b/src/components/List/ListItem.js index 18169b4..7c378c2 100644 --- a/src/components/List/ListItem.js +++ b/src/components/List/ListItem.js @@ -7,7 +7,8 @@ import React, { useEffect, useState } from 'react'; import Pagination from "react-js-pagination"; import { PulseLoader } from 'react-spinners'; import { HOST } from '../../config/index'; - +import swal from 'sweetalert'; +import { useLocation } from 'react-router-dom'; export default function ListItem() { @@ -23,6 +24,23 @@ export default function ListItem() { const [totalItems, setTotalItems] = useState(10) const itemsPerPage = 5 + const [token, setToken] = useState(''); + + const location = useLocation(); + + useEffect(() => { + if (location.search) { + let path = location.search.replace('?', '').split('&'); + let obj = {} + path.forEach(value => { + let arr = value.split('='); + obj[arr[0]] = arr[1] + }) + setToken(decodeURIComponent(obj.token)) + setToken((obj.token)) + } + }, [location]) + useEffect(() => { getData(1) }, []) @@ -32,6 +50,44 @@ export default function ListItem() { setShowModal(true) } + const onDelete = async (data) => { + let dataPost = { + obj_id: data._id, + id: data.id, + name: data.name, + birthday: data.birthday, + gender: data.gender, + is_deleted : 1 + } + try { + const result = await axios({ + method: 'POST', + url: `${HOST}/api/famous_persons/insert_or_update`, + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + // 'Authorization': token, + }, + data: dataPost, + }) + if (result.data.status === 10000) { + swal({ + icon: 'success', + title: 'Thành công', + text: 'Xoá thành công', + buttons: false, + }) + getData(activePage) + } else if (result.data.status === 10004) { + swal("Thất bại", "Xoá thất bại!", "error"); + } else { + swal("Thất bại", "Xoá thất bại!", "error"); + } + } catch (error) { + console.log(error); + } + } + const handlePageChange = (pageNumber) => { setActivePage(pageNumber) getData(pageNumber) @@ -83,8 +139,7 @@ export default function ListItem() { var listImg if (value.sample_images.length > 0) { - listImg = {value.sample_images.length 0 ? value.sample_images[0] : ""} src={`${value.sample_images.length > 0 && value.image_host + value.sample_images[1]}`} /> - + listImg = {value.sample_images.length 0 ? value.sample_images[0] : ""} src={`${value.sample_images.length > 0 && value.image_host + value.sample_images[0]}`} /> } else { listImg = } /> } @@ -105,6 +160,21 @@ export default function ListItem() { diff --git a/src/components/Login/Login.js b/src/components/Login/Login.js new file mode 100644 index 0000000..c18c8ac --- /dev/null +++ b/src/components/Login/Login.js @@ -0,0 +1,128 @@ +import { LockOutlined, UserOutlined } from '@ant-design/icons'; +import { Button, Form, Input, Typography } from 'antd'; +import axios from 'axios'; +// import { dispatchBox, dispatchToken } from 'features/Login/loginSlice'; +import React, { useState } from 'react'; +// import { useDispatch } from 'react-redux'; +// import { useHistory } from "react-router-dom"; +import { HOST } from '../../config/index'; + +const { Text } = Typography; + +const Login = () => { + + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + + // const dispatch = useDispatch(); + + // let history = useHistory(); + + const onFinish = async (values) => { + setLoading(true); + const dataReq = { + email: values.email, + password: values.password, + } + + await axios({ + method: 'POST', + url: `${HOST}/api/login`, + headers: {}, + data: dataReq + }).then((res) => { + if (res.data.status === 10000) { + // dispatch(dispatchToken({ token: 'Bearer ' + res.data.access_token })); + // dispatch(dispatchBox({ rule: res.data.user_info.rule })) + setError(''); + localStorage.setItem('refresh_token', 'Bearer ' + res.data.refresh_token); + localStorage.setItem('access_token', 'Bearer ' + res.data.access_token); + localStorage.setItem('rule', res.data.user_info.rule); + + // history.push('/preferential-management') + } else { + setError('Sai tài khoản hoặc mật khẩu'); + } + }) + .catch(err => { + setError('Vui lòng kiểm tra lại đường truyền mạng') + console.error(err); + }) + setLoading(false); + }; + + const onFinishFailed = (errorInfo) => { + console.log('Failed:', errorInfo); + }; + + + return ( +
+
+
+

Đăng nhập

+
+ + } + placeholder="Email" /> + + + } + type="password" + placeholder="Password" + /> + +
+ {error && * Sai tài khoản mật khẩu } +
+ + + +
+
+ +
+ +
+ ); +} + +export default Login; \ No newline at end of file diff --git a/src/components/Modal/ModalEdit.js b/src/components/Modal/ModalEdit.js index 5be2c91..6f329a3 100644 --- a/src/components/Modal/ModalEdit.js +++ b/src/components/Modal/ModalEdit.js @@ -1,5 +1,6 @@ import { UploadOutlined, UserOutlined } from '@ant-design/icons'; -import { Avatar, Button as ButtonAntd, DatePicker, Form, Input, Radio } from 'antd'; +import { Avatar, Button as ButtonAntd, DatePicker, Form, Input, Radio, Upload } from 'antd'; +import { useLocation } from 'react-router-dom'; import viVN from 'antd/lib/locale/vi_VN'; import axios from 'axios'; import moment from 'moment'; @@ -26,10 +27,15 @@ const Modaledit = (props) => { const [crrIdx, setCrrIdx] = useState(0) const [listChecked, setListChecked] = useState({ url: [] }); + const [disableBtn, setDisableBtn] = useState(true); + + const [listImgUpload, setListImgUpload] = useState([]); + useEffect(() => { setCrrData(data); setCrrImages(data.sample_images) setBirthday(data.birthday !== "" ? moment(data.birthday) : null) + setDisableBtn(data._id ? false : true) return () => { setCrrData(null); } @@ -51,33 +57,6 @@ const Modaledit = (props) => { } const click_handle = async () => { - // axios.all([ - // axios.post(`${HOST}/api/famous_persons/insert_or_update`, { - // myVar: 'myValue' - // }), - // axios.post(`/my-url2`, { - // myVar: 'myValue' - // }) - // ]) - // .then(axios.spread((data1, data2) => { - // // output of req. - // console.log('data1', data1, 'data2', data2) - // })); - - // Promise.all([ - // fetch('https://api.github.com/users/MaksymRudnyi'), - // fetch('https://api.github.com/users/taylorotwell') - // ]) - // .then(async([res1, res2]) => { - // const a = await res1.json(); - // const b = await res2.json(); - // console.log(a.login + ' has ' + a.public_repos + ' public repos on GitHub'); - // console.log(b.login + ' has ' + b.public_repos + ' public repos on GitHub'); - // }) - // .catch(error => { - // console.log(error); - // }); - let dataPost = { obj_id: crrData._id ? crrData._id : "", id: crrData.id, @@ -96,13 +75,22 @@ const Modaledit = (props) => { data: dataPost, }) if (result.data.status === 10000) { - swal({ - icon: 'success', - title: 'Thành công', - text: 'Sửa thông tin thành công', - buttons: false, - }) - return onHide(); + if (crrData._id) { + swal({ + icon: 'success', + title: 'Thành công', + text: 'Sửa thông tin thành công', + buttons: false, + }) + } else { + swal({ + icon: 'success', + title: 'Thành công', + text: 'Thêm mới thành công', + buttons: false, + }) + setDisableBtn(false) + } } else if (result.data.status === 10004) { swal("Thất bại", "Sửa thông tin thất bại!", "error"); } else { @@ -111,7 +99,6 @@ const Modaledit = (props) => { } - const bulletedImg = crrImages.map((value, index) => { let renderImg = value.includes("data:image") ? value : data.image_host + value return ( @@ -155,6 +142,71 @@ const Modaledit = (props) => { } } + const handleChange = (event) => { + + } + + const onUpload = async (file) => { + console.log(file) + const base64 = await convertBase64(file) + + let dataUploadImg = { + obj_id: crrData._id ? crrData._id : "", + base64_image_list: [base64.split(',')[1]] + } + + fetch(`${HOST}/api/face_images/famous_person`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + // 'Authorization': token + }, + body: JSON.stringify(dataUploadImg) + }) + .then(res => res.json()) + .then(data => { + console.log(data) + if (data.status === 10000) { + let listImg = [...crrImages] + listImg.unshift(data.data.toString()) + setCrrImages(listImg) + setCheckDeleteMulti(false) + } + }) + } + + const uploadImage = async (options) => { + const { file } = options; + const base64 = await convertBase64(file) + + let dataUploadImg = { + obj_id: crrData._id ? crrData._id : "", + base64_image_list: [base64.split(',')[1]] + } + + fetch(`${HOST}/api/face_images/famous_person`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + // 'Authorization': token + }, + body: JSON.stringify(dataUploadImg) + }) + .then(res => res.json()) + .then(data => { + if (data.status === 10000) { + let listImg = [...crrImages] + listImg.unshift(data.data.toString()) + setCrrImages(listImg) + setCheckDeleteMulti(false) + } + }) + }; + + + const convertBase64 = (file) => { return new Promise((resolve, reject) => { const fileReader = new FileReader(); @@ -170,10 +222,26 @@ const Modaledit = (props) => { const deleteFaceMulti = () => { - let arrImgDel = listChecked.url - let listDetele = crrImages.filter(item => !arrImgDel.includes(item)); - setCrrImages(listDetele) - setCheckDeleteMulti(false) + let listDelImg = listChecked.url + fetch(`${HOST}/api/face_images/ignore_face`, { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + // 'Authorization': token + }, + body: JSON.stringify({ + person_obj_id: crrData._id ? crrData._id : "", + images: listDelImg + }) + }) + .then(res => res.json()) + .then(data => { + if (data.status === 10000) { + let listDetele = crrImages.filter(item => !listDelImg.includes(item)); + setCrrImages(listDetele) + } + }) } const UserHandle = (e) => { @@ -268,20 +336,18 @@ const Modaledit = (props) => { } - onChangeFile(e)} - type="file" - accept="image/*" - style={{ display: "none" }} - // multiple={false} - /> - onClickUpload()} icon={}>Tải ảnh lên - + + }>Tải ảnh lên + -
+
{
+
+ +
} - + {/* - + */} ); } diff --git a/yarn.lock b/yarn.lock index a7c9d43..2fa8564 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1049,7 +1049,7 @@ core-js-pure "^3.19.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.4.2", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.16.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz" integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== @@ -4998,6 +4998,13 @@ he@^1.2.0: resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +history@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/history/-/history-5.2.0.tgz#7cdd31cf9bac3c5d31f09c231c9928fad0007b7c" + integrity sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig== + dependencies: + "@babel/runtime" "^7.7.6" + hoist-non-react-statics@^3.3.1: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" @@ -8326,6 +8333,21 @@ react-refresh@^0.11.0: resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== +react-router-dom@^6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.2.1.tgz#32ec81829152fbb8a7b045bf593a22eadf019bec" + integrity sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA== + dependencies: + history "^5.2.0" + react-router "6.2.1" + +react-router@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.2.1.tgz#be2a97a6006ce1d9123c28934e604faef51448a3" + integrity sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg== + dependencies: + history "^5.2.0" + react-scripts@5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.0.tgz"