Feat/generic editable cell all types (#987)

* Added generic relation cell

* Deactivated debug

* Added default warning

* Put back display component

* Removed unused types

* wip

* Renamed to view field

* Use new view field structure to have chip working

* Finished

* Added a temp feature flag

* Added double text chip cell

* Ok

* Finished tables

* Fixed icon size

* Fixed bug on date field

* Use icon index

* Fix

* Fixed naming

* Fix

* removed file from merge

* Fixed tests

* Coverage
This commit is contained in:
Lucas Bordeau
2023-07-29 23:48:43 +02:00
committed by GitHub
parent dc18bc40b0
commit d9f6ae8663
77 changed files with 1730 additions and 326 deletions

View File

@ -11,13 +11,18 @@ const StyledTitle = styled.div`
display: flex;
flex-direction: row;
font-weight: ${({ theme }) => theme.font.weight.medium};
gap: ${({ theme }) => theme.spacing(1)};
height: ${({ theme }) => theme.spacing(8)};
padding-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledIcon = styled.div`
display: flex;
margin-right: ${({ theme }) => theme.spacing(1)};
& > svg {
height: ${({ theme }) => theme.icon.size.md}px;
width: ${({ theme }) => theme.icon.size.md}px;
}
`;
export function ColumnHead({ viewName, viewIcon }: OwnProps) {

View File

@ -25,9 +25,9 @@ export function EntityTableCell({ cellIndex }: { cellIndex: number }) {
});
}
const entityFieldMetadata = useContext(ViewFieldContext);
const viewField = useContext(ViewFieldContext);
if (!entityFieldMetadata) {
if (!viewField) {
return null;
}
@ -37,12 +37,12 @@ export function EntityTableCell({ cellIndex }: { cellIndex: number }) {
<td
onContextMenu={(event) => handleContextMenu(event)}
style={{
width: entityFieldMetadata.columnSize,
minWidth: entityFieldMetadata.columnSize,
maxWidth: entityFieldMetadata.columnSize,
width: viewField.columnSize,
minWidth: viewField.columnSize,
maxWidth: viewField.columnSize,
}}
>
<GenericEditableCell fieldDefinition={entityFieldMetadata} />
<GenericEditableCell viewField={viewField} />
</td>
</ColumnIndexContext.Provider>
</RecoilScope>

View File

@ -1,12 +1,12 @@
import { useRecoilValue } from 'recoil';
import { viewFieldsState } from '../states/viewFieldsState';
import { viewFieldsFamilyState } from '../states/viewFieldsState';
import { ColumnHead } from './ColumnHead';
import { SelectAllCheckbox } from './SelectAllCheckbox';
export function EntityTableHeader() {
const viewFields = useRecoilValue(viewFieldsState);
const viewFields = useRecoilValue(viewFieldsFamilyState);
return (
<thead>

View File

@ -2,7 +2,7 @@ import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
import { ViewFieldContext } from '../states/ViewFieldContext';
import { viewFieldsState } from '../states/viewFieldsState';
import { viewFieldsFamilyState } from '../states/viewFieldsState';
import { CheckboxCell } from './CheckboxCell';
import { EntityTableCell } from './EntityTableCellV2';
@ -13,18 +13,18 @@ const StyledRow = styled.tr<{ selected: boolean }>`
`;
export function EntityTableRow({ rowId }: { rowId: string }) {
const entityFieldMetadataArray = useRecoilValue(viewFieldsState);
const viewFields = useRecoilValue(viewFieldsFamilyState);
return (
<StyledRow data-testid={`row-id-${rowId}`} selected={false}>
<td>
<CheckboxCell />
</td>
{entityFieldMetadataArray.map((entityFieldMetadata, columnIndex) => {
{viewFields.map((viewField, columnIndex) => {
return (
<ViewFieldContext.Provider
value={entityFieldMetadata}
key={entityFieldMetadata.columnOrder}
value={viewField}
key={viewField.columnOrder}
>
<EntityTableCell cellIndex={columnIndex} />
</ViewFieldContext.Provider>

View File

@ -1,37 +1,54 @@
import { ViewFieldDefinition } from '@/ui/table/types/ViewField';
import {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/table/types/ViewField';
import { isViewFieldChip } from '../types/guards/isViewFieldChip';
import { isViewFieldDate } from '../types/guards/isViewFieldDate';
import { isViewFieldDoubleText } from '../types/guards/isViewFieldDoubleText';
import { isViewFieldDoubleTextChip } from '../types/guards/isViewFieldDoubleTextChip';
import { isViewFieldNumber } from '../types/guards/isViewFieldNumber';
import { isViewFieldPhone } from '../types/guards/isViewFieldPhone';
import { isViewFieldRelation } from '../types/guards/isViewFieldRelation';
import { isViewFieldText } from '../types/guards/isViewFieldText';
import { isViewFieldURL } from '../types/guards/isViewFieldURL';
import { GenericEditableChipCell } from './GenericEditableChipCell';
import { GenericEditableDateCell } from './GenericEditableDateCell';
import { GenericEditableDoubleTextCell } from './GenericEditableDoubleTextCell';
import { GenericEditableDoubleTextChipCell } from './GenericEditableDoubleTextChipCell';
import { GenericEditableNumberCell } from './GenericEditableNumberCell';
import { GenericEditablePhoneCell } from './GenericEditablePhoneCell';
import { GenericEditableRelationCell } from './GenericEditableRelationCell';
import { GenericEditableTextCell } from './GenericEditableTextCell';
import { GenericEditableURLCell } from './GenericEditableURLCell';
type OwnProps = {
fieldDefinition: ViewFieldDefinition<unknown>;
viewField: ViewFieldDefinition<ViewFieldMetadata>;
};
export function GenericEditableCell({ fieldDefinition }: OwnProps) {
export function GenericEditableCell({ viewField: fieldDefinition }: OwnProps) {
if (isViewFieldText(fieldDefinition)) {
return (
<GenericEditableTextCell
viewField={fieldDefinition}
editModeHorizontalAlign="left"
/>
);
return <GenericEditableTextCell viewField={fieldDefinition} />;
} else if (isViewFieldRelation(fieldDefinition)) {
return <GenericEditableRelationCell fieldDefinition={fieldDefinition} />;
} else if (isViewFieldDoubleTextChip(fieldDefinition)) {
return <GenericEditableDoubleTextChipCell viewField={fieldDefinition} />;
} else if (isViewFieldDoubleText(fieldDefinition)) {
return <GenericEditableDoubleTextCell viewField={fieldDefinition} />;
} else if (isViewFieldPhone(fieldDefinition)) {
return <GenericEditablePhoneCell viewField={fieldDefinition} />;
} else if (isViewFieldURL(fieldDefinition)) {
return <GenericEditableURLCell viewField={fieldDefinition} />;
} else if (isViewFieldDate(fieldDefinition)) {
return <GenericEditableDateCell viewField={fieldDefinition} />;
} else if (isViewFieldNumber(fieldDefinition)) {
return <GenericEditableNumberCell viewField={fieldDefinition} />;
} else if (isViewFieldChip(fieldDefinition)) {
return (
<GenericEditableChipCell
viewField={fieldDefinition}
editModeHorizontalAlign="left"
/>
);
return <GenericEditableChipCell viewField={fieldDefinition} />;
} else {
console.warn(
`Unknown field type: ${fieldDefinition.type} in GenericEditableCell`,
`Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`,
);
return <></>;
}

View File

@ -3,7 +3,7 @@ import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../types/ViewField';
import { GenericEditableChipCellDisplayMode } from './GenericEditableChipCellDisplayMode';
import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMode';
import { GenericEditableChipCellEditMode } from './GenericEditableChipCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldChipMetadata>;
@ -14,17 +14,12 @@ type OwnProps = {
export function GenericEditableChipCell({
viewField,
editModeHorizontalAlign,
placeholder,
}: OwnProps) {
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditableTextCellEditMode
fieldName={viewField.metadata.contentFieldName}
viewFieldId={viewField.id}
placeholder={placeholder}
/>
<GenericEditableChipCellEditMode viewField={viewField} />
}
nonEditModeContent={
<GenericEditableChipCellDisplayMode fieldDefinition={viewField} />

View File

@ -0,0 +1,45 @@
import { useRecoilState } from 'recoil';
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { ViewFieldChipMetadata, ViewFieldDefinition } from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldChipMetadata>;
};
export function GenericEditableChipCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.contentFieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newText: string) {
if (newText === fieldValue) return;
setFieldValue(newText);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewField, newText);
}
}
return (
<InplaceInputTextCellEditMode
placeholder={viewField.metadata.placeHolder ?? ''}
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,39 @@
import { useRecoilValue } from 'recoil';
import { InplaceInputDateDisplayMode } from '@/ui/display/component/InplaceInputDateDisplayMode';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { ViewFieldDateMetadata, ViewFieldDefinition } from '../types/ViewField';
import { GenericEditableDateCellEditMode } from './GenericEditableDateCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDateMetadata>;
editModeHorizontalAlign?: 'left' | 'right';
};
export function GenericEditableDateCell({
viewField,
editModeHorizontalAlign,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const fieldValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditableDateCellEditMode viewField={viewField} />
}
nonEditModeContent={<InplaceInputDateDisplayMode value={fieldValue} />}
></EditableCell>
);
}

View File

@ -0,0 +1,50 @@
import { DateTime } from 'luxon';
import { useRecoilState } from 'recoil';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { EditableCellDateEditMode } from '../editable-cell/types/EditableCellDateEditMode';
import { ViewFieldDateMetadata, ViewFieldDefinition } from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDateMetadata>;
};
export function GenericEditableDateCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newDate: Date) {
const fieldValueDate = fieldValue
? DateTime.fromISO(fieldValue).toJSDate()
: null;
const newDateISO = DateTime.fromJSDate(newDate).toISO();
if (newDate === fieldValueDate || !newDateISO) return;
setFieldValue(newDateISO);
if (currentRowEntityId && updateField && newDateISO) {
updateField(currentRowEntityId, viewField, newDateISO);
}
}
return (
<EditableCellDateEditMode
value={DateTime.fromISO(fieldValue).toJSDate()}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,48 @@
import { useRecoilValue } from 'recoil';
import { InplaceInputTextDisplayMode } from '@/ui/display/component/InplaceInputTextDisplayMode';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldDoubleTextMetadata,
} from '../types/ViewField';
import { GenericEditableDoubleTextCellEditMode } from './GenericEditableDoubleTextCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDoubleTextMetadata>;
};
export function GenericEditableDoubleTextCell({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const firstValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.firstValueFieldName,
}),
);
const secondValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.secondValueFieldName,
}),
);
const displayName = `${firstValue ?? ''} ${secondValue ?? ''}`;
return (
<EditableCell
editModeContent={
<GenericEditableDoubleTextCellEditMode viewField={viewField} />
}
nonEditModeContent={
<InplaceInputTextDisplayMode>{displayName}</InplaceInputTextDisplayMode>
}
></EditableCell>
);
}

View File

@ -0,0 +1,60 @@
import { useRecoilState } from 'recoil';
import { InplaceInputDoubleTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputDoubleTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldDoubleTextMetadata,
} from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDoubleTextMetadata>;
};
export function GenericEditableDoubleTextCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [firstValue, setFirstValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.firstValueFieldName,
}),
);
const [secondValue, setSecondValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.firstValueFieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newFirstValue: string, newSecondValue: string) {
if (newFirstValue === firstValue && newSecondValue === secondValue) return;
setFirstValue(newFirstValue);
setSecondValue(newSecondValue);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewField, {
firstValue: newFirstValue,
secondValue: newSecondValue,
});
}
}
return (
<InplaceInputDoubleTextCellEditMode
firstValuePlaceholder={viewField.metadata.firstValuePlaceholder}
secondValuePlaceholder={viewField.metadata.secondValuePlaceholder}
firstValue={firstValue ?? ''}
secondValue={secondValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,28 @@
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { TableHotkeyScope } from '../types/TableHotkeyScope';
import {
ViewFieldDefinition,
ViewFieldDoubleTextChipMetadata,
} from '../types/ViewField';
import { GenericEditableDoubleTextChipCellDisplayMode } from './GenericEditableDoubleTextChipCellDisplayMode';
import { GenericEditableDoubleTextChipCellEditMode } from './GenericEditableDoubleTextChipCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
};
export function GenericEditableDoubleTextChipCell({ viewField }: OwnProps) {
return (
<EditableCell
editHotkeyScope={{ scope: TableHotkeyScope.CellDoubleTextInput }}
editModeContent={
<GenericEditableDoubleTextChipCellEditMode viewField={viewField} />
}
nonEditModeContent={
<GenericEditableDoubleTextChipCellDisplayMode viewField={viewField} />
}
></EditableCell>
);
}

View File

@ -0,0 +1,51 @@
import { useRecoilState } from 'recoil';
import { CompanyChip } from '@/companies/components/CompanyChip';
import { PersonChip } from '@/people/components/PersonChip';
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldDoubleTextChipMetadata,
} from '@/ui/table/types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
};
export function GenericEditableDoubleTextChipCellDisplayMode({
viewField,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const [firstValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.firstValueFieldName,
}),
);
const [secondValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.secondValueFieldName,
}),
);
const displayName = `${firstValue} ${secondValue}`;
switch (viewField.metadata.entityType) {
case Entity.Company: {
return <CompanyChip id={currentRowEntityId ?? ''} name={displayName} />;
}
case Entity.Person: {
return <PersonChip id={currentRowEntityId ?? ''} name={displayName} />;
}
default:
console.warn(
`Unknown relation type: "${viewField.metadata.entityType}" in GenericEditableDoubleTextChipCellDisplayMode`,
);
return <> </>;
}
}

View File

@ -0,0 +1,72 @@
import { useRecoilState } from 'recoil';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { EditableCellDoubleTextEditMode } from '../editable-cell/types/EditableCellDoubleTextEditMode';
import {
ViewFieldDefinition,
ViewFieldDoubleTextChipMetadata,
} from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldDoubleTextChipMetadata>;
};
export function GenericEditableDoubleTextChipCellEditMode({
viewField,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [firstValue, setFirstValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.firstValueFieldName,
}),
);
const [secondValue, setSecondValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.secondValueFieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newFirstValue: string, newSecondValue: string) {
const firstValueChanged = newFirstValue !== firstValue;
const secondValueChanged = newSecondValue !== secondValue;
if (firstValueChanged) {
setFirstValue(newFirstValue);
}
if (secondValueChanged) {
setSecondValue(newSecondValue);
}
if (
currentRowEntityId &&
updateField &&
(firstValueChanged || secondValueChanged)
) {
updateField(currentRowEntityId, viewField, {
firstValue: firstValueChanged ? newFirstValue : firstValue,
secondValue: secondValueChanged ? newSecondValue : secondValue,
});
}
}
return (
<EditableCellDoubleTextEditMode
firstValuePlaceholder={viewField.metadata.firstValuePlaceholder}
secondValuePlaceholder={viewField.metadata.secondValuePlaceholder}
firstValue={firstValue ?? ''}
secondValue={secondValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,41 @@
import { useRecoilValue } from 'recoil';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldNumberMetadata,
} from '../types/ViewField';
import { GenericEditableNumberCellEditMode } from './GenericEditableNumberCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldNumberMetadata>;
editModeHorizontalAlign?: 'left' | 'right';
};
export function GenericEditableNumberCell({
viewField,
editModeHorizontalAlign,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const fieldValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditableNumberCellEditMode viewField={viewField} />
}
nonEditModeContent={<>{fieldValue}</>}
></EditableCell>
);
}

View File

@ -0,0 +1,66 @@
import { useRecoilState } from 'recoil';
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldNumberMetadata,
} from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldNumberMetadata>;
};
export function GenericEditableNumberCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newText: string) {
if (newText === fieldValue) return;
try {
const numberValue = parseInt(newText);
if (isNaN(numberValue)) {
throw new Error('Not a number');
}
// TODO: find a way to store this better in DB
if (numberValue > 2000000000) {
throw new Error('Number too big');
}
console.log({ numberValue });
setFieldValue(numberValue.toString());
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewField, numberValue);
}
} catch (error) {
console.warn(
`In GenericEditableNumberCellEditMode, Invalid number: ${newText}, ${error}`,
);
}
}
return (
<InplaceInputTextCellEditMode
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,42 @@
import { useRecoilValue } from 'recoil';
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldPhoneMetadata,
} from '../types/ViewField';
import { GenericEditablePhoneCellEditMode } from './GenericEditablePhoneCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldPhoneMetadata>;
editModeHorizontalAlign?: 'left' | 'right';
};
export function GenericEditablePhoneCell({
viewField,
editModeHorizontalAlign,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const fieldValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditablePhoneCellEditMode viewField={viewField} />
}
nonEditModeContent={<InplaceInputPhoneDisplayMode value={fieldValue} />}
></EditableCell>
);
}

View File

@ -0,0 +1,48 @@
import { useRecoilState } from 'recoil';
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldPhoneMetadata,
} from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldPhoneMetadata>;
};
export function GenericEditablePhoneCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newText: string) {
if (newText === fieldValue) return;
setFieldValue(newText);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewField, newText);
}
}
return (
<InplaceInputTextCellEditMode
placeholder={viewField.metadata.placeHolder ?? ''}
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -24,9 +24,7 @@ export function GenericEditableRelationCell({
editModeHorizontalAlign={editModeHorizontalAlign}
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
editModeContent={
<GenericEditableRelationCellEditMode
viewFieldDefinition={fieldDefinition}
/>
<GenericEditableRelationCellEditMode viewField={fieldDefinition} />
}
nonEditModeContent={
<GenericEditableRelationCellDisplayMode

View File

@ -8,6 +8,7 @@ import {
ViewFieldDefinition,
ViewFieldRelationMetadata,
} from '@/ui/table/types/ViewField';
import { UserChip } from '@/users/components/UserChip';
import { getLogoUrlFromDomainName } from '~/utils';
type OwnProps = {
@ -21,6 +22,7 @@ export function GenericEditableRelationCellDisplayMode({
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: type value with generic getter
const fieldValue = useRecoilValue<any | null>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
@ -38,6 +40,14 @@ export function GenericEditableRelationCellDisplayMode({
/>
);
}
case Entity.User: {
return (
<UserChip
id={fieldValue?.id ?? ''}
name={fieldValue?.displayName ?? ''}
/>
);
}
default:
console.warn(
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,

View File

@ -1,24 +1,23 @@
import { useRecoilState } from 'recoil';
import { CompanyPickerCell } from '@/companies/components/CompanyPickerCell';
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
import { Entity } from '@/ui/relation-picker/types/EntityTypeForSelect';
import { useEditableCell } from '@/ui/table/editable-cell/hooks/useEditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import {
ViewFieldDefinition,
ViewFieldRelationMetadata,
} from '@/ui/table/types/ViewField';
import { UserPicker } from '@/users/components/UserPicker';
type OwnProps = {
viewFieldDefinition: ViewFieldDefinition<ViewFieldRelationMetadata>;
viewField: ViewFieldDefinition<ViewFieldRelationMetadata>;
};
export function GenericEditableRelationCellEditMode({
viewFieldDefinition,
}: OwnProps) {
export function GenericEditableRelationCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const { closeEditableCell } = useEditableCell();
@ -26,7 +25,7 @@ export function GenericEditableRelationCellEditMode({
const [fieldValueEntity] = useRecoilState<any | null>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewFieldDefinition.metadata.fieldName,
fieldName: viewField.metadata.fieldName,
}),
);
@ -38,11 +37,7 @@ export function GenericEditableRelationCellEditMode({
currentRowEntityId &&
updateEntityField
) {
updateEntityField(
currentRowEntityId,
viewFieldDefinition.id,
newFieldEntity,
);
updateEntityField(currentRowEntityId, viewField, newFieldEntity);
}
closeEditableCell();
@ -52,7 +47,7 @@ export function GenericEditableRelationCellEditMode({
closeEditableCell();
}
switch (viewFieldDefinition.metadata.relationType) {
switch (viewField.metadata.relationType) {
case Entity.Company: {
return (
<CompanyPickerCell
@ -62,9 +57,18 @@ export function GenericEditableRelationCellEditMode({
/>
);
}
case Entity.User: {
return (
<UserPicker
userId={fieldValueEntity?.id ?? null}
onSubmit={handleEntitySubmit}
onCancel={handleCancel}
/>
);
}
default:
console.warn(
`Unknown relation type: "${viewFieldDefinition.metadata.relationType}" in GenericEditableRelationCellEditMode`,
`Unknown relation type: "${viewField.metadata.relationType}" in GenericEditableRelationCellEditMode`,
);
return <></>;
}

View File

@ -12,13 +12,11 @@ import { GenericEditableTextCellEditMode } from './GenericEditableTextCellEditMo
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldTextMetadata>;
editModeHorizontalAlign?: 'left' | 'right';
placeholder?: string;
};
export function GenericEditableTextCell({
viewField,
editModeHorizontalAlign,
placeholder,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
@ -33,11 +31,7 @@ export function GenericEditableTextCell({
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={
<GenericEditableTextCellEditMode
fieldName={viewField.metadata.fieldName}
viewFieldId={viewField.id}
placeholder={placeholder}
/>
<GenericEditableTextCellEditMode viewField={viewField} />
}
nonEditModeContent={
<InplaceInputTextDisplayMode>{fieldValue}</InplaceInputTextDisplayMode>

View File

@ -1,28 +1,24 @@
import { useRecoilState } from 'recoil';
import { useUpdateEntityField } from '@/people/hooks/useUpdateEntityField';
import { InplaceInputTextEditMode } from '@/ui/inplace-input/components/InplaceInputTextEditMode';
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { ViewFieldDefinition, ViewFieldTextMetadata } from '../types/ViewField';
type OwnProps = {
fieldName: string;
viewFieldId: string;
placeholder?: string;
viewField: ViewFieldDefinition<ViewFieldTextMetadata>;
};
export function GenericEditableTextCellEditMode({
fieldName,
viewFieldId,
placeholder,
}: OwnProps) {
export function GenericEditableTextCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName,
fieldName: viewField.metadata.fieldName,
}),
);
@ -34,13 +30,13 @@ export function GenericEditableTextCellEditMode({
setFieldValue(newText);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewFieldId, newText);
updateField(currentRowEntityId, viewField, newText);
}
}
return (
<InplaceInputTextEditMode
placeholder={placeholder ?? ''}
<InplaceInputTextCellEditMode
placeholder={viewField.metadata.placeHolder ?? ''}
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}

View File

@ -0,0 +1,37 @@
import { useRecoilValue } from 'recoil';
import { InplaceInputURLDisplayMode } from '@/ui/display/component/InplaceInputURLDisplayMode';
import { EditableCell } from '@/ui/table/editable-cell/components/EditableCell';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { ViewFieldDefinition, ViewFieldURLMetadata } from '../types/ViewField';
import { GenericEditableURLCellEditMode } from './GenericEditableURLCellEditMode';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldURLMetadata>;
editModeHorizontalAlign?: 'left' | 'right';
};
export function GenericEditableURLCell({
viewField,
editModeHorizontalAlign,
}: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
const fieldValue = useRecoilValue<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
return (
<EditableCell
editModeHorizontalAlign={editModeHorizontalAlign}
editModeContent={<GenericEditableURLCellEditMode viewField={viewField} />}
nonEditModeContent={<InplaceInputURLDisplayMode value={fieldValue} />}
></EditableCell>
);
}

View File

@ -0,0 +1,45 @@
import { useRecoilState } from 'recoil';
import { InplaceInputTextCellEditMode } from '@/ui/inplace-input/components/InplaceInputTextCellEditMode';
import { useCurrentRowEntityId } from '@/ui/table/hooks/useCurrentEntityId';
import { useUpdateEntityField } from '@/ui/table/hooks/useUpdateEntityField';
import { tableEntityFieldFamilySelector } from '@/ui/table/states/tableEntityFieldFamilySelector';
import { ViewFieldDefinition, ViewFieldURLMetadata } from '../types/ViewField';
type OwnProps = {
viewField: ViewFieldDefinition<ViewFieldURLMetadata>;
};
export function GenericEditableURLCellEditMode({ viewField }: OwnProps) {
const currentRowEntityId = useCurrentRowEntityId();
// TODO: we could use a hook that would return the field value with the right type
const [fieldValue, setFieldValue] = useRecoilState<string>(
tableEntityFieldFamilySelector({
entityId: currentRowEntityId ?? '',
fieldName: viewField.metadata.fieldName,
}),
);
const updateField = useUpdateEntityField();
function handleSubmit(newText: string) {
if (newText === fieldValue) return;
setFieldValue(newText);
if (currentRowEntityId && updateField) {
updateField(currentRowEntityId, viewField, newText);
}
}
return (
<InplaceInputTextCellEditMode
placeholder={viewField.metadata.placeHolder ?? ''}
autoFocus
value={fieldValue ?? ''}
onSubmit={handleSubmit}
/>
);
}

View File

@ -0,0 +1,37 @@
import { FilterDefinition } from '@/ui/filter-n-sort/types/FilterDefinition';
import { useSetEntityTableData } from '@/ui/table/hooks/useSetEntityTableData';
import {
ViewFieldDefinition,
ViewFieldMetadata,
} from '@/ui/table/types/ViewField';
import { defaultOrderBy } from '../../../people/queries';
export function GenericEntityTableData({
useGetRequest,
getRequestResultKey,
orderBy = defaultOrderBy,
whereFilters,
viewFields,
filterDefinitionArray,
}: {
useGetRequest: any;
getRequestResultKey: string;
orderBy?: any;
whereFilters?: any;
viewFields: ViewFieldDefinition<ViewFieldMetadata>[];
filterDefinitionArray: FilterDefinition[];
}) {
const setEntityTableData = useSetEntityTableData();
useGetRequest({
variables: { orderBy, where: whereFilters },
onCompleted: (data: any) => {
const entities = data[getRequestResultKey] ?? [];
setEntityTableData(entities, viewFields, filterDefinitionArray);
},
});
return <></>;
}