Issue:
- Updating time from DateTimeRangePicker does not update instantly,
(ie. it flickers and returns to a previous value, multiple clicks are required to update time, the number of clicks required does not behave consistently)
Platform Tested On:
- Mozilla Firefox (Ubuntu 20.04.2 LTS)
Solutions Attempted:
- Tried to debounce by creating a useDebounce hook which added timeout of 10, 50, 500 ms before allowing next update. (No difference to outcome)
Potential Lead:
- Similar implementation seems to work on another screen without error.
- I suspect that something from the render order might be causing the issue. However, looking through the entire code, cant seem to find.
import moment from 'moment';
import React, { useEffect, useMemo, useState } from 'react';
import { Badge, Button, Col, Container, Form, Image, Row } from 'react-bootstrap';
import { DateRangePicker } from 'react-dates';
import DatetimeRangePicker from 'react-datetime-range-picker';
import { useDispatch, useSelector } from 'react-redux';
import Select from 'react-select';
import { PageTitle, ReactTable, SearchBar } from '../../../components';
import { getAttendanceActivity, getJobList, getLocations } from '../../../redux/actions';
import { convertAttendanceToLocalTimezone } from './service/convertAttendanceToLocalTimezone';
import useDebounce from '../../../hooks/useDebounceHook';
export default function EmployeeList() {
const dispatch = useDispatch();
const rsAttendance = useSelector(state => state.attendance.attendance);
const rsLocations = useSelector(state => state.locations.locations);
const rsJobs = useSelector(state => state.jobs.jobList);
const rsUserTimezone = useSelector(state => state.timezone.userTimezone)
const columns = useMemo(() => [
{
Header: '#',
accessor: 'row',
Cell: ({ row }) => row.index + 1
}
, {
Header: '',
accessor: 'avatar',
disableSortBy: true,
style: { width: '1%' },
Cell: ({ value }) => ( // { value } will be avatar image url
<Image
roundedCircle
className='shadow-sm'
width='40px'
src={require('../../../assets/images/avatar.svg')}
/>
)
}
, {
Header: 'Name',
accessor: 'email',
}
, {
Header: 'Location',
accessor: 'location_name',
Cell: ({ value }) => <Badge pill variant='info' className='location-badge'>{value}</Badge>
}
, {
Header: 'Job Name',
accessor: 'job_name',
Cell: ({ value }) => <Badge pill variant='info' className='location-badge'>{value}</Badge>
}
, {
Header: 'Date',
accessor: 'attendance_date',
Cell: ({ value }) => moment(value).format('DD MMM')
}
, {
Header: 'Time In',
accessor: 'first_check_in.attendance_check_in_time',
Cell: ({ value }) => value ? moment(value, 'HH:mm:ss').format('hh:mm A') : '-'
}
, {
Header: 'Time Out',
accessor: 'last_check_out.attendance_check_out_time',
Cell: ({ value }) => value ? moment(value, 'HH:mm:ss').format('hh:mm A') : '-'
}
, {
Header: 'Status',
accessor: 'status',
Cell: ({ value }) => {
if (value === 'on time') {
return <><span className='dot success'></span>On time</>
} else if (value === 'late') {
return <><span className='dot danger'></span>Late</>
}
}
}
], []);
const [data, setData] = useState([]);
const [locationOptions, setLocationOptions] = useState([]);
const [focused, setFocused] = useState(null); // react-dates
const [timeFocus, setTimeFocus] = useState(false); // react-datetime-range-picker
const [startDate, setStartDate] = useState(null);
const [endDate, setEndDate] = useState(null);
const [startTime, setStartTime] = useState(moment().startOf('day'));
const [endTime, setEndTime] = useState(moment().endOf('day'));
const [location, setLocation] = useState(null);
const [job, setJob] = useState(null);
const [jobOptions, setJobOptions] = useState([]);
// const debouncedStartTime = useDebounce(startTime, 1);
// const debouncedEndTime = useDebounce(endTime, 1);
useEffect(() => {
dispatch(getLocations());
dispatch(getJobList())
},[])
useEffect(() => {
let dispatchOptions = {
startDate: '2000-01-01',
endDate: '2050-01-01',
};
if (startDate) dispatchOptions.startDate = moment.tz(startDate, rsUserTimezone).utc().format('YYYY-MM-DD');
if (endDate) dispatchOptions.endDate = moment(endDate, rsUserTimezone).utc().format('YYYY-MM-DD');
if (job) dispatchOptions.jobId = job.value;
dispatch(getAttendanceActivity({...dispatchOptions}))
}, [startDate, endDate, job, location]);
useEffect(() => {
if (rsLocations) {
let options = [];
rsLocations.map(el => {
options.push({ value: el.id, label: el.location_name });
});
setLocationOptions(options);
}
}, [rsLocations]);
useEffect(() => {
if (rsJobs) {
let options = [];
rsJobs.map(el => {
options.push({ value: el.id, label: el.job_name })
})
setJobOptions(options)
}
}, [rsJobs])
useEffect(() => {
if (rsAttendance) {
const udpateAttendanceTable = async () => {
const attendanceList = await convertAttendanceToLocalTimezone(rsAttendance);
setData(attendanceList);
};
udpateAttendanceTable();
}
}, [rsAttendance]);
const clearFilters = () => {
setStartDate(null);
setEndDate(null);
setFocused(null);
setStartTime(moment().startOf('day'));
setEndTime(moment().endOf('day'));
setLocation(null);
setJob(null);
}
const onDatesChange = ({ startDate, endDate }) => {
setStartDate(startDate);
setEndDate(endDate);
}
const onTimesChange = ({ start, end }) => {
setStartTime(start);
setEndTime(end);
}
const onLocationChange = (option) => {
setLocation(option);
// dispatch get company members with location param
// ...
}
const onJobChange = (option) => {
setJob(option);
}
return (
<Container>
<Row className='mb-4'>
<Col>
<PageTitle
icon={<i className='bx bx-paper-plane' ></i>}
title='Activity'
description='Keep track of your employee activities at a glance.'
/>
</Col>
</Row>
{rsAttendance &&
<Row>
<Col sm={12} xl={3} className='mb-3'>
<Col className='segment filter-bar'>
<Row className='title'>
<Col className='d-flex align-items-center'>
<i className='bx bx-slider-alt mr-2'></i>
<p className='mb-0 font-weight-bold'>Filter</p>
</Col>
<Col className='col-auto'>
<Button
variant='link'
size='sm'
className='text-muted'
onClick={clearFilters}
>Clear all</Button>
</Col>
</Row>
<Row>
<Col>
<Form.Group controlId='search'>
<SearchBar
searchItem={data}
searchQuery={['email', 'role']}
onSearch={({ filteredItems }) => setData(filteredItems)}
/>
</Form.Group>
<Form.Row>
<Form.Group as={Col} sm={4} xl={12}>
<Form.Label>Date</Form.Label>
<div className={`custom-daterange-picker ${focused ? 'focused' : ''}`}>
<Form.Control
as={DateRangePicker}
startDate={startDate}
startDateId='startDate'
endDate={endDate}
endDateId='endDate'
onDatesChange={onDatesChange}
focusedInput={focused}
onFocusChange={focused => setFocused(focused)}
isOutsideRange={() => false}
displayFormat='DD/MM/YYYY'
showClearDates
customCloseIcon={<i className='bx bx-x' style=></i>}
customArrowIcon={'-'}
inputIconPosition='after'
readOnly={true}
hideKeyboardShortcutsPanel
block
/>
</div>
</Form.Group>
<Form.Group as={Col} sm={4} xl={12} controlId='time'>
<Form.Label>Time</Form.Label>
<Form.Control
as={DatetimeRangePicker}
className={`time-range-picker ${timeFocus ? 'focus' : ''}`}
startDate={startTime}
endDate={endTime}
dateFormat={false}
timeFormat='hh:mm A'
inputProps=
onChange={onTimesChange}
onFocus={() => setTimeFocus(true)}
onBlur={() => setTimeFocus(false)}
/>
</Form.Group>
{/* <Form.Group as={Col} sm={4} xl={12} controlId='location'>
<Form.Label>Location</Form.Label>
<Select
classNamePrefix='form-select'
className='form-select-container'
menuPlacement='auto'
isClearable
options={locationOptions}
value={location}
onChange={onLocationChange}
/>
</Form.Group> */}
<Form.Group as={Col} sm={4} xl={12} controlId='job'>
<Form.Label>Job</Form.Label>
<Select
classNamePrefix='form-select'
className='form-select-container'
menuPlacement='auto'
isClearable
options={jobOptions}
value={job}
onChange={onJobChange}
/>
</Form.Group>
</Form.Row>
</Col>
</Row>
</Col>
</Col>
<Col sm={12} xl={9} style=>
<ReactTable
className='table-detach'
columns={columns}
data={data}
/>
</Col>
</Row>
}
</Container>
);
}
Aucun commentaire:
Enregistrer un commentaire