diff --git a/front/src/modules/companies/editable-field/components/CompanyAccountOwnerEditableField.tsx b/front/src/modules/companies/editable-field/components/CompanyAccountOwnerEditableField.tsx deleted file mode 100644 index b5ed11bb8..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyAccountOwnerEditableField.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { IconUserCircle } from '@/ui/icon'; -import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { UserChip } from '@/users/components/UserChip'; -import { Company, User } from '~/generated/graphql'; - -import { CompanyAccountOwnerPickerFieldEditMode } from './CompanyAccountOwnerPickerFieldEditMode'; - -type OwnProps = { - company: Pick & { - accountOwner?: Pick | null; - }; -}; - -export function CompanyAccountOwnerEditableField({ company }: OwnProps) { - return ( - - - } - editModeContent={ - - } - displayModeContent={ - company.accountOwner?.displayName ? ( - - ) : ( - <> - ) - } - isDisplayModeContentEmpty={!company.accountOwner} - isDisplayModeFixHeight={true} - /> - - - ); -} diff --git a/front/src/modules/companies/editable-field/components/CompanyAccountOwnerPickerFieldEditMode.tsx b/front/src/modules/companies/editable-field/components/CompanyAccountOwnerPickerFieldEditMode.tsx deleted file mode 100644 index 0b63a3a06..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyAccountOwnerPickerFieldEditMode.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import styled from '@emotion/styled'; - -import { CompanyAccountOwnerPicker } from '@/companies/components/CompanyAccountOwnerPicker'; -import { useEditableField } from '@/ui/editable-field/hooks/useEditableField'; -import { Company, User } from '~/generated/graphql'; - -const StyledContainer = styled.div` - left: 0px; - position: absolute; - top: -8px; -`; - -export type OwnProps = { - company: Pick & { - accountOwner?: Pick | null; - }; - onSubmit?: () => void; - onCancel?: () => void; -}; - -export function CompanyAccountOwnerPickerFieldEditMode({ - company, - onSubmit, - onCancel, -}: OwnProps) { - const { closeEditableField } = useEditableField(); - - function handleSubmit() { - closeEditableField(); - onSubmit?.(); - } - - function handleCancel() { - closeEditableField(); - onCancel?.(); - } - - return ( - - - - ); -} diff --git a/front/src/modules/companies/editable-field/components/CompanyAddressEditableField.tsx b/front/src/modules/companies/editable-field/components/CompanyAddressEditableField.tsx deleted file mode 100644 index fe7430359..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyAddressEditableField.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { IconMap } from '@/ui/icon'; -import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql'; - -type OwnProps = { - company: Pick; -}; - -export function CompanyAddressEditableField({ company }: OwnProps) { - const [internalValue, setInternalValue] = useState(company.address); - - const [updateCompany] = useUpdateOneCompanyMutation(); - - useEffect(() => { - setInternalValue(company.address); - }, [company.address]); - - async function handleChange(newValue: string) { - setInternalValue(newValue); - } - - async function handleSubmit() { - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - address: internalValue ?? '', - }, - }, - }); - } - - async function handleCancel() { - setInternalValue(company.address); - } - - return ( - - } - editModeContent={ - { - handleChange(newValue); - }} - /> - } - displayModeContent={internalValue ?? ''} - isDisplayModeContentEmpty={!(internalValue !== '')} - /> - - ); -} diff --git a/front/src/modules/companies/editable-field/components/CompanyCreatedAtEditableField.tsx b/front/src/modules/companies/editable-field/components/CompanyCreatedAtEditableField.tsx deleted file mode 100644 index fd614ba03..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyCreatedAtEditableField.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { EditableFieldEditModeDate } from '@/ui/editable-field/variants/components/EditableFieldEditModeDate'; -import { IconCalendar } from '@/ui/icon'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql'; -import { formatToHumanReadableDate } from '~/utils'; -import { parseDate } from '~/utils/date-utils'; - -type OwnProps = { - company: Pick; -}; - -export function CompanyCreatedAtEditableField({ company }: OwnProps) { - const [internalValue, setInternalValue] = useState(company.createdAt); - - const [updateCompany] = useUpdateOneCompanyMutation(); - - useEffect(() => { - setInternalValue(company.createdAt); - }, [company.createdAt]); - - // TODO: refactor change and submit - async function handleChange(newValue: string) { - setInternalValue(newValue); - - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - createdAt: newValue ?? '', - }, - }, - }); - } - - async function handleSubmit() { - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - createdAt: internalValue ?? '', - }, - }, - }); - } - - async function handleCancel() { - setInternalValue(company.createdAt); - } - - return ( - - } - editModeContent={ - - } - displayModeContent={ - internalValue !== '' - ? formatToHumanReadableDate(parseDate(internalValue).toJSDate()) - : 'No date' - } - isDisplayModeContentEmpty={!(internalValue !== '')} - /> - - ); -} diff --git a/front/src/modules/companies/editable-field/components/CompanyDomainNameEditableField.tsx b/front/src/modules/companies/editable-field/components/CompanyDomainNameEditableField.tsx deleted file mode 100644 index 00f13d372..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyDomainNameEditableField.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldDisplayURL } from '@/ui/editable-field/components/FieldDisplayURL'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { IconLink } from '@/ui/icon'; -import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql'; - -type OwnProps = { - company: Pick; -}; - -export function CompanyDomainNameEditableField({ company }: OwnProps) { - const [internalValue, setInternalValue] = useState(company.domainName); - - const [updateCompany] = useUpdateOneCompanyMutation(); - - useEffect(() => { - setInternalValue(company.domainName); - }, [company.domainName]); - - async function handleChange(newValue: string) { - setInternalValue(newValue); - } - - async function handleSubmit() { - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - domainName: internalValue ?? '', - }, - }, - }); - } - - async function handleCancel() { - setInternalValue(company.domainName); - } - - return ( - - } - onCancel={handleCancel} - onSubmit={handleSubmit} - editModeContent={ - { - handleChange(newValue); - }} - /> - } - displayModeContent={} - useEditButton - isDisplayModeContentEmpty={!(internalValue !== '')} - /> - - ); -} diff --git a/front/src/modules/companies/editable-field/components/CompanyEmployeesEditableField.tsx b/front/src/modules/companies/editable-field/components/CompanyEmployeesEditableField.tsx deleted file mode 100644 index c4cb9d009..000000000 --- a/front/src/modules/companies/editable-field/components/CompanyEmployeesEditableField.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useEffect, useState } from 'react'; - -import { EditableField } from '@/ui/editable-field/components/EditableField'; -import { FieldContext } from '@/ui/editable-field/states/FieldContext'; -import { IconUsers } from '@/ui/icon'; -import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; -import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; -import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql'; -import { - canBeCastAsIntegerOrNull, - castAsIntegerOrNull, -} from '~/utils/cast-as-integer-or-null'; - -type OwnProps = { - company: Pick; -}; - -export function CompanyEmployeesEditableField({ company }: OwnProps) { - const [internalValue, setInternalValue] = useState( - company.employees?.toString(), - ); - - const [updateCompany] = useUpdateOneCompanyMutation(); - - useEffect(() => { - setInternalValue(company.employees?.toString()); - }, [company.employees]); - - async function handleChange(newValue: string) { - setInternalValue(newValue); - } - - async function handleSubmit() { - if (!canBeCastAsIntegerOrNull(internalValue)) { - handleCancel(); - return; - } - - const valueCastedAsNumberOrNull = castAsIntegerOrNull(internalValue); - - await updateCompany({ - variables: { - where: { - id: company.id, - }, - data: { - employees: valueCastedAsNumberOrNull, - }, - }, - }); - - setInternalValue(valueCastedAsNumberOrNull?.toString()); - } - - async function handleCancel() { - setInternalValue(company.employees?.toString()); - } - - return ( - - } - editModeContent={ - { - handleChange(newValue); - }} - /> - } - displayModeContent={internalValue} - isDisplayModeContentEmpty={!(internalValue && internalValue !== '0')} - /> - - ); -} diff --git a/front/src/modules/companies/queries/show.ts b/front/src/modules/companies/queries/show.ts index ebc4d63ab..a372632a5 100644 --- a/front/src/modules/companies/queries/show.ts +++ b/front/src/modules/companies/queries/show.ts @@ -1,5 +1,7 @@ import { gql } from '@apollo/client'; +import { useSetRecoilState } from 'recoil'; +import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState'; import { useGetCompanyQuery } from '~/generated/graphql'; export const GET_COMPANY = gql` @@ -33,5 +35,13 @@ export const GET_COMPANY = gql` `; export function useCompanyQuery(id: string) { - return useGetCompanyQuery({ variables: { where: { id } } }); + const updateCompanyShowPage = useSetRecoilState( + genericEntitiesFamilyState(id), + ); + return useGetCompanyQuery({ + variables: { where: { id } }, + onCompleted: (data) => { + updateCompanyShowPage(data?.findUniqueCompany); + }, + }); } diff --git a/front/src/modules/pipeline/constants/pipelineViewFields.tsx b/front/src/modules/pipeline/constants/pipelineViewFields.tsx index 59d7df110..4bce73430 100644 --- a/front/src/modules/pipeline/constants/pipelineViewFields.tsx +++ b/front/src/modules/pipeline/constants/pipelineViewFields.tsx @@ -61,6 +61,7 @@ export const pipelineViewFields: ViewFieldDefinition[] = [ type: 'relation', fieldName: 'pointOfContact', relationType: Entity.Person, + useEditButton: true, }, isVisible: true, } satisfies ViewFieldDefinition, diff --git a/front/src/modules/ui/board/states/FieldDefinitionContext.ts b/front/src/modules/ui/board/states/FieldDefinitionContext.ts index 0a7fb8b2a..1b165791d 100644 --- a/front/src/modules/ui/board/states/FieldDefinitionContext.ts +++ b/front/src/modules/ui/board/states/FieldDefinitionContext.ts @@ -1,7 +1,10 @@ import { createContext } from 'react'; import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition'; -import { FieldMetadata } from '@/ui/editable-field/types/FieldMetadata'; +import { + FieldMetadata, + FieldType, +} from '@/ui/editable-field/types/FieldMetadata'; export const FieldDefinitionContext = createContext< FieldDefinition @@ -9,6 +12,6 @@ export const FieldDefinitionContext = createContext< id: '', label: '', icon: undefined, - type: '', + type: 'unknown' satisfies FieldType, metadata: {} as FieldMetadata, }); diff --git a/front/src/modules/ui/editable-field/components/GenericEditableField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableField.tsx index c70881722..7c6a40b42 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableField.tsx @@ -5,10 +5,14 @@ import { isFieldDate } from '../types/guards/isFieldDate'; import { isFieldNumber } from '../types/guards/isFieldNumber'; import { isFieldProbability } from '../types/guards/isFieldProbability'; import { isFieldRelation } from '../types/guards/isFieldRelation'; +import { isFieldText } from '../types/guards/isFieldText'; +import { isFieldURL } from '../types/guards/isFieldURL'; import { GenericEditableDateField } from './GenericEditableDateField'; import { GenericEditableNumberField } from './GenericEditableNumberField'; import { GenericEditableRelationField } from './GenericEditableRelationField'; +import { GenericEditableTextField } from './GenericEditableTextField'; +import { GenericEditableURLField } from './GenericEditableURLField'; import { ProbabilityEditableField } from './ProbabilityEditableField'; export function GenericEditableField() { @@ -22,9 +26,13 @@ export function GenericEditableField() { return ; } else if (isFieldProbability(fieldDefinition)) { return ; + } else if (isFieldURL(fieldDefinition)) { + return ; + } else if (isFieldText(fieldDefinition)) { + return ; } else { console.warn( - `Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`, + `Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`, ); return <>; } diff --git a/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx index 4ce2ec326..ebf5dec19 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableNumberFieldEditMode.tsx @@ -69,6 +69,7 @@ export function GenericEditableNumberFieldEditMode() {
{ handleChange(newValue); diff --git a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx index ae2c42ec2..596b60dea 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableRelationField.tsx @@ -34,7 +34,7 @@ export function GenericEditableRelationField() { ); } + case Entity.User: { + return ( + + ); + } default: console.warn( `Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}" diff --git a/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx index 9b4c08c4f..18d9d2ab9 100644 --- a/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx +++ b/front/src/modules/ui/editable-field/components/GenericEditableRelationFieldEditMode.tsx @@ -3,9 +3,9 @@ import styled from '@emotion/styled'; import { useRecoilState } from 'recoil'; import { PeoplePicker } from '@/people/components/PeoplePicker'; -import { ViewFieldRelationValue } from '@/ui/editable-field/types/ViewField'; import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect'; import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; +import { UserPicker } from '@/users/components/UserPicker'; import { useEditableField } from '../hooks/useEditableField'; import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; @@ -13,7 +13,10 @@ import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitio import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; import { FieldDefinition } from '../types/FieldDefinition'; -import { FieldRelationMetadata } from '../types/FieldMetadata'; +import { + FieldRelationMetadata, + FieldRelationValue, +} from '../types/FieldMetadata'; const RelationPickerContainer = styled.div` left: 0px; @@ -28,7 +31,7 @@ function RelationPicker({ handleCancel, }: { fieldDefinition: FieldDefinition; - fieldValue: ViewFieldRelationValue; + fieldValue: FieldRelationValue; handleEntitySubmit: (newRelationId: EntityForSelect | null) => void; handleCancel: () => void; }) { @@ -36,7 +39,16 @@ function RelationPicker({ case Entity.Person: { return ( + ); + } + case Entity.User: { + return ( + @@ -46,7 +58,7 @@ function RelationPicker({ console.warn( `Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`, ); - return <> ; + return <>; } } diff --git a/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx new file mode 100644 index 000000000..038d1484d --- /dev/null +++ b/front/src/modules/ui/editable-field/components/GenericEditableTextField.tsx @@ -0,0 +1,41 @@ +import { useContext } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; + +import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext'; +import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { FieldContext } from '../states/FieldContext'; +import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldNumberMetadata } from '../types/FieldMetadata'; + +import { EditableField } from './EditableField'; +import { GenericEditableTextFieldEditMode } from './GenericEditableTextFieldEditMode'; + +export function GenericEditableTextField() { + const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); + const currentEditableFieldDefinition = useContext( + EditableFieldDefinitionContext, + ) as FieldDefinition; + + const fieldValue = useRecoilValue( + genericEntityFieldFamilySelector({ + entityId: currentEditableFieldEntityId ?? '', + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', + }), + ); + + return ( + + } + displayModeContent={fieldValue} + isDisplayModeContentEmpty={!fieldValue} + /> + + ); +} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableTextFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableTextFieldEditMode.tsx new file mode 100644 index 000000000..815fb2497 --- /dev/null +++ b/front/src/modules/ui/editable-field/components/GenericEditableTextFieldEditMode.tsx @@ -0,0 +1,72 @@ +import { useContext, useRef, useState } from 'react'; +import { useRecoilState } from 'recoil'; + +import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; + +import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; +import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; +import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext'; +import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldTextMetadata } from '../types/FieldMetadata'; + +export function GenericEditableTextFieldEditMode() { + const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); + const currentEditableFieldDefinition = useContext( + EditableFieldDefinitionContext, + ) as FieldDefinition; + + // TODO: we could use a hook that would return the field value with the right type + const [fieldValue, setFieldValue] = useRecoilState( + genericEntityFieldFamilySelector({ + entityId: currentEditableFieldEntityId ?? '', + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', + }), + ); + + const [internalValue, setInternalValue] = useState(fieldValue); + + const updateField = useUpdateGenericEntityField(); + + const wrapperRef = useRef(null); + + useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel); + + function handleSubmit() { + if (internalValue === fieldValue) return; + + setFieldValue(internalValue); + + if (currentEditableFieldEntityId && updateField) { + updateField( + currentEditableFieldEntityId, + currentEditableFieldDefinition, + internalValue, + ); + } + } + + function onCancel() { + setFieldValue(fieldValue); + } + + function handleChange(newValue: string) { + setInternalValue(newValue); + } + + return ( +
+ { + handleChange(newValue); + }} + /> +
+ ); +} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx b/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx new file mode 100644 index 000000000..322290d8f --- /dev/null +++ b/front/src/modules/ui/editable-field/components/GenericEditableURLField.tsx @@ -0,0 +1,43 @@ +import { useContext } from 'react'; +import { useRecoilValue } from 'recoil'; + +import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope'; + +import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext'; +import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { FieldContext } from '../states/FieldContext'; +import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldNumberMetadata } from '../types/FieldMetadata'; + +import { EditableField } from './EditableField'; +import { FieldDisplayURL } from './FieldDisplayURL'; +import { GenericEditableURLFieldEditMode } from './GenericEditableURLFieldEditMode'; + +export function GenericEditableURLField() { + const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); + const currentEditableFieldDefinition = useContext( + EditableFieldDefinitionContext, + ) as FieldDefinition; + + const fieldValue = useRecoilValue( + genericEntityFieldFamilySelector({ + entityId: currentEditableFieldEntityId ?? '', + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', + }), + ); + + return ( + + } + displayModeContent={} + isDisplayModeContentEmpty={!fieldValue} + /> + + ); +} diff --git a/front/src/modules/ui/editable-field/components/GenericEditableURLFieldEditMode.tsx b/front/src/modules/ui/editable-field/components/GenericEditableURLFieldEditMode.tsx new file mode 100644 index 000000000..a0e3b229f --- /dev/null +++ b/front/src/modules/ui/editable-field/components/GenericEditableURLFieldEditMode.tsx @@ -0,0 +1,74 @@ +import { useContext, useRef, useState } from 'react'; +import { useRecoilState } from 'recoil'; + +import { TextInputEdit } from '@/ui/input/text/components/TextInputEdit'; + +import { useRegisterCloseFieldHandlers } from '../hooks/useRegisterCloseFieldHandlers'; +import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField'; +import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext'; +import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext'; +import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector'; +import { FieldDefinition } from '../types/FieldDefinition'; +import { FieldURLMetadata } from '../types/FieldMetadata'; + +// This one is very similar to GenericEditableTextFieldEditMode +// We could probably merge them since FieldURLMetadata is basically a FieldTextMetadata +export function GenericEditableURLFieldEditMode() { + const currentEditableFieldEntityId = useContext(EditableFieldEntityIdContext); + const currentEditableFieldDefinition = useContext( + EditableFieldDefinitionContext, + ) as FieldDefinition; + + // TODO: we could use a hook that would return the field value with the right type + const [fieldValue, setFieldValue] = useRecoilState( + genericEntityFieldFamilySelector({ + entityId: currentEditableFieldEntityId ?? '', + fieldName: currentEditableFieldDefinition + ? currentEditableFieldDefinition.metadata.fieldName + : '', + }), + ); + + const [internalValue, setInternalValue] = useState(fieldValue); + + const updateField = useUpdateGenericEntityField(); + + const wrapperRef = useRef(null); + + useRegisterCloseFieldHandlers(wrapperRef, handleSubmit, onCancel); + + function handleSubmit() { + if (internalValue === fieldValue) return; + + setFieldValue(internalValue); + + if (currentEditableFieldEntityId && updateField) { + updateField( + currentEditableFieldEntityId, + currentEditableFieldDefinition, + internalValue, + ); + } + } + + function onCancel() { + setFieldValue(fieldValue); + } + + function handleChange(newValue: string) { + setInternalValue(newValue); + } + + return ( +
+ { + handleChange(newValue); + }} + /> +
+ ); +} diff --git a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts index 545bc86d7..1e6b2e1aa 100644 --- a/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts +++ b/front/src/modules/ui/editable-field/hooks/useUpdateGenericEntityField.ts @@ -1,7 +1,5 @@ import { useContext } from 'react'; -import { isFieldChip } from '@/ui/editable-field/types/guards/isFieldChip'; - import { EditableFieldMutationContext } from '../states/EditableFieldMutationContext'; import { FieldDefinition } from '../types/FieldDefinition'; import { @@ -27,6 +25,7 @@ import { FieldURLMetadata, FieldURLValue, } from '../types/FieldMetadata'; +import { isFieldChip } from '../types/guards/isFieldChip'; import { isFieldChipValue } from '../types/guards/isFieldChipValue'; import { isFieldDate } from '../types/guards/isFieldDate'; import { isFieldDateValue } from '../types/guards/isFieldDateValue'; @@ -53,31 +52,30 @@ export function useUpdateGenericEntityField() { const [updateEntity] = useUpdateEntityMutation(); return function updateEntityField< - MetadataType extends FieldMetadata, - ValueType extends MetadataType extends FieldDoubleTextMetadata + ValueType extends FieldMetadata extends FieldDoubleTextMetadata ? FieldDoubleTextValue - : MetadataType extends FieldTextMetadata + : FieldMetadata extends FieldTextMetadata ? FieldTextValue - : MetadataType extends FieldPhoneMetadata + : FieldMetadata extends FieldPhoneMetadata ? FieldPhoneValue - : MetadataType extends FieldURLMetadata + : FieldMetadata extends FieldURLMetadata ? FieldURLValue - : MetadataType extends FieldNumberMetadata + : FieldMetadata extends FieldNumberMetadata ? FieldNumberValue - : MetadataType extends FieldDateMetadata + : FieldMetadata extends FieldDateMetadata ? FieldDateValue - : MetadataType extends FieldChipMetadata + : FieldMetadata extends FieldChipMetadata ? FieldChipValue - : MetadataType extends FieldDoubleTextChipMetadata + : FieldMetadata extends FieldDoubleTextChipMetadata ? FieldDoubleTextChipValue - : MetadataType extends FieldRelationMetadata + : FieldMetadata extends FieldRelationMetadata ? FieldRelationValue - : MetadataType extends FieldProbabilityMetadata + : FieldMetadata extends FieldProbabilityMetadata ? FieldProbabilityValue : unknown, >( currentEntityId: string, - field: FieldDefinition, + field: FieldDefinition, newFieldValue: ValueType, ) { const newFieldValueUnknown = newFieldValue as unknown; diff --git a/front/src/modules/ui/editable-field/states/EditableFieldDefinitionContext.ts b/front/src/modules/ui/editable-field/states/EditableFieldDefinitionContext.ts index 2ce04ca7b..6a804aeb7 100644 --- a/front/src/modules/ui/editable-field/states/EditableFieldDefinitionContext.ts +++ b/front/src/modules/ui/editable-field/states/EditableFieldDefinitionContext.ts @@ -1,8 +1,8 @@ import { createContext } from 'react'; import { FieldDefinition } from '../types/FieldDefinition'; -import { ViewFieldMetadata } from '../types/ViewField'; +import { FieldMetadata } from '../types/FieldMetadata'; export const EditableFieldDefinitionContext = createContext< - FieldDefinition ->({} as FieldDefinition); + FieldDefinition +>({} as FieldDefinition); diff --git a/front/src/modules/ui/editable-field/types/FieldDefinition.ts b/front/src/modules/ui/editable-field/types/FieldDefinition.ts index 0cb858308..41cf30e36 100644 --- a/front/src/modules/ui/editable-field/types/FieldDefinition.ts +++ b/front/src/modules/ui/editable-field/types/FieldDefinition.ts @@ -1,9 +1,9 @@ -import { FieldMetadata } from './FieldMetadata'; +import { FieldMetadata, FieldType } from './FieldMetadata'; export type FieldDefinition = { id: string; label: string; icon?: JSX.Element; - type: string; + type: FieldType; metadata: T; }; diff --git a/front/src/modules/ui/editable-field/types/FieldMetadata.ts b/front/src/modules/ui/editable-field/types/FieldMetadata.ts index 0b2e598b3..b7afa92f9 100644 --- a/front/src/modules/ui/editable-field/types/FieldMetadata.ts +++ b/front/src/modules/ui/editable-field/types/FieldMetadata.ts @@ -2,6 +2,7 @@ import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelec import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; export type FieldType = + | 'unknown' | 'text' | 'relation' | 'chip' @@ -14,41 +15,36 @@ export type FieldType = | 'probability'; export type FieldTextMetadata = { - type: 'text'; placeHolder: string; fieldName: string; }; export type FieldPhoneMetadata = { - type: 'phone'; placeHolder: string; fieldName: string; }; export type FieldURLMetadata = { - type: 'url'; placeHolder: string; fieldName: string; }; export type FieldDateMetadata = { - type: 'date'; fieldName: string; }; export type FieldNumberMetadata = { - type: 'number'; fieldName: string; + placeHolder: string; }; export type FieldRelationMetadata = { - type: 'relation'; relationType: Entity; fieldName: string; + useEditButton?: boolean; }; export type FieldChipMetadata = { - type: 'chip'; relationType: Entity; contentFieldName: string; urlFieldName: string; @@ -56,7 +52,6 @@ export type FieldChipMetadata = { }; export type FieldDoubleTextMetadata = { - type: 'double-text'; firstValueFieldName: string; firstValuePlaceholder: string; secondValueFieldName: string; @@ -64,7 +59,6 @@ export type FieldDoubleTextMetadata = { }; export type FieldDoubleTextChipMetadata = { - type: 'double-text-chip'; firstValueFieldName: string; firstValuePlaceholder: string; secondValueFieldName: string; @@ -74,11 +68,10 @@ export type FieldDoubleTextChipMetadata = { }; export type FieldProbabilityMetadata = { - type: 'probability'; fieldName: string; }; -export type FieldMetadata = { type: FieldType } & ( +export type FieldMetadata = | FieldTextMetadata | FieldRelationMetadata | FieldChipMetadata @@ -88,8 +81,7 @@ export type FieldMetadata = { type: FieldType } & ( | FieldURLMetadata | FieldNumberMetadata | FieldDateMetadata - | FieldProbabilityMetadata -); + | FieldProbabilityMetadata; export type FieldTextValue = string; diff --git a/front/src/modules/ui/editable-field/types/ViewField.ts b/front/src/modules/ui/editable-field/types/ViewField.ts index 24b04bdce..f1386e15c 100644 --- a/front/src/modules/ui/editable-field/types/ViewField.ts +++ b/front/src/modules/ui/editable-field/types/ViewField.ts @@ -46,6 +46,7 @@ export type ViewFieldRelationMetadata = { type: 'relation'; relationType: Entity; fieldName: string; + useEditButton?: boolean; }; export type ViewFieldChipMetadata = { diff --git a/front/src/pages/auth/__stories__/CreateProfile.stories.tsx b/front/src/pages/auth/__stories__/CreateProfile.stories.tsx index 72a40a548..ad89c28b2 100644 --- a/front/src/pages/auth/__stories__/CreateProfile.stories.tsx +++ b/front/src/pages/auth/__stories__/CreateProfile.stories.tsx @@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/testing-library'; import { graphql } from 'msw'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -16,7 +17,7 @@ const meta: Meta = { title: 'Pages/Auth/CreateProfile', component: CreateProfile, decorators: [PageDecorator], - args: { currentPath: '/create/profile' }, + args: { routePath: AppPath.CreateProfile }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: [ diff --git a/front/src/pages/auth/__stories__/CreateWorkspace.stories.tsx b/front/src/pages/auth/__stories__/CreateWorkspace.stories.tsx index 80b43cf1c..ac8fd4a9b 100644 --- a/front/src/pages/auth/__stories__/CreateWorkspace.stories.tsx +++ b/front/src/pages/auth/__stories__/CreateWorkspace.stories.tsx @@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/testing-library'; import { graphql } from 'msw'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -16,7 +17,7 @@ const meta: Meta = { title: 'Pages/Auth/CreateWorkspace', component: CreateWorkspace, decorators: [PageDecorator], - args: { currentPath: '/create/workspace' }, + args: { routePath: AppPath.CreateWorkspace }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: [ diff --git a/front/src/pages/auth/__stories__/SignInUp.stories.tsx b/front/src/pages/auth/__stories__/SignInUp.stories.tsx index cee551bd5..ffd41aab9 100644 --- a/front/src/pages/auth/__stories__/SignInUp.stories.tsx +++ b/front/src/pages/auth/__stories__/SignInUp.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { fireEvent, within } from '@storybook/testing-library'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -13,7 +14,7 @@ const meta: Meta = { title: 'Pages/Auth/SignInUp', component: SignInUp, decorators: [PageDecorator], - args: { currentPath: '/sign-in' }, + args: { routePath: AppPath.SignIn }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/companies/CompanyShow.tsx b/front/src/pages/companies/CompanyShow.tsx index f3396118a..b914e9a1e 100644 --- a/front/src/pages/companies/CompanyShow.tsx +++ b/front/src/pages/companies/CompanyShow.tsx @@ -3,25 +3,29 @@ import { useTheme } from '@emotion/react'; import { Timeline } from '@/activities/timeline/components/Timeline'; import { CompanyTeam } from '@/companies/components/CompanyTeam'; -import { CompanyAccountOwnerEditableField } from '@/companies/editable-field/components/CompanyAccountOwnerEditableField'; -import { CompanyAddressEditableField } from '@/companies/editable-field/components/CompanyAddressEditableField'; -import { CompanyCreatedAtEditableField } from '@/companies/editable-field/components/CompanyCreatedAtEditableField'; -import { CompanyDomainNameEditableField } from '@/companies/editable-field/components/CompanyDomainNameEditableField'; -import { CompanyEmployeesEditableField } from '@/companies/editable-field/components/CompanyEmployeesEditableField'; import { useCompanyQuery } from '@/companies/queries'; import { useFavorites } from '@/favorites/hooks/useFavorites'; +import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField'; import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox'; +import { EditableFieldDefinitionContext } from '@/ui/editable-field/states/EditableFieldDefinitionContext'; +import { EditableFieldEntityIdContext } from '@/ui/editable-field/states/EditableFieldEntityIdContext'; +import { EditableFieldMutationContext } from '@/ui/editable-field/states/EditableFieldMutationContext'; import { IconBuildingSkyscraper } from '@/ui/icon'; import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer'; import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer'; import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer'; import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard'; -import { CommentableType } from '~/generated/graphql'; +import { + CommentableType, + useUpdateOneCompanyMutation, +} from '~/generated/graphql'; import { getLogoUrlFromDomainName } from '~/utils'; import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField'; import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer'; +import { companyShowFieldsDefinition } from './constants/companyShowFieldsDefinition'; + export function CompanyShow() { const companyId = useParams().companyId ?? ''; const { insertCompanyFavorite, deleteCompanyFavorite } = useFavorites(); @@ -59,11 +63,22 @@ export function CompanyShow() { )} /> - - - - - + + + {companyShowFieldsDefinition.map((fieldDefinition) => { + return ( + + + + ); + })} + + diff --git a/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx b/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx index e2f9b6e04..55737c39c 100644 --- a/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx +++ b/front/src/pages/companies/__stories__/Companies.filterBy.stories.tsx @@ -3,6 +3,7 @@ import type { Meta } from '@storybook/react'; import { userEvent, within } from '@storybook/testing-library'; import assert from 'assert'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -18,7 +19,7 @@ const meta: Meta = { title: 'Pages/Companies/FilterBy', component: Companies, decorators: [PageDecorator], - args: { currentPath: '/companies' }, + args: { routePath: AppPath.CompaniesPage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx b/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx index 1107a9f04..3b0dbba24 100644 --- a/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx +++ b/front/src/pages/companies/__stories__/Companies.sortBy.stories.tsx @@ -2,6 +2,7 @@ import { expect } from '@storybook/jest'; import type { Meta } from '@storybook/react'; import { userEvent, within } from '@storybook/testing-library'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -16,7 +17,7 @@ const meta: Meta = { title: 'Pages/Companies/SortBy', component: Companies, decorators: [PageDecorator], - args: { currentPath: '/companies' }, + args: { routePath: AppPath.CompaniesPage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/companies/__stories__/Companies.stories.tsx b/front/src/pages/companies/__stories__/Companies.stories.tsx index dd33f97dc..6c3029249 100644 --- a/front/src/pages/companies/__stories__/Companies.stories.tsx +++ b/front/src/pages/companies/__stories__/Companies.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -12,7 +13,7 @@ const meta: Meta = { title: 'Pages/Companies', component: Companies, decorators: [PageDecorator], - args: { currentPath: '/companies' }, + args: { routePath: AppPath.CompaniesPage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/companies/__stories__/Company.stories.tsx b/front/src/pages/companies/__stories__/Company.stories.tsx index f1dc79c43..0c6f0267e 100644 --- a/front/src/pages/companies/__stories__/Company.stories.tsx +++ b/front/src/pages/companies/__stories__/Company.stories.tsx @@ -7,6 +7,7 @@ import { graphql } from 'msw'; import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '@/activities/queries'; import { CREATE_ACTIVITY_WITH_COMMENT } from '@/activities/queries/create'; import { GET_COMPANY, UPDATE_ONE_COMPANY } from '@/companies/queries'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -21,7 +22,10 @@ const meta: Meta = { title: 'Pages/Companies/Company', component: CompanyShow, decorators: [PageDecorator], - args: { currentPath: '/companies/89bb825c-171e-4bcc-9cf7-43448d6fb278' }, + args: { + routePath: AppPath.CompanyShowPage, + routeParams: { ':companyId': mockedCompaniesData[0].id }, + }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: [ diff --git a/front/src/pages/companies/constants/companyShowFieldsDefinition.tsx b/front/src/pages/companies/constants/companyShowFieldsDefinition.tsx new file mode 100644 index 000000000..a834b4237 --- /dev/null +++ b/front/src/pages/companies/constants/companyShowFieldsDefinition.tsx @@ -0,0 +1,69 @@ +import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition'; +import { + FieldDateMetadata, + FieldMetadata, + FieldNumberMetadata, + FieldRelationMetadata, + FieldTextMetadata, + FieldURLMetadata, +} from '@/ui/editable-field/types/FieldMetadata'; +import { + IconCalendar, + IconLink, + IconMap, + IconUserCircle, + IconUsers, +} from '@/ui/icon'; +import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect'; + +export const companyShowFieldsDefinition: FieldDefinition[] = [ + { + id: 'domainName', + label: 'Domain name', + icon: , + type: 'url', + metadata: { + fieldName: 'domainName', + placeHolder: 'URL', + }, + } satisfies FieldDefinition, + { + id: 'accountOwner', + label: 'Account owner', + icon: , + type: 'relation', + metadata: { + fieldName: 'accountOwner', + relationType: Entity.User, + }, + } satisfies FieldDefinition, + { + id: 'employees', + label: 'Employees', + icon: , + type: 'number', + metadata: { + fieldName: 'employees', + placeHolder: 'Employees', + }, + } satisfies FieldDefinition, + { + id: 'address', + label: 'Address', + icon: , + type: 'text', + metadata: { + fieldName: 'address', + placeHolder: 'Address', + }, + } satisfies FieldDefinition, + { + id: 'createdAt', + label: 'Created at', + icon: , + type: 'date', + metadata: { + fieldName: 'createdAt', + }, + } satisfies FieldDefinition, +]; diff --git a/front/src/pages/opportunities/__stories__/Opportunities.stories.tsx b/front/src/pages/opportunities/__stories__/Opportunities.stories.tsx index b3abbf2a5..6fef38518 100644 --- a/front/src/pages/opportunities/__stories__/Opportunities.stories.tsx +++ b/front/src/pages/opportunities/__stories__/Opportunities.stories.tsx @@ -1,6 +1,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { within } from '@storybook/testing-library'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -13,7 +14,7 @@ const meta: Meta = { title: 'Pages/Opportunities/Default', component: Opportunities, decorators: [PageDecorator], - args: { currentPath: '/opportunities' }, + args: { routePath: AppPath.OpportunitiesPage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/people/PersonShow.tsx b/front/src/pages/people/PersonShow.tsx index 710a70c9a..c78029cb2 100644 --- a/front/src/pages/people/PersonShow.tsx +++ b/front/src/pages/people/PersonShow.tsx @@ -21,6 +21,7 @@ import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageCo export function PersonShow() { const personId = useParams().personId ?? ''; + const { insertPersonFavorite, deletePersonFavorite } = useFavorites(); const { data } = usePersonQuery(personId); diff --git a/front/src/pages/people/__stories__/People.filterBy.stories.tsx b/front/src/pages/people/__stories__/People.filterBy.stories.tsx index 3f28b210d..32be98a5d 100644 --- a/front/src/pages/people/__stories__/People.filterBy.stories.tsx +++ b/front/src/pages/people/__stories__/People.filterBy.stories.tsx @@ -3,6 +3,7 @@ import type { Meta } from '@storybook/react'; import { userEvent, within } from '@storybook/testing-library'; import assert from 'assert'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -18,7 +19,7 @@ const meta: Meta = { title: 'Pages/People/FilterBy', component: People, decorators: [PageDecorator], - args: { currentPath: '/people' }, + args: { routePath: AppPath.PeoplePage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/people/__stories__/People.inputs.stories.tsx b/front/src/pages/people/__stories__/People.inputs.stories.tsx index 0804acdf9..d879a54a3 100644 --- a/front/src/pages/people/__stories__/People.inputs.stories.tsx +++ b/front/src/pages/people/__stories__/People.inputs.stories.tsx @@ -6,6 +6,7 @@ import { graphql } from 'msw'; import { UPDATE_ONE_PERSON } from '@/people/queries'; import { SEARCH_COMPANY_QUERY } from '@/search/queries/search'; +import { AppPath } from '@/types/AppPath'; import { Company } from '~/generated/graphql'; import { PageDecorator, @@ -25,7 +26,7 @@ const meta: Meta = { title: 'Pages/People/Input', component: People, decorators: [PageDecorator], - args: { currentPath: '/people' }, + args: { routePath: AppPath.PeoplePage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, @@ -216,14 +217,6 @@ export const EditRelation: Story = { await userEvent.click(airbnbChip); }); - await step( - 'Click on last row company cell to exit relation picker', - async () => { - const otherCell = await canvas.findByText('Janice Dane'); - await userEvent.click(otherCell); - }, - ); - await step('Check if Airbnb is in second row company cell', async () => { await canvas.findByText('Airbnb'); }); diff --git a/front/src/pages/people/__stories__/People.sortBy.stories.tsx b/front/src/pages/people/__stories__/People.sortBy.stories.tsx index 18aebeb8f..d9f7b4310 100644 --- a/front/src/pages/people/__stories__/People.sortBy.stories.tsx +++ b/front/src/pages/people/__stories__/People.sortBy.stories.tsx @@ -2,6 +2,7 @@ import { expect } from '@storybook/jest'; import type { Meta } from '@storybook/react'; import { userEvent, within } from '@storybook/testing-library'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -17,7 +18,7 @@ const meta: Meta = { title: 'Pages/People/SortBy', component: People, decorators: [PageDecorator], - args: { currentPath: '/people' }, + args: { routePath: AppPath.PeoplePage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/people/__stories__/People.stories.tsx b/front/src/pages/people/__stories__/People.stories.tsx index 07b4cf1f4..764cc32d9 100644 --- a/front/src/pages/people/__stories__/People.stories.tsx +++ b/front/src/pages/people/__stories__/People.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -12,7 +13,7 @@ const meta: Meta = { title: 'Pages/People', component: People, decorators: [PageDecorator], - args: { currentPath: '/people' }, + args: { routePath: AppPath.PeoplePage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/people/__stories__/Person.stories.tsx b/front/src/pages/people/__stories__/Person.stories.tsx index 5835ba544..83a674b97 100644 --- a/front/src/pages/people/__stories__/Person.stories.tsx +++ b/front/src/pages/people/__stories__/Person.stories.tsx @@ -1,6 +1,6 @@ -import { Route, Routes } from 'react-router-dom'; import type { Meta, StoryObj } from '@storybook/react'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -13,15 +13,11 @@ import { PersonShow } from '../PersonShow'; const meta: Meta = { title: 'Pages/People/Person', component: PersonShow, - decorators: [ - (Story) => ( - - } /> - - ), - PageDecorator, - ], - args: { currentPath: `/person/${mockedPeopleData[0].id}` }, + decorators: [PageDecorator], + args: { + routePath: AppPath.PersonShowPage, + routeParams: { ':personId': mockedPeopleData[0].id }, + }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/settings/__stories__/SettingsProfile.stories.tsx b/front/src/pages/settings/__stories__/SettingsProfile.stories.tsx index 7906be53b..2d1e3fd3a 100644 --- a/front/src/pages/settings/__stories__/SettingsProfile.stories.tsx +++ b/front/src/pages/settings/__stories__/SettingsProfile.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { title: 'Pages/Settings/SettingsProfile', component: SettingsProfile, decorators: [PageDecorator], - args: { currentPath: '/settings/profile' }, + args: { routePath: '/settings/profile' }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/settings/__stories__/SettingsWorkspaceMembers.stories.tsx b/front/src/pages/settings/__stories__/SettingsWorkspaceMembers.stories.tsx index 3354a3ab7..140d54447 100644 --- a/front/src/pages/settings/__stories__/SettingsWorkspaceMembers.stories.tsx +++ b/front/src/pages/settings/__stories__/SettingsWorkspaceMembers.stories.tsx @@ -13,7 +13,7 @@ const meta: Meta = { title: 'Pages/Settings/SettingsWorkspaceMembers', component: SettingsWorkspaceMembers, decorators: [PageDecorator], - args: { currentPath: '/settings/workspace-members' }, + args: { routePath: '/settings/workspace-members' }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/pages/tasks/__stories__/Tasks.stories.tsx b/front/src/pages/tasks/__stories__/Tasks.stories.tsx index d443ee77f..f27f6f622 100644 --- a/front/src/pages/tasks/__stories__/Tasks.stories.tsx +++ b/front/src/pages/tasks/__stories__/Tasks.stories.tsx @@ -1,5 +1,6 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { AppPath } from '@/types/AppPath'; import { PageDecorator, type PageDecoratorArgs, @@ -12,7 +13,7 @@ const meta: Meta = { title: 'Pages/Tasks/Default', component: Tasks, decorators: [PageDecorator], - args: { currentPath: '/tasks' }, + args: { routePath: AppPath.TasksPage }, parameters: { docs: { story: 'inline', iframeHeight: '500px' }, msw: graphqlMocks, diff --git a/front/src/testing/decorators/PageDecorator.tsx b/front/src/testing/decorators/PageDecorator.tsx index 628ef34b5..f055baa2e 100644 --- a/front/src/testing/decorators/PageDecorator.tsx +++ b/front/src/testing/decorators/PageDecorator.tsx @@ -1,4 +1,4 @@ -import { MemoryRouter } from 'react-router-dom'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { Decorator } from '@storybook/react'; import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider'; @@ -7,18 +7,32 @@ import { UserProvider } from '~/modules/users/components/UserProvider'; import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout'; -export type PageDecoratorArgs = { currentPath: string }; +export type PageDecoratorArgs = { routePath: string; routeParams: RouteParams }; -export const PageDecorator: Decorator<{ currentPath: string }> = ( - Story, - { args }, -) => ( +type RouteParams = { + [param: string]: string; +}; + +function computeLocation(routePath: string, routeParams: RouteParams) { + return routePath.replace(/:(\w+)/g, (paramName) => { + return routeParams[paramName] ?? ''; + }); +} + +export const PageDecorator: Decorator<{ + routePath: string; + routeParams: RouteParams; +}> = (Story, { args }) => ( - + - + + } /> +