diff --git a/src/components/Table/Columns/index.ts b/src/components/Table/Columns/index.ts index babb1fd..ee6776a 100755 --- a/src/components/Table/Columns/index.ts +++ b/src/components/Table/Columns/index.ts @@ -3,6 +3,7 @@ import { Rule } from 'antd/lib/form' import { ColumnProps } from 'antd/lib/table' export { makeDateColumn } from './date' +export { makeTimeColumn } from './time' export { RegExpIsFloat, makeNumericRender, diff --git a/src/components/Table/Columns/time.tsx b/src/components/Table/Columns/time.tsx new file mode 100644 index 0000000..7b7144e --- /dev/null +++ b/src/components/Table/Columns/time.tsx @@ -0,0 +1,26 @@ +import { ReactNode } from 'react' + +import { formatTime } from '@utils/datetime' + +import { makeColumn, columnPropsOther } from '.' +import { makeTimeSorter, TimePickerWrapper, TimePickerWrapperProps } from '..' + +export const makeTimeColumn = ( + title: ReactNode, + key: string, + utc?: boolean, + format?: string, + other?: columnPropsOther, + pickerOther?: TimePickerWrapperProps, +) => makeColumn(title, key, { + ...other, + render: (time) => ( +
+ {formatTime(time, utc, format) ?? '-'} +
+ ), + sorter: makeTimeSorter(key), + input: , +}) + +export default makeTimeColumn diff --git a/src/components/Table/TimePickerWrapper.tsx b/src/components/Table/TimePickerWrapper.tsx new file mode 100644 index 0000000..6ebe6a9 --- /dev/null +++ b/src/components/Table/TimePickerWrapper.tsx @@ -0,0 +1,31 @@ +import { Moment } from 'moment' +import { TimePicker, TimePickerProps } from 'antd' +import { memo, useCallback, useMemo } from 'react' + +import { defaultTimeFormat, momentToTimeOnly, timeOnlyToMoment } from '@utils/datetime' +import { TimeOnly } from '@api' + +export type TimePickerWrapperProps = Omit, 'onChange'> & { + value?: TimeOnly, + onChange?: (date: TimeOnly | null) => any + isUTC?: boolean +} + +export const TimePickerWrapper = memo(({ value, onChange, isUTC, ...other }) => { + const time = useMemo(() => value ? timeOnlyToMoment(value, isUTC) : null, [value, isUTC]) + + const onTimeChange = useCallback((time: Moment | null) => onChange?.(time ? momentToTimeOnly(time) : null), [onChange]) + + return ( + + ) +}) + +export default TimePickerWrapper diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index ddf735d..715c2d2 100755 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -1,6 +1,7 @@ -export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters' +export { makeDateSorter, makeNumericSorter, makeStringSorter, makeTimeSorter } from './sorters' export { EditableTable, makeActionHandler } from './EditableTable' export { DatePickerWrapper } from './DatePickerWrapper' +export { TimePickerWrapper } from './TimePickerWrapper' export { DateRangeWrapper } from './DateRangeWrapper' export { Table } from './Table' export { @@ -8,6 +9,7 @@ export { timezoneOptions, TimezoneSelect, makeDateColumn, + makeTimeColumn, makeGroupColumn, makeColumn, makeColumnsPlanFact, @@ -36,6 +38,7 @@ export type { } from './Columns' export type { DateRangeWrapperProps } from './DateRangeWrapper' export type { DatePickerWrapperProps } from './DatePickerWrapper' +export type { TimePickerWrapperProps } from './TimePickerWrapper' export type { BaseTableColumn, TableColumns, TableContainer } from './Table' export const defaultPagination = { diff --git a/src/components/Table/sorters.ts b/src/components/Table/sorters.ts index bbccbbd..2592300 100755 --- a/src/components/Table/sorters.ts +++ b/src/components/Table/sorters.ts @@ -1,4 +1,6 @@ +import { timeOnlyToMoment } from '@utils/datetime' import { isRawDate } from '@utils' +import { TimeOnly } from '@api' import { DataType } from './Columns' @@ -23,3 +25,14 @@ export const makeDateSorter = (key: keyof DataType) => (a: return date.getTime() - new Date(bdate).getTime() } + +export const makeTimeSorter = (key: keyof DataType) => (a: DataType, b: DataType) => { + const elma = a[key] + const elmb = b[key] + + if (!elma && !elmb) return 0 + if (!elma) return 1 + if (!elmb) return -1 + + return timeOnlyToMoment(elma).diff(timeOnlyToMoment(elmb)) +} diff --git a/src/utils/datetime.ts b/src/utils/datetime.ts index e72f63c..cb92700 100755 --- a/src/utils/datetime.ts +++ b/src/utils/datetime.ts @@ -1,10 +1,11 @@ -import moment from 'moment' +import moment, { Moment } from 'moment' -import { SimpleTimezoneDto } from '@api' +import { SimpleTimezoneDto, TimeOnly } from '@api' export type RawDate = number | string | Date export const defaultFormat: Readonly = 'DD.MM.YYYY HH:mm' +export const defaultTimeFormat: Readonly = 'HH:mm:ss' export enum timeInS { millisecond = 0.001, @@ -19,12 +20,25 @@ export function isRawDate(value: unknown): value is RawDate { return !isNaN(Date.parse(String(value))) } +export function isTimeOnly(value: unknown): value is TimeOnly { + if (!value || typeof value !== 'object') + return false + const keys = Object.keys(value) + return ['hour', 'minute', 'second'].every((key) => keys.includes(key)) +} + export const formatDate = (date: unknown, utc: boolean = false, format: string = defaultFormat) => { if (!isRawDate(date)) return null const out = utc ? moment.utc(date).local() : moment(date) return out.format(format) } +export const formatTime = (time: unknown, utc: boolean = false, format: string = defaultTimeFormat) => { + if(!isTimeOnly(time)) return + const out = timeOnlyToMoment(time, utc, format) + return out.format(format) +} + export const periodToString = (time?: number) => { if (!time || time <= 0) return '00:00:00' const days = Math.floor(time / timeInS.day) @@ -77,3 +91,16 @@ export const isTimezoneId = (value: unknown): value is TimezoneId => !!value && export const findTimezoneId = (value: SimpleTimezoneDto): TimezoneId => (isTimezoneId(value.timezoneId) && value.timezoneId) || (Object.keys(rawTimezones) as TimezoneId[]).find(id => rawTimezones[id] === value.hours) as TimezoneId + +export const timeOnlyToMoment = (time?: TimeOnly | null, isUtc?: boolean, format: string = defaultTimeFormat): Moment => { + const input = `${time?.hour ?? 0}:${time?.minute ?? 0}:${time?.second ?? 0}` + return isUtc ? moment.utc(input, format).local() : moment(input, format) +} + +export const momentToTimeOnly = (time?: Moment | null): TimeOnly => ({ + hour: time?.hour() ?? 0, + minute: time?.minute() ?? 0, + second: time?.second() ?? 0, + millisecond: 0, + ticks: 0, +})