feat(): restructure

This commit is contained in:
bi
2022-01-08 20:02:01 +07:00
parent e8bc7e463a
commit f6b4257ac7
176 changed files with 26 additions and 0 deletions

278
app/src/App.css Normal file
View File

@@ -0,0 +1,278 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.modal-size {
min-width: 50%
}
.modal-size-res {
min-width: 40%
}
.ant-upload-list-picture-card-container, .ant-upload.ant-upload-select-picture-card{
width: 95px!important;
height: 95px!important;
}
/* custome andt upload */
.ant-upload-list-item-actions .anticon-eye,.ant-upload-list-item-actions .ant-upload-list-item-card-actions-btn{
display: none;
}
.ant-upload-list-item-info {
cursor: pointer;
}
.ant-upload-list-item.ant-upload-list-item-done.ant-upload-list-item-list-type-picture-card, .ant-upload-list-item.ant-upload-list-item-error.ant-upload-list-item-list-type-picture-card {
padding: 0
}
.boder-right {
border-right: 1px solid rgb(242, 242, 242);
}
img.opacity_img_click {
opacity: 0.6;
}
img.opacity_img_click.active {
opacity: 1;
}
.img_check {
width: 100%;
height: 80px;
background-size: contain;
cursor: pointer;
}
.img_check_training {
width: 120px;
max-width: 100%;
height: 120px;
background-size: contain;
cursor: pointer;
}
.img-checkbox>span {
background: 0 0;
position: absolute;
top: 0px;
left: 0px;
right: 0;
bottom: 0;
margin: auto;
height: 100%;
width: 100%;
background-color: #9a9caf8c;
}
.img-checkbox>span:after {
border: solid #00c5dc;
}
.img-checkbox>span {
border: none;
}
.img-checkbox>input:checked~span {
border: none;
background-color: transparent;
}
.img-checkbox>span:after {
top: 50%;
left: 50%;
margin-left: 0px;
margin-top: -20px;
width: 15px;
height: 25px;
border-width: 0 2px 2px 0!important;
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
}
/* pagination */
.pagination li a {
border-radius: 50%;
cursor: pointer;
display: inline-block;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-moz-justify-content: center;
-ms-justify-content: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-moz-align-items: center;
-ms-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
text-align: center;
vertical-align: middle;
height: 2.25rem;
min-width: 2.25rem;
vertical-align: middle;
padding: 0.5rem;
text-align: center;
position: relative;
font-size: 1rem;
line-height: 1rem;
font-weight: 400;
border: 0;
}
.pagination li.active a {
z-index: 1;
color: #fff;
background-color: #5867dd;
border-color: #5867dd;
}
.pagination li {
margin-left: 0.4rem;
}
.pagination li.disabled a {
color: #6c757d;
pointer-events: none;
cursor: auto;
background-color: #fff;
border-color: #dee2e6;
}
.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;
}

23
app/src/App.js Normal file
View File

@@ -0,0 +1,23 @@
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 (
<div className="Container">
<Router>
<Routes>
<Route path="/" element={<Navigate replace to="/list-famous" />} />
<Route path='/login' element={<Login />} />
<Route path="/list-famous" element={<ListItem />} />
</Routes>
</Router>
</div>
);
}
export default App;

View File

@@ -0,0 +1,311 @@
import { UserOutlined } from '@ant-design/icons';
import { Avatar, Tooltip } from 'antd';
import axios from 'axios';
import Modaledit from 'components/Modal/ModalEdit';
import momment from 'moment';
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() {
const [data, setData] = useState(null)
const [showModal, setShowModal] = useState(false)
const [dataEdit, setDataEdit] = useState(null)
const [loading, setLoading] = useState(true)
const [dataSearch, setDataSearch] = useState("")
const [activePage, setActivePage] = useState(1)
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)
}, [])
const onClickEdit = (data) => {
setDataEdit(data)
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)
}
const getData = async (page) => {
try {
const result = await axios({
method: 'POST',
url: `${HOST}/api/famous_persons/search`,
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
// 'Authorization': token
},
data: {
index: page,
item_per_page: itemsPerPage,
search_data: dataSearch
},
})
if (result.data.status === 10000) {
setData(result.data.data)
setTotalItems(result.data.count)
setLoading(false)
}
} catch (error) {
console.log(error);
}
}
const handleOnKeyDown = e => {
if (e.key === 'Enter') {
getData(1);
}
}
const tableRows = (dataAll) => dataAll.map((value, index) => {
var gender = ""
if (value.gender === "1") {
gender = "Nam"
} else if (value.gender === "2") {
gender = "Nữ"
} else if (value.gender === "3") {
gender = "Giới tính khác"
} else {
gender = ""
}
var listImg
if (value.sample_images.length > 0) {
listImg = <img key={index} style={{ width: "80px", height: "80px" }} alt={value.sample_images.length > 0 ? value.sample_images[0] : ""} src={`${value.sample_images.length > 0 && value.image_host + value.sample_images[0]}`} />
} else {
listImg = <Avatar shape="square" size={80} icon={<UserOutlined />} />
}
return (
<tr key={index}>
<td>{listImg}</td>
<td>{value.name}</td>
<td>{gender}</td>
<td>{value.birthday !== "" ? momment(value.birthday).format("DD-MM-YYYY") : ""}</td>
<td>
<Tooltip placement="top" title={"Sửa"}>
<button
onClick={() => onClickEdit(value)}
className="m-portlet__nav-link btn m-btn m-btn--hover-warning m-btn--icon m-btn--icon-only m-btn--pill" data-tip data-for="Edit" >
<i className="la la-edit" />
</button>
</Tooltip>
<Tooltip placement="top" title={"Xoá"}>
<button
onClick={f => {
f.preventDefault();
swal({
// title: "Are you sure?",
text: "Bạn có chắc muốn xoá " + value.name,
icon: "warning",
buttons: true,
dangerMode: true,
})
.then(willDelete => {
if (willDelete) {
onDelete(value);
}
});
}}
className="m-portlet__nav-link btn m-btn m-btn--hover-danger m-btn--icon m-btn--icon-only m-btn--pill" data-tip data-for="Edit" >
<i className="la la-trash" />
</button>
</Tooltip>
</td>
</tr>
)
})
const onCloseModal = () => {
setShowModal(false)
getData(1)
}
const reset = () => {
setDataSearch("")
getData(1)
}
if (!data) return <></>
return (
<div>
{showModal && <Modaledit data={dataEdit} onHide={onCloseModal} show={showModal} />}
<div className="m-portlet m-portlet--mobile pb-3">
<div className="m-portlet__head">
<div className="m-portlet__head-caption">
<div className="m-portlet__head-title">
<h3 className="m-portlet__head-text">
Danh sách người nổi tiếng
</h3>
</div>
</div>
</div>
<div className="m-portlet__body pt-2">
<div className="row pl-4">
<div className="row p-3 col-xl-11">
<div className="col-lg-2 p-0 m--margin-bottom-10-tablet-and-mobile">
<input type="text"
onKeyPress={(e) => handleOnKeyDown(e)}
onChange={(e) => {
setDataSearch(e.target.value)
}}
value={dataSearch}
id="inputSearch" className="form-control m-input"
placeholder="Họ tên..."
data-col-index={0}
/>
</div>
<div className="pl-3">
<button
onClick={() => {
getData(1)
}}
className="btn btn-accent m-btn m-btn--icon" id="m_search">
<span>
<i className="la la-search" />
<span>Tìm kiếm</span>
</span>
</button>
</div>
<div className="pl-3">
<button
onClick={() => {
reset()
}}
className="btn btn-secondary m-btn m-btn--icon" id="m_reset">
<span>
<i className="la la-rotate-left" />
<span>Tải lại</span>
</span>
</button>
</div>
</div>
<div className="m-portlet__head-tools col-xl-1 d-flex align-items-center">
<button
onClick={(e) => {
let dataEdit = {
obj_id: "",
id: "",
name: "",
birthday: "",
gender: "",
sample_images: [],
image_host: ""
};
setDataEdit(dataEdit)
setShowModal(true)
}}
className="btn btn-accent m-btn m-btn--custom m-btn--icon m-btn--pill m-btn--air">
<span>
<i className="la la-plus" />
<span>Thêm mới</span>
</span>
</button>
</div>
</div>
{/*begin: Datatable */}
<table className="table table-striped- table-bordered table-hover table-checkable" id="m_table_1">
<thead>
<tr>
<th>Ảnh</th>
<th>Họ tên</th>
<th>Giới tính</th>
<th>Ngày sinh</th>
<th>Thao tác</th>
</tr>
</thead>
<tbody>{tableRows(data)}</tbody>
</table>
<PulseLoader
// css={override}
sizeUnit={"px"}
size={12}
margin={'2px'}
color={'#36D7B7'}
loading={loading}
/>
<Pagination
prevPageText='Trang trước'
nextPageText='Trang sau'
firstPageText='Trang đầu'
lastPageText='Trang cuối'
activePage={activePage}
itemsCountPerPage={itemsPerPage}
totalItemsCount={totalItems}
pageRangeDisplayed={5}
onChange={handlePageChange}
/>
</div>
</div>
</div>
)
}

View File

@@ -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 (
<div className="login__page">
<div className="login__form">
<div className="login__sec">
<h2 className="login__text">Đăng nhập</h2>
<Form
layout="vertical"
name="normal_login"
className="login-form"
initialValues={{
remember: true,
}}
onFinish={onFinish}
onFinishFailed={onFinishFailed}
>
<Form.Item
name="email"
label="Email"
rules={[
{
required: true,
message: '',
},
]}
>
<Input
className="input__Cus"
prefix={<UserOutlined className="site-form-item-icon" />}
placeholder="Email" />
</Form.Item>
<Form.Item
name="password"
label="Mật khẩu"
rules={[
{
required: true,
message: '',
},
]}
>
<Input.Password
className="input__Cus"
prefix={<LockOutlined className="site-form-item-icon" />}
type="password"
placeholder="Password"
/>
</Form.Item>
<div className="login__error">
{error && <Text strong="true" className="hide__error" type="danger">* Sai tài khoản mật khẩu </Text>}
</div>
<Form.Item>
<Button
type="primary"
htmlType="submit"
loading={loading}
className="login-form-button"
>
Đăng nhập
</Button>
</Form.Item>
</Form>
</div>
</div>
</div>
);
}
export default Login;

View File

@@ -0,0 +1,414 @@
import { UploadOutlined, UserOutlined } from '@ant-design/icons';
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';
import 'moment/locale/vi';
import React, { useEffect, useRef, useState } from 'react';
import { Button, Modal } from 'react-bootstrap';
import swal from 'sweetalert';
import { HOST } from '../../config/index';
const Modaledit = (props) => {
const { show, onHide, data } = props;
const [crrImages, setCrrImages] = useState([]);
const [form] = Form.useForm()
const fileInput = useRef(null);
const [birthday, setBirthday] = useState(moment())
const [crrData, setCrrData] = useState(null);
const [checkDeleteMulti, setCheckDeleteMulti] = useState(false);
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);
}
}, [data]);
const handleCheckedImg = (event, value) => {
let newListChecked = { ...listChecked }
if (newListChecked.url.indexOf(event.target.value) === -1) {
newListChecked.url.push(event.target.value)
} else {
var i = newListChecked.url.indexOf(event.target.value);
if (i !== -1) {
newListChecked.url.splice(i, 1);
}
}
setListChecked(newListChecked)
}
const click_handle = async () => {
let dataPost = {
obj_id: crrData._id ? crrData._id : "",
id: crrData.id,
name: crrData.name,
birthday: crrData.birthday,
gender: crrData.gender ? crrData.gender : ""
}
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) {
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 {
swal("Thất bại", "Sửa thông tin thất bại!", "error");
}
}
const bulletedImg = crrImages.map((value, index) => {
let renderImg = value.includes("data:image") ? value : data.image_host + value
return (
<div className="img-res col-md-4 pl-0 mb-3" key={index + 1}>
<div style={{ 'height': 80 }}>
{
checkDeleteMulti === false ?
<img alt="" src={renderImg} onClick={() => { setCrrIdx(index) }} className={"opacity_img_click img_check " + (crrIdx === index ? 'active' : '')} />
:
<label className="m-checkbox img-checkbox m-checkbox-day pl-0 mb-0" style={{ 'width': '100%' }}>
<input type="checkbox"
defaultValue={value}
name="img_checked"
checked={listChecked.url.indexOf(value) === -1 ? false : true}
onClick={e => {
handleCheckedImg(e, value)
}} readOnly />
<img alt="" src={renderImg} className="opacity_img img_check" />
<span />
</label>
}
</div>
</div>
)
})
const onChangeFile = async (event) => {
const file = event.target.files[0]
const base64 = await convertBase64(file)
let imgUplpad = [...crrImages]
imgUplpad.push(base64)
setCrrImages(imgUplpad)
setCheckDeleteMulti(false)
}
const onClickUpload = () => {
if (crrImages.length >= 3) {
swal("Cảnh báo", "Số lượng ảnh tải lên tối đa là 3", "warning")
} else {
fileInput.current.click()
}
}
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();
fileReader.readAsDataURL(file)
fileReader.onload = () => {
resolve(fileReader.result);
}
fileReader.onerror = (error) => {
reject(error);
}
})
}
const deleteFaceMulti = () => {
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) => {
setCrrData({ ...crrData, [e.target.name]: e.target.value })
}
const onChangeBirthday = (date) => {
setBirthday(date)
setCrrData({ ...crrData, birthday: moment(date).format("YYYY-MM-DD") })
}
if (!crrData) return <></>;
return (
<Modal
{...props}
animation={false}
size="lg"
// keyboard={false}
dialogClassName={`${window.innerWidth >= 1920 ? "modal-size-res" : "modal-size"}`}
aria-labelledby="contained-modal-title-vcenter"
>
<Modal.Header closeButton>
<Modal.Title id="contained-modal-title-vcenter">{crrData._id ? "Sửa thông tin" : "Thêm mới"}
</Modal.Title>
</Modal.Header>
{crrData &&
<Modal.Body>
<div id="formUpdateMeeting">
<div className="m-widget14 p-0">
<div className="row">
<div className="col-md-7 boder-right d-flex flex-column justify-content-between ">
<div className="row justify-content-center pb-3">
{
crrImages[crrIdx] ?
<Avatar shape="square" src={crrImages[crrIdx].includes("data:image") ? crrImages[crrIdx] : data.image_host + crrImages[crrIdx]} size={180} icon={<UserOutlined />} /> :
<Avatar shape="square" size={180} icon={<UserOutlined />} />
}
</div>
<div className="row mr-4 ml-4">
{
crrImages.length > 0
?
<div className="col-md-12 mt-2 mb-2 p-0">
<label className="m-checkbox m-checkbox--success mt-3 ml-1">
<input type="checkbox"
onClick={(e) => {
setCheckDeleteMulti(!checkDeleteMulti)
setListChecked({
url: [],
})
}}
checked={checkDeleteMulti} /> <div style={{ 'paddingTop': '2px' }}>Chọn</div>
<span />
</label>
{
checkDeleteMulti === true
?
<div style={{ 'display': 'inline-flex' }} className="pull-right">
<button
onClick={(e) => {
e.preventDefault();
if (listChecked.url.length > 0) {
swal({
title: "Bạn chắc chắn muốn xóa ảnh này",
icon: "warning",
buttons: ["Huỷ", "Oke!"],
dangerMode: false,
})
.then((willDelete) => {
if (!willDelete) throw null;
deleteFaceMulti();
})
} else {
swal("Cảnh báo!", "Bạn chưa chọn ảnh để xóa", "warning");
}
}}
className="btn btn-sm btn-danger width-mobie--100 m-loader--light m-loader--right" id="btn_deleteMulti">
Xóa ảnh đã chọn
</button>
</div>
:
""
}
</div>
:
""
}
<div className="row col-md-12 pr-0 image_capture_small pull-left">
{
bulletedImg
}
</div>
<Upload
customRequest = {uploadImage}
accept="image/*"
showUploadList={false}
disabled={disableBtn}
>
<ButtonAntd className={`${disableBtn ? "disable-btn-upload" : ""}`} icon={<UploadOutlined />}>Tải ảnh lên</ButtonAntd>
</Upload>
</div>
</div>
<div className="col-md-5 d-flex flex-column justify-content-between">
<Form
id="formEdit"
form={form}
layout="vertical"
onFinish={() => click_handle()}
// onFinishFailed={onFinishFailed}
autoComplete="off"
initialValues={{
name: crrData.name
}}
>
<Form.Item
label="Họ tên"
name="name"
rules={[{ required: true, message: '' }]}
>
<Input placeholder="Họ tên" value={crrData.name} onChange={e => UserHandle(e)} name='name' />
</Form.Item>
<Form.Item name="radio-group" label="Giới tính">
<Radio.Group name="gender" onChange={e => UserHandle(e)} defaultValue={crrData.gender}>
<Radio name="gender" value={"1"}>Nam</Radio>
<Radio name="gender" value={"2"}>Nữ</Radio>
<Radio name="gender" value={"3"}>Giới tính khác</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="Ngày sinh"
name="birthday"
>
<DatePicker className="form-control m-input"
locale={viVN}
defaultValue={birthday !== null ? moment(birthday, 'DD-MM-YYYY') : null}
format={'DD-MM-YYYY'}
onChange={e => onChangeBirthday(e)}
placeholder="Ngày sinh"
/>
</Form.Item>
</Form>
<div className="row d-flex justify-content-end mr-1">
<Button variant="accent" className={"m-loader--light m-loader--right "}
// disabled={loading}
onClick={() => form.submit()}
>Lưu</Button>
</div>
</div>
</div>
</div>
</div>
</Modal.Body>
}
{/* <Modal.Footer>
<Button variant="accent" className={"m-loader--light m-loader--right "}
// disabled={loading}
onClick={() => form.submit()}
>Lưu</Button>
<Button variant="secondary" onClick={onHide}>Đóng</Button>
</Modal.Footer> */}
</Modal>
);
}
export default Modaledit;

7
app/src/config/index.js Normal file
View File

@@ -0,0 +1,7 @@
var _HOST = ""
_HOST = process.env.REACT_APP_HOST
export const HOST = _HOST

13
app/src/index.css Normal file
View File

@@ -0,0 +1,13 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}

22
app/src/index.js Normal file
View File

@@ -0,0 +1,22 @@
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ConfigProvider } from 'antd';
import 'moment/locale/vi';
import viVN from 'antd/lib/locale/vi_VN';
ReactDOM.render(
<React.StrictMode>
<ConfigProvider locale={viVN}>
<App />
</ConfigProvider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

View File

@@ -0,0 +1,13 @@
const reportWebVitals = onPerfEntry => {
if (onPerfEntry && onPerfEntry instanceof Function) {
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
});
}
};
export default reportWebVitals;