From e9b4cfba2473698603d40c3edd9de6e7c2a520b9 Mon Sep 17 00:00:00 2001 From: awenn2015 Date: Mon, 5 Jun 2023 00:53:44 +0400 Subject: [PATCH] update to v2.1.6-beta-1 --- package.json | 2 +- src/assets/styles/index.css | 5 + src/context/TableContext.ts | 5 +- src/defines.ts | 4 +- src/service/ImportService.ts | 73 +++--- src/temps/App.tsx | 3 +- src/temps/Bottom.tsx | 6 +- src/temps/Updates.tsx | 84 ++++++- src/temps/filter/Filter.module.scss | 2 + src/temps/filter/Filter.tsx | 4 +- .../{FilterTech.tsx => FilterEntity.tsx} | 11 +- src/temps/left/Left.module.scss | 5 +- src/temps/left/Left.tsx | 35 +-- src/temps/modals/AddingModal.tsx | 19 +- src/temps/modals/HelpModal.tsx | 2 +- src/temps/repeater/BaseRepeater.tsx | 40 ++-- src/temps/setting/EntityRepeater.tsx | 2 + src/temps/setting/SettingModal.tsx | 29 ++- src/temps/table/EditableCell.tsx | 42 ++++ src/temps/table/Table.tsx | 3 +- src/temps/table/TableFoot.tsx | 16 +- src/temps/table/TableRow.tsx | 218 +++++++++--------- src/types/index.d.ts | 17 +- 23 files changed, 413 insertions(+), 214 deletions(-) rename src/temps/filter/{FilterTech.tsx => FilterEntity.tsx} (63%) create mode 100644 src/temps/table/EditableCell.tsx diff --git a/package.json b/package.json index b04a892..88c64a4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "working-hours", "homepage": "/working-hours/", - "version": "2.1.5-beta-4", + "version": "2.1.6-beta-1", "private": true, "dependencies": { "bootstrap": "^5.2.3", diff --git a/src/assets/styles/index.css b/src/assets/styles/index.css index 1d9d4dd..75f68d7 100644 --- a/src/assets/styles/index.css +++ b/src/assets/styles/index.css @@ -18,3 +18,8 @@ code { --bs-btn-font-size: 0.875rem; --bs-btn-border-radius: 0.25rem; } + +.offcanvas-footer { + flex-shrink: 1; + padding: var(--bs-offcanvas-padding-y) var(--bs-offcanvas-padding-x); +} diff --git a/src/context/TableContext.ts b/src/context/TableContext.ts index 12dfde7..9188422 100644 --- a/src/context/TableContext.ts +++ b/src/context/TableContext.ts @@ -1,6 +1,7 @@ import { createContext, Dispatch, useContext } from 'react' import { DTEnum, IAppSettings, ITableOptions, IWorkTable, IWorkTableRow } from '@/types' import { defAppSetting } from '@/utils/login' +import Random from '@/utils/class/Random' type DispatchRewrite = { key: K, value: T[K] } type RewriteDispatch2 = (key: K, value: T[K]) => DispatchRewrite @@ -29,7 +30,7 @@ export type IModalVisible = { type DispatchTable = ({ key: 'id' | 'start' | 'finish' | 'description', value: string } - | { key: 'entity', value: string } + | { key: 'entityId', value: string | null } | { key: 'isPaid', value: boolean }) & { id: string } type DispatchFilter = @@ -105,7 +106,7 @@ export const defOptions: ITableOptions = { down: 'ArrowDown', }, listOfTech: [ - { key: 'base', text: 'Базовый', rate: 200 }, + { id: Random.uuid(10), key: 'base', text: 'Базовый', rate: 200 }, ], } diff --git a/src/defines.ts b/src/defines.ts index 80cfe5a..0d0a21d 100644 --- a/src/defines.ts +++ b/src/defines.ts @@ -4,6 +4,6 @@ // prev = 214023 export const appVersion = { - name: '2.1.5-beta-4', - code: 215024, + name: '2.1.6-beta-1', + code: 216021, } \ No newline at end of file diff --git a/src/service/ImportService.ts b/src/service/ImportService.ts index c3a155e..bdb84d8 100644 --- a/src/service/ImportService.ts +++ b/src/service/ImportService.ts @@ -1,10 +1,19 @@ import { ITableOptionsEntity, IWorkTableRow } from '@/types' +import Random from '@/utils/class/Random' +import TableService from '@/service/TableService' type EventHandlers = { update: null | ((list: ITableOptionsEntity[]) => void), success: null | ((table: IWorkTableRow[]) => void) } +/** + * Пока что реализован импорт без сущностей + * + * TODO: Поменять формат импорта на {table: [...], entities: [{ key? text? rate? }]} + * TODO: Переписать связи с entity в tableRow и по умолчанию кидать невидимую entity 0 (Добавить) + * TODO: Дописать проверку в настройках и изменение entity в данных таблицы + */ class ImportService { private reader: FileReader private handlers: EventHandlers @@ -46,7 +55,10 @@ class ImportService { if (!this.handlers.update) return const list = entity.filter(key => !prevTech.includes(key)).map((it, i) => ({ - key: it, text: `Текст - ${i + 1}`, rate: 100, + id: Random.uuid(10), + key: it, + text: `Ставка - ${i + 1}`, + rate: 150, })) this.handlers.update(list) @@ -61,16 +73,16 @@ class ImportService { const data = this.validate(any) if (!data.length) return - const entity = data.reduce((list, it) => { - if (!list.includes(it.entity)) list.push(it.entity) - return list - }, []) - - const prevTech = this.entities.map(({ key }) => key) - const extra = [...entity, ...prevTech] - - if (extra.length !== prevTech.length) - this.insertEntities(entity, prevTech) + // const entity = data.reduce((list, it) => { + // if (!list.includes(it.entity)) list.push(it.entity) + // return list + // }, []) + // + // const prevTech = this.entities.map(({ key }) => key) + // const extra = [...entity, ...prevTech] + // + // if (extra.length !== prevTech.length) + // this.insertEntities(entity, prevTech) this.handlers.success(data) } catch (err) { @@ -93,7 +105,6 @@ class ImportService { { key: 'id', type: 'string', empty: false }, { key: 'start', type: 'string', empty: false }, { key: 'finish', type: 'string', empty: false }, - { key: 'entity', type: 'string', empty: false }, { key: 'isPaid', type: 'boolean' }, { key: 'description', type: 'string', empty: true }, ] @@ -101,6 +112,17 @@ class ImportService { const list = [] let order = this.length + const options = TableService.getActiveOptions(this.active) + + if (!options) { + alert('Произошла непредвиденная ошибка при импорте!') + throw new Error('Нет данных о настройках таблицы!') + } + + const entityId = options.listOfTech.length + ? options.listOfTech[0]!.id + : null + for (const it of data) { if (!(typeof it === 'object' && !Array.isArray(it))) return [] @@ -111,21 +133,20 @@ class ImportService { }) if (!check) continue - else { - const item: IWorkTableRow = { - id: it.id, - tableId: this.active, - start: it.start, - finish: it.finish, - entity: it.entity, - isPaid: it.isPaid, - description: it.description, - order: order + 1, - } - - list.push(item) - order++ + + const item: IWorkTableRow = { + id: it.id, + tableId: this.active, + entityId, + start: it.start, + finish: it.finish, + isPaid: it.isPaid, + description: it.description, + order: order + 1, } + + list.push(item) + order++ } return list diff --git a/src/temps/App.tsx b/src/temps/App.tsx index 079df30..98d87ff 100644 --- a/src/temps/App.tsx +++ b/src/temps/App.tsx @@ -13,7 +13,7 @@ import { import { useEffect, useReducer, useState } from 'react' import Bottom from './Bottom' import Filter from './filter/Filter' -import { getAllIds, getTypedKeys, localStorageKeys } from '@/utils' +import { getAllIds, getTypedKeys } from '@/utils' import Left from './left/Left' import DescrModal from './modals/DescrModal' import TableService from '@/service/TableService' @@ -24,7 +24,6 @@ import CompareData from '@/utils/class/CompareData' import { getAppSettings } from '@/utils/login' import HelpModal from '@/temps/modals/HelpModal' import AddingModal from '@/temps/modals/AddingModal' -import { getLsOptionsKey } from '@/data' type BoundPartsOfStore = Pick diff --git a/src/temps/Bottom.tsx b/src/temps/Bottom.tsx index 5933b68..4868ce3 100644 --- a/src/temps/Bottom.tsx +++ b/src/temps/Bottom.tsx @@ -38,11 +38,11 @@ const Bottom = () => { } const item: IWorkTableRow = { - id: Random.uuid(), - tableId: Random.uuid(), + id: Random.uuid(13), + tableId: activeTable!, + entityId: options.listOfTech[0]!.id, start, finish, - entity: options.listOfTech[0]!.key, isPaid: false, description: '', order: modifiedTable.length + 1, diff --git a/src/temps/Updates.tsx b/src/temps/Updates.tsx index c386113..a73f137 100644 --- a/src/temps/Updates.tsx +++ b/src/temps/Updates.tsx @@ -5,17 +5,23 @@ import { getLocalVersion, LS_VERSION_KEY } from '@/data' import TableService from '@/service/TableService' import { localStorageKeys } from '@/utils' import { appVersion } from '@/defines' +import Random from '@/utils/class/Random' +import { ITableRowLegacy, IWorkTableRow } from '@/types' type ListOfUpdates = { need: boolean, reformat: (need: boolean) => void }[] - -function reformatLegacy215023(when: boolean) { - if (!when) return +/** + * Удалить все опции таблиц которые не были удалены до фикса + */ +function reformatLegacy215023(need: boolean) { + if (!need) return const match = 'awenn2015_wh_options_' const list = TableService.listOfTablesInfo const ids = list.map(it => it.id) + if (!ids.length) return + localStorageKeys((key) => { if (!key.includes(match)) return @@ -29,17 +35,22 @@ function reformatLegacy215023(when: boolean) { }) } -function reformatLegacy215024(when: boolean) { - if (!when) return +/** + * Переименовываем table.tech в table.entity и добавить поле order + */ +function reformatLegacy215024(need: boolean) { + if (!need) return const list = TableService.listOfTablesInfo + if (!list.length) return + for (const { id } of list) { - const table = TableService.getActiveTableData(id) + const table = TableService.getActiveTableData(id) as ITableRowLegacy[] if (!table.length) continue for (let i = 0; i < table.length; i++) { - table[i]!['entity'] = table[i]?.['tech'] || table[i]!.entity + table[i]!['entity'] = table[i]?.['tech'] || table[i]!.entity! delete table[i]?.['tech'] if (table[i]?.tableId !== id) @@ -48,7 +59,56 @@ function reformatLegacy215024(when: boolean) { table[i]!['order'] = i + 1 } - TableService.updateActiveTableData(id, table) + TableService.updateActiveTableData(id, table as IWorkTableRow[]) + } +} + +/** + * Добавляем всем сущностям уникальный id + */ +function reformatLegacy215040(need: boolean) { + if (!need) return + + const list = TableService.listOfTablesInfo + if (!list.length) return + + for (const { id } of list) { + const options = TableService.getActiveOptions(id) + if (!options || !options.listOfTech.length) continue + + for (const entity of options.listOfTech) + entity['id'] = entity?.id || Random.uuid(10) + + TableService.updateActiveOptions(id, options) + } +} + +/** + * Переименовываем table.entity в table.entityId по id entity и поправить tableId + */ +function reformatLegacy216021(need: boolean) { + if (!need) return + + const list = TableService.listOfTablesInfo + if (!list.length) return + + for (const { id } of list) { + const table = TableService.getActiveTableData(id) as ITableRowLegacy[] + const options = TableService.getActiveOptions(id) + + if (!options || !table.length) return + + for (const row of table) { + row['entityId'] = row?.entityId || (row.entity && options.listOfTech.length + ? options.listOfTech.find(({ key }) => key === row.entity)?.id || null + : null) + + delete row['entity'] + + row['tableId'] = id + } + + TableService.updateActiveTableData(id, table as IWorkTableRow[]) } } @@ -61,6 +121,14 @@ const listOfUpdates: ListOfUpdates = [ need: getLocalVersion() < 215024, reformat: reformatLegacy215024, }, + { + need: getLocalVersion() < 215040, + reformat: reformatLegacy215040, + }, + { + need: getLocalVersion() < 216021, + reformat: reformatLegacy216021, + }, ] const Updates = () => { diff --git a/src/temps/filter/Filter.module.scss b/src/temps/filter/Filter.module.scss index 1fb22bb..e319303 100644 --- a/src/temps/filter/Filter.module.scss +++ b/src/temps/filter/Filter.module.scss @@ -8,6 +8,8 @@ display: flex; align-items: center; justify-content: space-between; + flex-wrap: wrap; + row-gap: 10px; } .overview { diff --git a/src/temps/filter/Filter.tsx b/src/temps/filter/Filter.tsx index 2e386bc..23b17a4 100644 --- a/src/temps/filter/Filter.tsx +++ b/src/temps/filter/Filter.tsx @@ -1,4 +1,4 @@ -import FilterTech from './FilterTech' +import FilterEntity from './FilterEntity' import { Actions, defTableFilter, useTableContext } from '@/context/TableContext' import FilterDate from './FilterDate' import scss from './Filter.module.scss' @@ -70,7 +70,7 @@ const FilterOverview = () => {
- +
diff --git a/src/temps/filter/FilterTech.tsx b/src/temps/filter/FilterEntity.tsx similarity index 63% rename from src/temps/filter/FilterTech.tsx rename to src/temps/filter/FilterEntity.tsx index 3af6922..43a1a31 100644 --- a/src/temps/filter/FilterTech.tsx +++ b/src/temps/filter/FilterEntity.tsx @@ -1,20 +1,23 @@ import React, { ChangeEvent } from 'react' import { Actions, useTableContext } from '@/context/TableContext' -const FilterTech = () => { +const FilterEntity = () => { const [{ filter, options }, dispatch] = useTableContext() function dispatchLang({ target }: ChangeEvent) { dispatch({ type: Actions.Filter, - payload: { key: 'entity', value: target.value }, + payload: { + key: 'entity', + value: target.value !== 'none' ? options.listOfTech.find(it => it.key === target.value)!.id : 'none', + }, }) } return ( setCreateMode(true)} + /> + + {listOfTables.length > 0 && ( setCreateMode(true)} + className="btn btn-outline-danger" + value="Удалить все таблицы" + onClick={() => TableService.deleteAllTables()} /> - - {listOfTables.length > 0 && ( - TableService.deleteAllTables()} - /> - )} -
+ )} - + ) diff --git a/src/temps/modals/AddingModal.tsx b/src/temps/modals/AddingModal.tsx index 4f5184f..634ad3d 100644 --- a/src/temps/modals/AddingModal.tsx +++ b/src/temps/modals/AddingModal.tsx @@ -5,8 +5,13 @@ import { getDateTimeWithOffset, getFormattedDateTime, roundDateTime } from '@/ut import { IWorkTableRow } from '@/types' import Random from '@/utils/class/Random' -type ExcludeFromTable = 'id' | 'tableId' | 'tech' | 'order' -type ITask = Omit +type ITask = { + start: string, + finish: string, + isPaid: boolean, + entity: string, + description: string +} const defTask: ITask = { start: '', @@ -49,7 +54,7 @@ const AddingModal = () => { setState(prev => ({ ...prev, ...getDateTime(options.dtRoundStep) })) if (!state.entity) - setState(prev => ({ ...prev, entity: options.listOfTech[0]!.key })) + setState(prev => ({ ...prev, entity: options.listOfTech[0]?.key || '' })) return () => { close?.removeEventListener('click', cancelCreate) @@ -71,10 +76,14 @@ const AddingModal = () => { } function addNewTableRow() { + const { entity, ...other } = state + const entityId = options.listOfTech.find(it => it.key === entity)!.id + const item: IWorkTableRow = { - id: Random.uuid(), + id: Random.uuid(13), tableId: activeTable!, - ...state, + entityId, + ...other, order: modifiedTable.length + 1, } diff --git a/src/temps/modals/HelpModal.tsx b/src/temps/modals/HelpModal.tsx index 3378f00..2910e86 100644 --- a/src/temps/modals/HelpModal.tsx +++ b/src/temps/modals/HelpModal.tsx @@ -7,7 +7,7 @@ const list = [ 'Что бы отредактировать данные добавленной строки в таблице кликните два раза по ячейке', `Редактируемые ячейки: 'Начал', 'Закончил', 'Сущность', 'Отплачено', 'Описание'`, `Для того что бы манипулировать со строкой (переместить или удалить) используйте клавиши забитые в настройках, по умолчанию это 'Delete', 'ArrowUp' и 'ArrowDown'`, - `Формат данных json для импорта {id: string, start: string, finish: string, entity: string, isPaid: boolean, description: string}[]` + `Формат данных json для импорта {id: string, start: string, finish: string, isPaid: boolean, description: string}[]` ] const HelpModal = () => { diff --git a/src/temps/repeater/BaseRepeater.tsx b/src/temps/repeater/BaseRepeater.tsx index 195b99e..e68aa79 100644 --- a/src/temps/repeater/BaseRepeater.tsx +++ b/src/temps/repeater/BaseRepeater.tsx @@ -13,17 +13,19 @@ export type RepeaterDispatch = (key: keyof T, value: any, index: number) => v interface BaseRepeaterProps { data: Array, baseKeys: (keyof T)[], - onChange: (list: T[]) => void, - onRender: (state: [T, RepeaterDispatch], i: number) => JSX.Element | JSX.Element[], title?: string, onHideItem?: OnHideItemRepeater, - onMount?: (() => void), textConfirmDeleteItem?: string, id?: string, baseTypes?: ListOfTypes, asForm?: boolean, - onSubmit?: ((e: React.FormEvent) => void), submitDisabled?: boolean + onMount?: (() => void), + onAdding?: (item: T) => void + onBeforeDelete?: (index: number) => boolean, + onChange: (list: T[], isAdding?: boolean) => void, + onRender: (state: [T, RepeaterDispatch], i: number) => JSX.Element | JSX.Element[], + onSubmit?: ((e: React.FormEvent) => void), } function getTypedKeys(obj: T) { @@ -46,6 +48,7 @@ function ListRepeater( title, data, baseKeys, + onAdding, onChange, textConfirmDeleteItem, onRender, @@ -56,6 +59,7 @@ function ListRepeater( asForm, onSubmit, submitDisabled, + onBeforeDelete, }: BaseRepeaterProps, ): JSX.Element { const ls = '__awenn2015_react_repeater' @@ -175,7 +179,9 @@ function ListRepeater( * Adds a new repeater element to the end */ function push() { - onChange([...data, create()]) + const it = create() + if (onAdding) onAdding(it) + onChange([...data, it], true) } /** @@ -186,11 +192,15 @@ function ListRepeater( onChange(data.filter((_, i) => i !== index)) } - if (isFieldEmpty(index)) __delete() - else { - const text = textConfirmDeleteItem || 'Do you really want to delete this item?' - const result = window.confirm(text) - if (result) __delete() + if (onBeforeDelete) { + if (onBeforeDelete(index)) __delete() + } else { + if (isFieldEmpty(index)) __delete() + else { + const text = textConfirmDeleteItem || 'Вы действительно хотите удалить этот элемент?' + const result = window.confirm(text) + if (result) __delete() + } } } @@ -198,11 +208,13 @@ function ListRepeater( * Adds a new element at the specified index */ function add(index: number) { + const it = create() + if (onAdding) onAdding(it) + onChange([ - ...data.slice(0, index), - create(), + ...data.slice(0, index), it, ...data.slice(index, data.length), - ]) + ], true) } /** @@ -374,7 +386,7 @@ function ListRepeater( * TODO: Сделать проверку на кол-во элементов и если 1 то не покалывать скрывашку * * @author awenn2015 - * @version 1.0.3 + * @version 1.0.4 */ function BaseRepeater(props: BaseRepeaterProps) { return props.asForm diff --git a/src/temps/setting/EntityRepeater.tsx b/src/temps/setting/EntityRepeater.tsx index 3f93012..500436f 100644 --- a/src/temps/setting/EntityRepeater.tsx +++ b/src/temps/setting/EntityRepeater.tsx @@ -34,6 +34,8 @@ const EntityRepeater: FC = ({ state: [item, change], i }) = onChange={({ target }) => change('rate', target.value, i)} placeholder="Ставка" /> + + ) } diff --git a/src/temps/setting/SettingModal.tsx b/src/temps/setting/SettingModal.tsx index 669ab45..b5d1c0a 100644 --- a/src/temps/setting/SettingModal.tsx +++ b/src/temps/setting/SettingModal.tsx @@ -13,6 +13,7 @@ import EntityRepeater from '@/temps/setting/EntityRepeater' import { appVersion } from '@/defines' import SettingFields from '@/temps/setting/SettingFields' import HiddenCols from '@/temps/setting/HiddenCols' +import Random from '@/utils/class/Random' export const labelsCols: Record = { number: 'Нумерация', @@ -21,7 +22,7 @@ export const labelsCols: Record = { } const SettingModal = () => { - const [{ visibility, options, activeTable }, dispatch, payload] = useTableContext() + const [{ visibility, options, activeTable, modifiedTable }, dispatch, payload] = useTableContext() const [modified, setModified] = useState(options) useDidUpdateEffect(() => { @@ -114,6 +115,14 @@ const SettingModal = () => { }) } + function createLabel(number: number, titles: string[]) { + const cases = [2, 0, 1, 1, 1, 2] + return `${titles[ + number % 100 > 4 && number % 100 < 20 + ? 2 : cases[number % 10 < 5 ? number % 10 : 5]! + ]}` + } + return ( { id={activeTable!} title="Сущности таблицы" data={modified.listOfTech} - baseKeys={['key', 'text', 'rate']} - baseTypes={{ text: 'string', key: 'string', rate: 'number' }} + baseKeys={['id', 'key', 'text', 'rate']} + baseTypes={{ id: 'string', text: 'string', key: 'string', rate: 'number' }} + onAdding={(it) => it['id'] = Random.uuid(10)} + onBeforeDelete={(i) => { + const { id } = modified.listOfTech[i]! + const qty = modifiedTable.filter(it => it.entityId === id).length + + if (!qty) return true + else { + const bind = createLabel(qty, ['связан', 'связано', 'связано']) + const elem = createLabel(qty, ['элемент', 'элемента', 'элементов']) + const msg = `С этой сущностью ${bind} ${qty} ${elem}` + + return window.confirm(`${msg} таблицы, вы действительно хотите её удалить?`) + } + }} onChange={(list) => { setModified(prev => ({ ...prev, listOfTech: list })) }} diff --git a/src/temps/table/EditableCell.tsx b/src/temps/table/EditableCell.tsx new file mode 100644 index 0000000..f000773 --- /dev/null +++ b/src/temps/table/EditableCell.tsx @@ -0,0 +1,42 @@ +import React, { useEffect, useRef, useState } from 'react' + +interface EditableRowProps { + data?: string | number | undefined, + hidden?: boolean, + onEdit: (ref: React.MutableRefObject, blur: () => void) => JSX.Element, + children?: JSX.Element +} + +function EditableCell( + { + data, + hidden, + onEdit, + children, + }: EditableRowProps): JSX.Element | null { + const [isEdit, setEdit] = useState(false) + const ref = useRef(null) + + useEffect(() => { + if (isEdit && ref.current) + ref.current.focus() + }, [isEdit]) + + function handler() { + setEdit(false) + } + + if (hidden) return null + + return ( + setEdit(true)} + > + {isEdit + ? onEdit(ref, handler) + : (children || data || 'undefined')} + + ) +} + +export default EditableCell \ No newline at end of file diff --git a/src/temps/table/Table.tsx b/src/temps/table/Table.tsx index 7ff87a4..0ca58cd 100644 --- a/src/temps/table/Table.tsx +++ b/src/temps/table/Table.tsx @@ -5,13 +5,12 @@ import TableHead from './TableHead' import { Actions, ChangeDateTime, ListOfSorting, useTableContext } from '@/context/TableContext' import TableFoot from './TableFoot' import { IWorkTableRow } from '@/types' -import ArrayExt from '@/utils/class/ArrayExt' import arrayExt from '@/utils/class/ArrayExt' class Filtering { public static byEntity(entity: string | 'none', list: IWorkTableRow[]) { return entity !== 'none' - ? list.filter((it) => it.entity === entity) : list + ? list.filter((it) => it.entityId === entity) : list } public static byDate(date: string | 'none', list: IWorkTableRow[]) { diff --git a/src/temps/table/TableFoot.tsx b/src/temps/table/TableFoot.tsx index a2930b2..b0a0682 100644 --- a/src/temps/table/TableFoot.tsx +++ b/src/temps/table/TableFoot.tsx @@ -27,14 +27,20 @@ const TableFoot = () => { else dispatchSelect(getAllIds(filteredTable)) } - function calcAmountByTech() { + function calcAmountByEntity() { const filtered = filteredTable.filter(it => selectedRows.includes(it.id)) return filtered.reduce>((list, it) => { const diff = getDiffOfHours(it.start, it.finish) const hours = getHoursOrZero(diff) - list[it.entity] = (list?.[it.entity] || 0) + hours + const key = options.listOfTech.find(entity => { + return entity.id === it.entityId + })?.key || null + + if (key === null) return list + + list[key] = (list?.[key] || 0) + hours return list }, {}) @@ -47,17 +53,17 @@ const TableFoot = () => { return filtered.reduce((total, it) => { const hours = getHoursOrZero(getDiffOfHours(it.start, it.finish)) - const data = options.listOfTech.find(({ key }) => key === it.entity) + const entity = options.listOfTech.find(({ id }) => id === it.entityId) return [ total[0] + hours, - total[1] + (hours * (data?.rate || 0)), + total[1] + (hours * (entity?.rate || 0)), ] }, [0, 0] as [number, number]) }, [filteredTable, selectedRows, options.listOfTech]) const textAmountsByLang = useMemo(() => { - const list = calcAmountByTech() + const list = calcAmountByEntity() return Object.keys(list).map((key) => { const data = options.listOfTech.find(it => it.key === key) diff --git a/src/temps/table/TableRow.tsx b/src/temps/table/TableRow.tsx index b5821a6..a59c19b 100644 --- a/src/temps/table/TableRow.tsx +++ b/src/temps/table/TableRow.tsx @@ -1,15 +1,13 @@ -import React, { FC, useEffect, useRef, useState } from 'react' +import React, { FC, useEffect, useMemo, useState } from 'react' import { formatDate, getDiffOfHours, getHoursOrZero, getTimeByDT } from '@/utils' import type { FieldsEnum, IWorkTableRow } from '@/types' import { DTEnum } from '@/types' import { Actions, ChangeDateTime, useTableContext } from '@/context/TableContext' import { TableRowActions } from '@/temps/table/Table' +import EditableCell from '@/temps/table/EditableCell' type Nullable = T | null - -type WriterList = { - [key in FieldsEnum]: boolean -} +type WriterList = Record interface WTRowProps { data: IWorkTableRow, @@ -32,32 +30,14 @@ function calcWorkHours(data: IWorkTableRow) { const TableRow: FC = ({ data, index, changeDT, onAction }) => { const [{ filteredTable, selectedRows, options }, dispatch, payload] = useTableContext() // state - const [writingMode, setWritingMode] = useState(defWritingData) const [diffDate, setDiffDate] = useState(formatDate(data)) const [qtyHours, setQtyHours] = useState(() => calcWorkHours(data)) - // ref - const startRef = useRef>(null) - const finishRef = useRef>(null) - const langRef = useRef>(null) - const paidRef = useRef>(null) - - const changeWritingMode = (type: FieldsEnum, value: boolean) => { - setWritingMode(prev => ({ ...prev, [type]: value })) - } - - useEffect(() => { - if (writingMode.start) startRef.current?.focus() - if (writingMode.finish) finishRef.current?.focus() - if (writingMode.entity) langRef.current?.focus() - if (writingMode.paid) paidRef.current?.focus() - }, [writingMode]) useEffect(() => { setQtyHours(calcWorkHours(data)) }, [filteredTable]) - function onBlurHandle(type: DTEnum) { - changeWritingMode(type, false) + function onBlurHandle() { setDiffDate(formatDate(data)) setQtyHours(getDiffOfHours(data.start, data.finish)) } @@ -109,6 +89,17 @@ const TableRow: FC = ({ data, index, changeDT, onAction }) => { dispatchSelected([...selectedRows, data.id]) } + function getEntityField(value: string | null, field: 'key' | 'id', def: string | null = null, list = options.listOfTech) { + const arr: ('key' | 'id')[] = ['key', 'id'] + const key = arr[Number(!Boolean(arr.indexOf(field)))]! + + return value ? list.find(it => it[key] === value)?.[field] || def : def + } + + const entity = useMemo(() => { + return getEntityField(data.entityId, 'key') ?? 'Нет' + }, [data.entityId, options.listOfTech]) + return ( {!options.hiddenCols.number && ( @@ -117,99 +108,100 @@ const TableRow: FC = ({ data, index, changeDT, onAction }) => { {diffDate} - changeWritingMode('start', true)} - > - {writingMode.start - ? ( - preChange('start', target.value)} - onBlur={() => onBlurHandle('start')} - ref={startRef} - /> - ) - : getTimeByDT(data.start)} - - - changeWritingMode('finish', true)} - > - {writingMode.finish - ? ( - preChange('finish', target.value)} - onBlur={() => onBlurHandle('finish')} - ref={finishRef} - /> - ) - : getTimeByDT(data.finish)} - + + data={getTimeByDT(data.start)} + onEdit={(ref, blur) => ( + { + preChange('start', target.value) + }} + onBlur={() => { + blur() + onBlurHandle() + }} + /> + )} + /> + + + data={getTimeByDT(data.finish)} + onEdit={(ref, blur) => ( + { + preChange('finish', target.value) + }} + onBlur={() => { + blur() + onBlurHandle() + }} + /> + )} + /> {qtyHours} ч. - {!options.hiddenCols.entity && ( - changeWritingMode('entity', true)} - > - {writingMode.entity - ? ( - - ) - : data.entity} - - )} - - changeWritingMode('paid', true)} + + data={entity} + hidden={options.hiddenCols.entity} + onEdit={(ref, blur) => ( + + )} + /> + + + onEdit={(ref, blur) => ( + { + dispatch({ + type: Actions.WH_Item, + payload: { + key: 'isPaid', id: data.id, value: !data.isPaid, + }, + }) + }} + /> + )} > - {writingMode.paid - ? ( - changeWritingMode('paid', false)} - onChange={() => { - dispatch({ - type: Actions.WH_Item, - payload: { - key: 'isPaid', id: data.id, value: !data.isPaid, - }, - }) - }} - /> - ) - : (data.isPaid - ? () - : ()) - } - + {data.isPaid + ? () + : ()} + @@ -35,12 +40,18 @@ export interface IWorkTable { export type IWorkTableRow = { id: string, tableId: string, + entityId: string | null, start: string, finish: string, - entity: string, isPaid: boolean, description: string, order: number -} & { tech?: string } +} + +export type ITableRowLegacy = Omit & { + tech?: string, + entity?: string, + entityId?: string | null +} export type BaseDispatch = (key: K, value: T[K]) => void \ No newline at end of file