Bladeren bron

public user search and view

master
Anna Ho 2 jaren geleden
bovenliggende
commit
66d5e59cbb
9 gewijzigde bestanden met toevoegingen van 309 en 156 verwijderingen
  1. +4
    -5
      src/pages/pnspsUserDetailPage_Individual/UserInformationCard_Individual.js
  2. +99
    -42
      src/pages/pnspsUserDetailPage_Organization/UserInformationCard_Organization.js
  3. +1
    -1
      src/pages/pnspsUserDetailPage_Organization/index.js
  4. +20
    -19
      src/pages/pnspsUserSearchPage_Individual/UserSearchForm_Individual.js
  5. +70
    -22
      src/pages/pnspsUserSearchPage_Individual/UserTable_Individual.js
  6. +41
    -18
      src/pages/pnspsUserSearchPage_Organization/UserSearchForm_Organization.js
  7. +68
    -22
      src/pages/pnspsUserSearchPage_Organization/UserTable_Organization.js
  8. +1
    -26
      src/utils/ComboData.js
  9. +5
    -1
      src/utils/IconUtils.js

+ 4
- 5
src/pages/pnspsUserDetailPage_Individual/UserInformationCard_Individual.js Bestand weergeven

@@ -79,6 +79,8 @@ const UserInformationCard_Individual = ({userData,userFile, loadDataFun}) => {
userData["faxNumber"] = userData.faxNo?.faxNumber;
userData["fax_countryCode"] = userData.faxNo?.countryCode;

userData["lastLoginDate"] = userData.lastLogin?DateUtils.datetimeStr(userData.lastLogin):"";

setIdDocType(userData.idDocType);
setDistrict(userData.district);
setCountry(userData.country);
@@ -645,11 +647,8 @@ const UserInformationCard_Individual = ({userData,userFile, loadDataFun}) => {

<TextField
fullWidth
{...register("lastLogin",
{
value: currentUserData?.lastLogin,
})}
id='lastLogin'
{...register("lastLoginDate")}
id='lastLoginDate'
disabled
>
</TextField>


+ 99
- 42
src/pages/pnspsUserDetailPage_Organization/UserInformationCard_Organization.js Bestand weergeven

@@ -1,6 +1,7 @@
// material-ui
import {
Grid, TextField, Typography, Button
Grid, TextField, Typography, Button,
OutlinedInput, FormHelperText
} from '@mui/material';
import MainCard from "../../components/MainCard";
import * as React from "react";
@@ -10,6 +11,9 @@ import * as DateUtils from '../../utils/DateUtils';
import * as HttpUtils from '../../utils/HttpUtils';
import * as UrlUtils from "../../utils/ApiPathConst";

import { useFormik,FormikProvider } from 'formik';
import * as yup from 'yup';

// ==============================|| DASHBOARD - DEFAULT ||============================== //


@@ -19,10 +23,33 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
const [editMode, setEditMode] = useState(false);
const [locked, setLocked] = useState(false);


const from = useForm({defaultValues: userData});
const {register,reset, handleSubmit} = from;

const formik = useFormik({
initialValues:(currentUserData),
validationSchema:yup.object().shape({
enName: yup.string().max(255).required('請輸入英文姓名'),
enCompanyName: yup.string().max(255).required('請輸入英文名稱'),
chName: yup.string().max(255).required('請輸入中文姓名'),
address1: yup.string().max(255).required('請輸入第一行地址'),
address2: yup.string().max(255).required('請輸入第二行地址'),
address3: yup.string().max(255).required('請輸入第三行地址'),
email: yup.string().email('請輸入電郵格式').max(255).required('請輸入電郵'),
emailConfirm: yup.string().email('請輸入電郵格式').max(255).required('請輸入電郵').oneOf([yup.ref('email'), null], '請輸入相同電郵'),
phoneCountryCode: yup.string().min(3,'請輸入3位數字').required('請輸入國際區號'),
faxCountryCode: yup.string().min(3,'請輸入3位數字'),
phone: yup.string().min(8,'請輸入8位數字').required('請輸入聯絡電話'),
fax: yup.string().min(8,'請輸入8位數字'),
brExpiryDate: yup.string().min(8,'請輸入商業登記證有效日期'),
brNo: yup.string().min(8,'請輸入商業登記證號碼'),
})
});


useEffect(() => {
let createDate = DateUtils.datetimeStr(userData.created);
let modifiedBy = DateUtils.datetimeStr(userData.modified)+", "+userData.modifiedBy;
@@ -51,29 +78,36 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
}, [currentUserData]);


function onSubmitForm(_formData) {

function onSubmitForm() {
HttpUtils.post({
url: UrlUtils.POST_IND_USER+"/"+userData.id,
params: {
chName: _formData.chName,
enName: _formData.enName,
chName: formik.values.chName,
enName: formik.values.enName,
contactTel: {
countryCode: _formData.tel_countryCode,
phoneNumber: _formData.phoneNumber
countryCode: formik.values.tel_countryCode,
phoneNumber: formik.values.phoneNumber
},
faxNo: {
countryCode: _formData.fax_countryCode,
faxNumber: _formData.faxNumber
countryCode: formik.values.fax_countryCode,
faxNumber: formik.values.faxNumber
},
addressBus: {
country: _formData.country,
district: _formData.district,
addressLine1: _formData.addressLine1,
addressLine2: _formData.addressLine2,
addressLine3: _formData.addressLine3,
country: formik.values.country,
district: formik.values.district,
addressLine1: formik.values.addressLine1,
addressLine2: formik.values.addressLine2,
addressLine3: formik.values.addressLine3,
},
identification: _formData.identification,
emailBus:_formData.emailBus,
identification: formik.values.identification,
emailBus:formik.values.emailBus,
contactPerson: formik.values.contactPerson,
enCompanyName: formik.values.enCompanyName,
chCompanyName: formik.values.chCompanyName,
brNo: formik.values.brNo,
brExpiryDate: formik.values.brExpiryDate,

},
onSuccess: function(){
loadDataFun();
@@ -89,6 +123,15 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
window.open("/org/fromUser/"+userData.id, "_blank", "noreferrer");
};

const onVerifiedClick = () => {
HttpUtils.get({
url: UrlUtils.GET_IND_USER_VERIFY+"/"+userData.id,
onSuccess: function(){
loadDataFun();
}
});
};

const doLock = () => {
HttpUtils.get({
url: UrlUtils.GET_USER_LOCK+"/"+userData.id,
@@ -107,6 +150,7 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
});
};

return (
<MainCard elevation={0}
border={false}
@@ -116,6 +160,7 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
Information
</Typography>

<FormikProvider value={formik}>
<form onSubmit={handleSubmit(onSubmitForm)}>

{/*top button*/}
@@ -302,10 +347,7 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {

<Grid item xs={7} s={7} md={7} lg={6} >
<TextField
{...register("identification",
{
value: currentUserData?.identification,
})}
{...register("identification")}
id='identification'
disabled={!editMode}
/>
@@ -325,8 +367,8 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
<Grid item xs={7} s={7} md={7} lg={6}>
<TextField
fullWidth
{...register("emailAddress")}
id='emailAddress'
{...register("emailBus")}
id='emailBus'
disabled={!editMode}
/>
</Grid>
@@ -341,8 +383,9 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
Verified:
</Grid>

{
currentUserData.verified || editMode?
currentUserData.verifiedBy || editMode?
<Grid item xs={6}>
<TextField
fullWidth
@@ -369,17 +412,20 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
textTransform: 'capitalize',
alignItems: 'end'
}}
onClick={onVerifiedClick}
>
Verify
</Button>
</Grid>
</>
}
</Grid>
</Grid>




<Grid item lg={4}>
<Grid container alignItems={"center"}>
<Grid item xs={4} s={4} md={4} lg={4}
@@ -522,7 +568,7 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
fullWidth
{...register("chCompanyName")}
id='chCompanyName'
disabled
disabled={!editMode}
/>
</Grid>
</Grid>
@@ -597,12 +643,30 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
</Grid>

<Grid item xs={7} s={7} md={7} lg={6}>
<TextField
fullWidth
{...register("brExpiryDateTemp")}
id='brExpiryDateTemp'
disabled={!editMode}
/>
<OutlinedInput
fullWidth
disabled={!editMode}
{...register("brExpiryDate")}
error={Boolean(formik.touched.brExpiryDate && formik.errors.brExpiryDate)}
id="brExpiryDate"
type="date"
value={formik.values.brExpiryDate}
name="brExpiryDate"
onChange={formik.handleChange}
placeholder="與與商業登記證相同如有"
inputProps={{
onKeyDown: (e) => {
if (e.key === 'Enter') {
e.preventDefault();
}
},
}}
/>
{formik.touched.brExpiryDate && formik.errors.brExpiryDate && (
<FormHelperText error id="brExpiryDate">
{formik.errors.brExpiryDate}
</FormHelperText>
)}
</Grid>
</Grid>
</Grid>
@@ -617,28 +681,19 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
<Grid item xs={7} s={7} md={12} lg={6}>
<TextField
fullWidth
{...register("addressLine1",
{
value: currentUserData?.address?.addressLine1,
})}
{...register("addressLine1")}
id='addressLine1'
disabled={!editMode}
/>
<TextField
fullWidth
{...register("addressLine2",
{
value: currentUserData?.address?.addressLine2,
})}
{...register("addressLine2")}
id='addressLine2'
disabled={!editMode}
/>
<TextField
fullWidth
{...register("addressLine3",
{
value: currentUserData?.address?.addressLine3,
})}
{...register("addressLine3")}
id='addressLine3'
disabled={!editMode}
/>
@@ -685,6 +740,8 @@ const UserInformationCard_Organization = ({userData, loadDataFun}) => {
</Grid>

</form>

</FormikProvider>
</MainCard>
);
};


+ 1
- 1
src/pages/pnspsUserDetailPage_Organization/index.js Bestand weergeven

@@ -30,7 +30,7 @@ const UserMaintainPage_Organization = () => {
HttpUtils.get({
url: `${UrlUtils.GET_ORG_USER_PATH}/${params.id}`,
onSuccess: function(response){
response.data["addressBus"] = JSON.parse(response.data["address"]);
response.data["addressBus"] = JSON.parse(response.data["addressBus"]);
response.data["contactTel"] = JSON.parse(response.data["contactTel"]);
response.data["faxNo"] = JSON.parse(response.data["faxNo"]);
setUserData(response.data)


+ 20
- 19
src/pages/pnspsUserSearchPage_Individual/UserSearchForm_Individual.js Bestand weergeven

@@ -1,15 +1,13 @@
// material-ui
import {
Button,
CardContent, FormControlLabel,
CardContent,
Grid, TextField,
Typography
Autocomplete
} from '@mui/material';
import MainCard from "../../components/MainCard";
import {useForm} from "react-hook-form";

import { useState} from "react";
import Checkbox from "@mui/material/Checkbox";
import * as React from "react";
// ==============================|| DASHBOARD - DEFAULT ||============================== //

@@ -17,8 +15,7 @@ import * as React from "react";
const UserSearchForm_Individual = ({applySearch}) => {

const [type, setType] = useState([]);
const [locked, setLocked] = useState(false);

const [accountFilter, setAccountFilter] = useState("Active");

const { reset, register, handleSubmit } = useForm()
const onSubmit = (data) => {
@@ -34,14 +31,13 @@ const UserSearchForm_Individual = ({applySearch}) => {
fullName: data.fullName,
email: data.email,
phone: data.phone,
locked: locked,
accountFilter: accountFilter,
};
applySearch(temp);
};

function resetForm(){
setType([]);
setLocked(false);
reset();
}

@@ -97,18 +93,23 @@ const UserSearchForm_Individual = ({applySearch}) => {
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>
<FormControlLabel
control={
<Checkbox
checked={locked}
onChange={(event) => setLocked(event.target.checked)}
name="checked"
color="primary"
size="small"
<Autocomplete
{...register("accountFilter")}
disablePortal
id="accountFilter"
options={["Active","Locked","Not verified"]}
value={accountFilter}
onChange={(event, newValue) => {
if (newValue !== null){
setAccountFilter(newValue);
}
}}
renderInput={(params) => (
<TextField {...params}
label="Status"
/>
)}
/>
}
label={<Typography variant="h6">Locked</Typography>}
/>
</Grid>
{/*<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>*/}
{/* <TextField*/}


+ 70
- 22
src/pages/pnspsUserSearchPage_Individual/UserTable_Individual.js Bestand weergeven

@@ -2,19 +2,19 @@
import * as React from 'react';
import {
DataGrid,
GridActionsCellItem,
GridActionsCellItem
} from "@mui/x-data-grid";
import VisibilityIcon from '@mui/icons-material/Visibility';
import HighlightOff from '@mui/icons-material/HighlightOff';
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
import {useEffect} from "react";
import {useNavigate} from "react-router-dom";
import { useTheme } from '@mui/material/styles';
import Checkbox from '@mui/material/Checkbox';
import * as DateUtils from "../../utils/DateUtils";
// ==============================|| EVENT TABLE ||============================== //

export default function UserTable_Individual({recordList}) {
const [rows, setRows] = React.useState(recordList);
const [rowModesModel] = React.useState({});
const theme = useTheme();

const navigate = useNavigate()

@@ -52,34 +52,82 @@ export default function UserTable_Individual({recordList}) {
flex: 1,
},
{
id: 'name',
field: 'name',
headerName: 'Full Name',
id: 'enName',
field: 'enName',
headerName: 'Name (Eng)',
flex: 1,
},
{
id: 'email',
field: 'email',
id: 'chName',
field: 'chName',
headerName: 'Name (Ch)',
flex: 1,
},
{
id: 'contactTel',
field: 'contactTel',
headerName: 'Tel.',
flex: 1,
valueGetter:(params)=>{
if(params.value){
let tel = JSON.parse(params.value);
return "+"+tel.countryCode+ " "+tel.phoneNumber;
}
}
},
{
id: 'emailAddress',
field: 'emailAddress',
headerName: 'Email',
flex: 1,
},
{
id: 'lastLogin',
field: 'lastLogin',
headerName: 'Last Login',
flex: 1,
valueGetter:(params)=>{
if(params.value){
return DateUtils.datetimeStr(params.value);
}
}
},
{
id: 'locked',
field: 'locked',
type: 'bool',
headerName: 'Locked',
headerName: 'Status',
flex: 1,
renderCell: (params) => {
return (
<Checkbox
theme={theme}
key="locked"
checked={params.row.locked}
color="primary"
size="small"
//onChange={handleChange}
/>
);
valueGetter:(params)=>{
if(params.value){
return "Locked";
}else{
return "Active";
}
}
},
{
field: 'verifiedDate',
type: 'actions',
headerName: 'Verified',
width: 100,
cellClassName: 'actions',
getActions: (params) => {
if(params.row.verifiedDate)
return [
<GridActionsCellItem
key=""
icon={<CheckCircleOutline/>}
color="success"
/>];
return [
<GridActionsCellItem
key=""
icon={<HighlightOff/>}
color="error"
/>];
},
},
];


+ 41
- 18
src/pages/pnspsUserSearchPage_Organization/UserSearchForm_Organization.js Bestand weergeven

@@ -1,15 +1,13 @@
// material-ui
import {
Button,
CardContent, FormControlLabel,
Grid, TextField,
Typography
CardContent, Autocomplete,
Grid, TextField
} from '@mui/material';
import MainCard from "../../components/MainCard";
import {useForm} from "react-hook-form";

import { useState} from "react";
import Checkbox from "@mui/material/Checkbox";
import * as React from "react";
// ==============================|| DASHBOARD - DEFAULT ||============================== //

@@ -17,7 +15,7 @@ import * as React from "react";
const UserSearchForm_Organization = ({applySearch}) => {

const [type, setType] = useState([]);
const [locked, setLocked] = useState(false);
const [accountFilter, setAccountFilter] = useState("Active");


const { reset, register, handleSubmit } = useForm()
@@ -34,14 +32,15 @@ const UserSearchForm_Organization = ({applySearch}) => {
fullName: data.fullName,
email: data.email,
phone: data.phone,
locked: locked,
brNoStr: data.brNoStr,
orgName: data.orgName,
accountFilter: accountFilter,
};
applySearch(temp);
};

function resetForm(){
setType([]);
setLocked(false);
reset();
}

@@ -60,6 +59,25 @@ const UserSearchForm_Organization = ({applySearch}) => {

{/*row 2*/}
<Grid container alignItems={"center"}>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>
<TextField
fullWidth
{...register("orgName")}
id="orgName"
label="Org Name"
/>
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>
<TextField
fullWidth
{...register("brNoStr")}
id="brNoStr"
label="BR No."
/>
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>
<TextField
fullWidth
@@ -97,18 +115,23 @@ const UserSearchForm_Organization = ({applySearch}) => {
</Grid>

<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>
<FormControlLabel
control={
<Checkbox
checked={locked}
onChange={(event) => setLocked(event.target.checked)}
name="checked"
color="primary"
size="small"
<Autocomplete
{...register("accountFilter")}
disablePortal
id="accountFilter"
options={["Active","Locked","Not verified"]}
value={accountFilter}
onChange={(event, newValue) => {
if (newValue !== null){
setAccountFilter(newValue);
}
}}
renderInput={(params) => (
<TextField {...params}
label="Status"
/>
)}
/>
}
label={<Typography variant="h6">Locked</Typography>}
/>
</Grid>
{/*<Grid item xs={9} s={6} md={5} lg={3} sx={{ml:3, mr:3, mb:3}}>*/}
{/* <TextField*/}


+ 68
- 22
src/pages/pnspsUserSearchPage_Organization/UserTable_Organization.js Bestand weergeven

@@ -7,14 +7,13 @@ import {
import VisibilityIcon from '@mui/icons-material/Visibility';
import {useEffect} from "react";
import {useNavigate} from "react-router-dom";
import { useTheme } from '@mui/material/styles';
import Checkbox from '@mui/material/Checkbox';
import HighlightOff from '@mui/icons-material/HighlightOff';
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
// ==============================|| EVENT TABLE ||============================== //

export default function UserTable_Organization({recordList}) {
const [rows, setRows] = React.useState(recordList);
const [rowModesModel] = React.useState({});
const theme = useTheme();

const navigate = useNavigate()

@@ -52,34 +51,81 @@ export default function UserTable_Organization({recordList}) {
flex: 1,
},
{
id: 'name',
field: 'name',
headerName: 'Full Name',
id: 'enName',
field: 'enName',
headerName: 'Name (Eng)',
flex: 1,
},
{
id: 'email',
field: 'email',
headerName: 'Email',
id: 'chName',
field: 'chName',
headerName: 'Name (Ch)',
flex: 1,
},
{
id: 'enCompanyName',
field: 'enCompanyName',
headerName: 'Company(Eng)',
flex: 1,
},
{
id: 'chCompanyName',
field: 'chCompanyName',
headerName: 'Company(Ch)',
flex: 1,
},
{
id: 'brNo',
field: 'brNo',
headerName: 'Br No',
flex: 1,
},
{
id: 'lastLogin',
field: 'lastLogin',
headerName: 'Last Login',
flex: 1,
valueGetter:(params)=>{
if(params.value){
return DateUtils.datetimeStr(params.value);
}
}
},
{
id: 'locked',
field: 'locked',
type: 'bool',
headerName: 'Locked',
headerName: 'Status',
flex: 1,
renderCell: (params) => {
return (
<Checkbox
theme={theme}
key="locked"
checked={params.row.locked}
color="primary"
size="small"
//onChange={handleChange}
/>
);
valueGetter:(params)=>{
if(params.value){
return "Locked";
}else{
return "Active";
}
}
},
{
field: 'verifiedDate',
type: 'actions',
headerName: 'Verified',
width: 100,
cellClassName: 'actions',
getActions: (params) => {
if(params.row.verifiedDate)
return [
<GridActionsCellItem
key=""
icon={<CheckCircleOutline/>}
color="success"
/>];
return [
<GridActionsCellItem
key=""
icon={<HighlightOff/>}
color="error"
/>];
},
},
];


+ 1
- 26
src/utils/ComboData.js Bestand weergeven

@@ -1,32 +1,7 @@
import * as yup from 'yup';

export const idDocType = ["passport","HKID","CNID","BR","otherCert"];
export const district = ["北區","長洲區","大埔區","大嶼山區","東區","觀塘區","黃大仙區","九龍城區","葵青區","南區","南丫島區",
"坪洲區","荃灣區","沙田區","深水埗區","屯門區","灣仔區","西貢區","油尖旺區","元朗區","中西區"];
export const country = ["香港","內地","澳門"];

export const validationSchema = ()=>{
return yup.object().shape({
username: yup.string().min(8,"用戶名稱最少8位").required('請輸入用戶名稱'),
password: yup.string().min(8,'請輸入最少8位密碼').required('請輸入密碼')
.matches(/^(?=.*[a-z])/, '請包括最少1個小寫字母')
.matches(/^(?=.*[A-Z])/, '請包括最少1個大寫字母')
.matches(/^(?=.*[0-9])/, '請包括最少1個數字')
.matches(/^(?=.*[!@#%&])/, '請包括最少1個特殊字符'),
confirmPassword: yup.string().min(8,'請最少輸入8位密碼').required('請確認密碼').oneOf([yup.ref('password'), null], '請輸入相同密碼'),
enName: yup.string().max(255).required('請輸入英文姓名'),
chName: yup.string().max(255).required('請輸入中文姓名'),
address1: yup.string().max(255).required('請輸入第一行地址'),
address2: yup.string().max(255).required('請輸入第二行地址'),
address3: yup.string().max(255).required('請輸入第三行地址'),
email: yup.string().email('請輸入電郵格式').max(255).required('請輸入電郵'),
emailConfirm: yup.string().email('請輸入電郵格式').max(255).required('請輸入電郵').oneOf([yup.ref('email'), null], '請輸入相同電郵'),
idNo: yup.string().min(7,"請輸入證件號碼").required('請輸入證件號碼'),
checkDigit:yup.string().max(1).required('請輸入括號內的數字或字母'),
idDocType: yup.string().max(255).required('請輸入證件類別'),
phoneCountryCode: yup.string().min(3,'請輸入3位數字').required('請輸入國際區號'),
faxCountryCode: yup.string().min(3,'請輸入3位數字'),
phone: yup.string().min(8,'請輸入8位數字').required('請輸入聯絡電話'),
fax: yup.string().min(8,'請輸入8位數字'),
});
}
export const accountFilter = [{display:"Active", value:"active"},{display:"Locked", value:"locked"},{display:"Not verified", value:"notVerified"}];

+ 5
- 1
src/utils/IconUtils.js Bestand weergeven

@@ -3,8 +3,12 @@ import DeleteIcon from '@mui/icons-material/DeleteForever';
import EditIcon from '@mui/icons-material/Edit';
import ViewIcon from '@mui/icons-material/Visibility';
import DownloadICon from '@mui/icons-material/Download';
import VaildIcon from '@mui/icons-material/CheckCircleOutline';
import InVaildIcon from '@mui/icons-material/HighlightOff';

export const Delete = DeleteIcon;
export const Edit = EditIcon;
export const View = ViewIcon;
export const Download = DownloadICon;
export const Download = DownloadICon;
export const Vaild = VaildIcon;
export const Invaild = InVaildIcon;

Laden…
Annuleren
Opslaan