Automatic locale time formatting

This commit is contained in:
Anton Tananaev 2024-05-09 07:24:14 -07:00
parent f6d15a1a44
commit c4754d0bef
16 changed files with 27 additions and 60 deletions

View File

@ -42,14 +42,13 @@ const PositionValue = ({ position, property, attribute }) => {
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
const coordinateFormat = usePreference('coordinateFormat');
const hours12 = usePreference('twelveHourFormat');
const formatValue = () => {
switch (key) {
case 'fixTime':
case 'deviceTime':
case 'serverTime':
return formatTime(value, 'seconds', hours12);
return formatTime(value, 'seconds');
case 'latitude':
return formatCoordinate('latitude', value, coordinateFormat);
case 'longitude':

View File

@ -1,6 +1,7 @@
import dayjs from 'dayjs';
import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime';
import localizedFormat from 'dayjs/plugin/localizedFormat';
import {
altitudeFromMeters,
@ -16,6 +17,7 @@ import { prefixString } from './stringUtils';
dayjs.extend(duration);
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);
export const formatBoolean = (value, t) => (value ? t('sharedYes') : t('sharedNo'));
@ -29,18 +31,18 @@ export const formatVoltage = (value, t) => `${value.toFixed(2)} ${t('sharedVoltA
export const formatConsumption = (value, t) => `${value.toFixed(2)} ${t('sharedLiterPerHourAbbreviation')}`;
export const formatTime = (value, format, hours12) => {
export const formatTime = (value, format) => {
if (value) {
const d = dayjs(value);
switch (format) {
case 'date':
return d.format('YYYY-MM-DD');
return d.format('L');
case 'time':
return d.format(hours12 ? 'hh:mm:ss A' : 'HH:mm:ss');
return d.format('LTS');
case 'minutes':
return d.format(hours12 ? 'YYYY-MM-DD hh:mm A' : 'YYYY-MM-DD HH:mm');
return d.format('L LT');
default:
return d.format(hours12 ? 'YYYY-MM-DD hh:mm:ss A' : 'YYYY-MM-DD HH:mm:ss');
return d.format('L LTS');
}
}
return '';

View File

@ -9,7 +9,6 @@ import DeleteIcon from '@mui/icons-material/Delete';
import { formatNotificationTitle, formatTime } from '../common/util/formatter';
import { useTranslation } from '../common/components/LocalizationProvider';
import { eventsActions } from '../store';
import { usePreference } from '../common/util/preferences';
const useStyles = makeStyles((theme) => ({
drawer: {
@ -30,8 +29,6 @@ const EventsDrawer = ({ open, onClose }) => {
const dispatch = useDispatch();
const t = useTranslation();
const hours12 = usePreference('twelveHourFormat');
const devices = useSelector((state) => state.devices.items);
const events = useSelector((state) => state.events.items);
@ -66,7 +63,7 @@ const EventsDrawer = ({ open, onClose }) => {
>
<ListItemText
primary={`${devices[event.deviceId]?.name}${formatType(event)}`}
secondary={formatTime(event.eventTime, 'seconds', hours12)}
secondary={formatTime(event.eventTime, 'seconds')}
/>
<IconButton size="small" onClick={() => dispatch(eventsActions.delete(event))}>
<DeleteIcon fontSize="small" className={classes.delete} />

View File

@ -5,7 +5,7 @@ import { useTheme } from '@mui/styles';
import { map } from './core/MapView';
import { formatTime, getStatusColor } from '../common/util/formatter';
import { mapIconKey } from './core/preloadImages';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import { useCatchCallback } from '../reactHelper';
const MapPositions = ({ positions, onClick, showStatus, selectedPosition, titleField }) => {
@ -21,7 +21,6 @@ const MapPositions = ({ positions, onClick, showStatus, selectedPosition, titleF
const selectedDeviceId = useSelector((state) => state.devices.selectedId);
const mapCluster = useAttributePreference('mapCluster', true);
const hours12 = usePreference('twelveHourFormat');
const directionType = useAttributePreference('mapDirection', 'selected');
const createFeature = (devices, position, selectedPositionId) => {
@ -42,7 +41,7 @@ const MapPositions = ({ positions, onClick, showStatus, selectedPosition, titleF
id: position.id,
deviceId: position.deviceId,
name: device.name,
fixTime: formatTime(position.fixTime, 'seconds', hours12),
fixTime: formatTime(position.fixTime, 'seconds'),
category: mapIconKey(device.category),
color: showStatus ? position.attributes.color || getStatusColor(device.status) : 'neutral',
rotation: position.course,

View File

@ -25,7 +25,6 @@ import { useCatch } from '../reactHelper';
import MapCamera from '../map/MapCamera';
import MapGeofence from '../map/MapGeofence';
import StatusCard from '../common/components/StatusCard';
import { usePreference } from '../common/util/preferences';
const useStyles = makeStyles((theme) => ({
root: {
@ -82,8 +81,6 @@ const ReplayPage = () => {
const navigate = useNavigate();
const timerRef = useRef();
const hours12 = usePreference('twelveHourFormat');
const defaultDeviceId = useSelector((state) => state.devices.selectedId);
const [positions, setPositions] = useState([]);
@ -210,7 +207,7 @@ const ReplayPage = () => {
<IconButton onClick={() => setIndex((index) => index + 1)} disabled={playing || index >= positions.length - 1}>
<FastForwardIcon />
</IconButton>
{formatTime(positions[index].fixTime, 'seconds', hours12)}
{formatTime(positions[index].fixTime, 'seconds')}
</div>
</>
) : (

View File

@ -13,7 +13,7 @@ import PageLayout from '../common/components/PageLayout';
import ReportsMenu from './components/ReportsMenu';
import usePositionAttributes from '../common/attributes/usePositionAttributes';
import { useCatch } from '../reactHelper';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import {
altitudeFromMeters, distanceFromMeters, speedFromKnots, volumeFromLiters,
} from '../common/util/converter';
@ -29,7 +29,6 @@ const ChartReportPage = () => {
const altitudeUnit = useAttributePreference('altitudeUnit');
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
const hours12 = usePreference('twelveHourFormat');
const [items, setItems] = useState([]);
const [types, setTypes] = useState(['speed']);
@ -126,7 +125,7 @@ const ChartReportPage = () => {
<XAxis
dataKey="fixTime"
type="number"
tickFormatter={(value) => formatTime(value, 'time', hours12)}
tickFormatter={(value) => formatTime(value, 'time')}
domain={['dataMin', 'dataMax']}
scale="time"
/>
@ -138,7 +137,7 @@ const ChartReportPage = () => {
<CartesianGrid strokeDasharray="3 3" />
<Tooltip
formatter={(value, key) => [value, positionAttributes[key]?.name || key]}
labelFormatter={(value) => formatTime(value, 'seconds', hours12)}
labelFormatter={(value) => formatTime(value, 'seconds')}
/>
<Line type="monotone" dataKey={type} />
</LineChart>

View File

@ -15,7 +15,6 @@ import TableShimmer from '../common/components/TableShimmer';
import MapCamera from '../map/MapCamera';
import MapGeofence from '../map/MapGeofence';
import { formatTime } from '../common/util/formatter';
import { usePreference } from '../common/util/preferences';
import { prefixString } from '../common/util/stringUtils';
import MapMarkers from '../map/MapMarkers';
@ -25,8 +24,6 @@ const CombinedReportPage = () => {
const devices = useSelector((state) => state.devices.items);
const hours12 = usePreference('twelveHourFormat');
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
@ -90,7 +87,7 @@ const CombinedReportPage = () => {
{!loading ? items.flatMap((item) => item.events.map((event, index) => (
<TableRow key={event.id}>
<TableCell>{index ? '' : devices[item.deviceId].name}</TableCell>
<TableCell>{formatTime(event.eventTime, 'seconds', hours12)}</TableCell>
<TableCell>{formatTime(event.eventTime, 'seconds')}</TableCell>
<TableCell>{t(prefixString('event', event.type))}</TableCell>
</TableRow>
))) : (<TableShimmer columns={3} />)}

View File

@ -17,7 +17,7 @@ import ColumnSelect from './components/ColumnSelect';
import { useCatch, useEffectAsync } from '../reactHelper';
import useReportStyles from './common/useReportStyles';
import TableShimmer from '../common/components/TableShimmer';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import MapView from '../map/core/MapView';
import MapGeofence from '../map/MapGeofence';
import MapPositions from '../map/MapPositions';
@ -42,7 +42,6 @@ const EventReportPage = () => {
const geofences = useSelector((state) => state.geofences.items);
const speedUnit = useAttributePreference('speedUnit');
const hours12 = usePreference('twelveHourFormat');
const [allEventTypes, setAllEventTypes] = useState([['allEvents', 'eventAll']]);
@ -123,7 +122,7 @@ const EventReportPage = () => {
const value = item[key];
switch (key) {
case 'eventTime':
return formatTime(value, 'seconds', hours12);
return formatTime(value, 'seconds');
case 'type':
return t(prefixString('event', value));
case 'geofenceId':

View File

@ -12,7 +12,6 @@ import ColumnSelect from './components/ColumnSelect';
import { useCatch } from '../reactHelper';
import useReportStyles from './common/useReportStyles';
import TableShimmer from '../common/components/TableShimmer';
import { usePreference } from '../common/util/preferences';
const columnsArray = [
['captureTime', 'statisticsCaptureTime'],
@ -32,8 +31,6 @@ const StatisticsPage = () => {
const classes = useReportStyles();
const t = useTranslation();
const hours12 = usePreference('twelveHourFormat');
const [columns, setColumns] = usePersistedState('statisticsColumns', ['captureTime', 'activeUsers', 'activeDevices', 'messagesStored']);
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
@ -71,7 +68,7 @@ const StatisticsPage = () => {
<TableRow key={item.id}>
{columns.map((key) => (
<TableCell key={key}>
{key === 'captureTime' ? formatTime(item[key], 'date', hours12) : item[key]}
{key === 'captureTime' ? formatTime(item[key], 'date') : item[key]}
</TableCell>
))}
</TableRow>

View File

@ -10,7 +10,7 @@ import {
formatDistance, formatVolume, formatTime, formatNumericHours,
} from '../common/util/formatter';
import ReportFilter from './components/ReportFilter';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import ReportsMenu from './components/ReportsMenu';
@ -44,7 +44,6 @@ const StopReportPage = () => {
const distanceUnit = useAttributePreference('distanceUnit');
const volumeUnit = useAttributePreference('volumeUnit');
const hours12 = usePreference('twelveHourFormat');
const [columns, setColumns] = usePersistedState('stopColumns', ['startTime', 'endTime', 'startOdometer', 'address']);
const [items, setItems] = useState([]);
@ -92,7 +91,7 @@ const StopReportPage = () => {
switch (key) {
case 'startTime':
case 'endTime':
return formatTime(value, 'minutes', hours12);
return formatTime(value, 'minutes');
case 'startOdometer':
return formatDistance(value, distanceUnit, t);
case 'duration':

View File

@ -8,7 +8,7 @@ import {
formatDistance, formatSpeed, formatVolume, formatTime, formatNumericHours,
} from '../common/util/formatter';
import ReportFilter from './components/ReportFilter';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import ReportsMenu from './components/ReportsMenu';
@ -41,7 +41,6 @@ const SummaryReportPage = () => {
const distanceUnit = useAttributePreference('distanceUnit');
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
const hours12 = usePreference('twelveHourFormat');
const [columns, setColumns] = usePersistedState('summaryColumns', ['startTime', 'distance', 'averageSpeed']);
const [daily, setDaily] = useState(false);
@ -93,7 +92,7 @@ const SummaryReportPage = () => {
case 'deviceId':
return devices[value].name;
case 'startTime':
return formatTime(value, 'date', hours12);
return formatTime(value, 'date');
case 'startOdometer':
case 'endOdometer':
case 'distance':

View File

@ -9,7 +9,7 @@ import {
formatDistance, formatSpeed, formatVolume, formatTime, formatNumericHours,
} from '../common/util/formatter';
import ReportFilter from './components/ReportFilter';
import { useAttributePreference, usePreference } from '../common/util/preferences';
import { useAttributePreference } from '../common/util/preferences';
import { useTranslation } from '../common/components/LocalizationProvider';
import PageLayout from '../common/components/PageLayout';
import ReportsMenu from './components/ReportsMenu';
@ -50,7 +50,6 @@ const TripReportPage = () => {
const distanceUnit = useAttributePreference('distanceUnit');
const speedUnit = useAttributePreference('speedUnit');
const volumeUnit = useAttributePreference('volumeUnit');
const hours12 = usePreference('twelveHourFormat');
const [columns, setColumns] = usePersistedState('tripColumns', ['startTime', 'endTime', 'distance', 'averageSpeed']);
const [items, setItems] = useState([]);
@ -134,7 +133,7 @@ const TripReportPage = () => {
switch (key) {
case 'startTime':
case 'endTime':
return formatTime(value, 'minutes', hours12);
return formatTime(value, 'minutes');
case 'startOdometer':
case 'endOdometer':
case 'distance':

View File

@ -13,7 +13,6 @@ import CollectionFab from './components/CollectionFab';
import CollectionActions from './components/CollectionActions';
import TableShimmer from '../common/components/TableShimmer';
import SearchHeader, { filterByKeyword } from './components/SearchHeader';
import { usePreference } from '../common/util/preferences';
import { formatTime } from '../common/util/formatter';
import { useDeviceReadonly } from '../common/util/permissions';
import useSettingsStyles from './common/useSettingsStyles';
@ -25,8 +24,6 @@ const DevicesPage = () => {
const groups = useSelector((state) => state.groups.items);
const hours12 = usePreference('twelveHourFormat');
const deviceReadonly = useDeviceReadonly();
const [timestamp, setTimestamp] = useState(Date.now());
@ -84,7 +81,7 @@ const DevicesPage = () => {
<TableCell>{item.phone}</TableCell>
<TableCell>{item.model}</TableCell>
<TableCell>{item.contact}</TableCell>
<TableCell>{formatTime(item.expirationTime, 'date', hours12)}</TableCell>
<TableCell>{formatTime(item.expirationTime, 'date')}</TableCell>
<TableCell className={classes.columnAction} padding="none">
<CollectionActions
itemId={item.id}

View File

@ -190,10 +190,6 @@ const ServerPage = () => {
label={t('serverAnnouncement')}
/>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={item.twelveHourFormat} onChange={(event) => setItem({ ...item, twelveHourFormat: event.target.checked })} />}
label={t('settingsTwelveHourFormat')}
/>
<FormControlLabel
control={<Checkbox checked={item.forceSettings} onChange={(event) => setItem({ ...item, forceSettings: event.target.checked })} />}
label={t('serverForceSettings')}

View File

@ -270,12 +270,6 @@ const UserPage = () => {
onChange={(e) => setItem({ ...item, poiLayer: e.target.value })}
label={t('mapPoiLayer')}
/>
<FormGroup>
<FormControlLabel
control={<Checkbox checked={item.twelveHourFormat} onChange={(e) => setItem({ ...item, twelveHourFormat: e.target.checked })} />}
label={t('settingsTwelveHourFormat')}
/>
</FormGroup>
</AccordionDetails>
</Accordion>
<Accordion>

View File

@ -15,7 +15,6 @@ import CollectionActions from './components/CollectionActions';
import TableShimmer from '../common/components/TableShimmer';
import { useManager } from '../common/util/permissions';
import SearchHeader, { filterByKeyword } from './components/SearchHeader';
import { usePreference } from '../common/util/preferences';
import useSettingsStyles from './common/useSettingsStyles';
const UsersPage = () => {
@ -25,8 +24,6 @@ const UsersPage = () => {
const manager = useManager();
const hours12 = usePreference('twelveHourFormat');
const [timestamp, setTimestamp] = useState(Date.now());
const [items, setItems] = useState([]);
const [searchKeyword, setSearchKeyword] = useState('');
@ -91,7 +88,7 @@ const UsersPage = () => {
<TableCell>{item.email}</TableCell>
<TableCell>{formatBoolean(item.administrator, t)}</TableCell>
<TableCell>{formatBoolean(item.disabled, t)}</TableCell>
<TableCell>{formatTime(item.expirationTime, 'date', hours12)}</TableCell>
<TableCell>{formatTime(item.expirationTime, 'date')}</TableCell>
<TableCell className={classes.columnAction} padding="none">
<CollectionActions
itemId={item.id}