Use FieldDefinition for company show page (#1171)
* Use FieldDefinition for company show page * removing console.log * fix conflicts * fix address placeholder + company show page field definition ordering * fix story * add replacePlaceholder * use AppPath enum in stories * add routeParams * fix people input story
This commit is contained in:
@ -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<Company, 'id' | 'accountOwnerId'> & {
|
|
||||||
accountOwner?: Pick<User, 'id' | 'displayName' | 'avatarUrl'> | null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
|
|
||||||
return (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<RecoilScope>
|
|
||||||
<EditableField
|
|
||||||
customEditHotkeyScope={{
|
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
|
||||||
}}
|
|
||||||
iconLabel={<IconUserCircle />}
|
|
||||||
editModeContent={
|
|
||||||
<CompanyAccountOwnerPickerFieldEditMode company={company} />
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
company.accountOwner?.displayName ? (
|
|
||||||
<UserChip
|
|
||||||
id={company.accountOwner.id}
|
|
||||||
name={company.accountOwner?.displayName ?? ''}
|
|
||||||
pictureUrl={company.accountOwner?.avatarUrl ?? ''}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<></>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!company.accountOwner}
|
|
||||||
isDisplayModeFixHeight={true}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<Company, 'id'> & {
|
|
||||||
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
|
|
||||||
};
|
|
||||||
onSubmit?: () => void;
|
|
||||||
onCancel?: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CompanyAccountOwnerPickerFieldEditMode({
|
|
||||||
company,
|
|
||||||
onSubmit,
|
|
||||||
onCancel,
|
|
||||||
}: OwnProps) {
|
|
||||||
const { closeEditableField } = useEditableField();
|
|
||||||
|
|
||||||
function handleSubmit() {
|
|
||||||
closeEditableField();
|
|
||||||
onSubmit?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCancel() {
|
|
||||||
closeEditableField();
|
|
||||||
onCancel?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<StyledContainer>
|
|
||||||
<CompanyAccountOwnerPicker
|
|
||||||
company={company}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
/>
|
|
||||||
</StyledContainer>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<Company, 'id' | 'address'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconMap />}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'Address'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={internalValue ?? ''}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<Company, 'id' | 'createdAt'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconCalendar />}
|
|
||||||
editModeContent={
|
|
||||||
<EditableFieldEditModeDate
|
|
||||||
value={internalValue}
|
|
||||||
onChange={handleChange}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={
|
|
||||||
internalValue !== ''
|
|
||||||
? formatToHumanReadableDate(parseDate(internalValue).toJSDate())
|
|
||||||
: 'No date'
|
|
||||||
}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<Company, 'id' | 'domainName'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
iconLabel={<IconLink />}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'URL'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={<FieldDisplayURL URL={internalValue} />}
|
|
||||||
useEditButton
|
|
||||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -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<Company, 'id' | 'employees'>;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<RecoilScope SpecificContext={FieldContext}>
|
|
||||||
<EditableField
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
onCancel={handleCancel}
|
|
||||||
iconLabel={<IconUsers />}
|
|
||||||
editModeContent={
|
|
||||||
<TextInputEdit
|
|
||||||
placeholder={'Employees'}
|
|
||||||
autoFocus
|
|
||||||
value={internalValue ?? ''}
|
|
||||||
onChange={(newValue: string) => {
|
|
||||||
handleChange(newValue);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
displayModeContent={internalValue}
|
|
||||||
isDisplayModeContentEmpty={!(internalValue && internalValue !== '0')}
|
|
||||||
/>
|
|
||||||
</RecoilScope>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { genericEntitiesFamilyState } from '@/ui/editable-field/states/genericEntitiesFamilyState';
|
||||||
import { useGetCompanyQuery } from '~/generated/graphql';
|
import { useGetCompanyQuery } from '~/generated/graphql';
|
||||||
|
|
||||||
export const GET_COMPANY = gql`
|
export const GET_COMPANY = gql`
|
||||||
@ -33,5 +35,13 @@ export const GET_COMPANY = gql`
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export function useCompanyQuery(id: string) {
|
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);
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,7 @@ export const pipelineViewFields: ViewFieldDefinition<ViewFieldMetadata>[] = [
|
|||||||
type: 'relation',
|
type: 'relation',
|
||||||
fieldName: 'pointOfContact',
|
fieldName: 'pointOfContact',
|
||||||
relationType: Entity.Person,
|
relationType: Entity.Person,
|
||||||
|
useEditButton: true,
|
||||||
},
|
},
|
||||||
isVisible: true,
|
isVisible: true,
|
||||||
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
} satisfies ViewFieldDefinition<ViewFieldRelationMetadata>,
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
import { FieldDefinition } from '@/ui/editable-field/types/FieldDefinition';
|
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<
|
export const FieldDefinitionContext = createContext<
|
||||||
FieldDefinition<FieldMetadata>
|
FieldDefinition<FieldMetadata>
|
||||||
@ -9,6 +12,6 @@ export const FieldDefinitionContext = createContext<
|
|||||||
id: '',
|
id: '',
|
||||||
label: '',
|
label: '',
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
type: '',
|
type: 'unknown' satisfies FieldType,
|
||||||
metadata: {} as FieldMetadata,
|
metadata: {} as FieldMetadata,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,10 +5,14 @@ import { isFieldDate } from '../types/guards/isFieldDate';
|
|||||||
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
import { isFieldNumber } from '../types/guards/isFieldNumber';
|
||||||
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
import { isFieldProbability } from '../types/guards/isFieldProbability';
|
||||||
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
import { isFieldRelation } from '../types/guards/isFieldRelation';
|
||||||
|
import { isFieldText } from '../types/guards/isFieldText';
|
||||||
|
import { isFieldURL } from '../types/guards/isFieldURL';
|
||||||
|
|
||||||
import { GenericEditableDateField } from './GenericEditableDateField';
|
import { GenericEditableDateField } from './GenericEditableDateField';
|
||||||
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
import { GenericEditableNumberField } from './GenericEditableNumberField';
|
||||||
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
import { GenericEditableRelationField } from './GenericEditableRelationField';
|
||||||
|
import { GenericEditableTextField } from './GenericEditableTextField';
|
||||||
|
import { GenericEditableURLField } from './GenericEditableURLField';
|
||||||
import { ProbabilityEditableField } from './ProbabilityEditableField';
|
import { ProbabilityEditableField } from './ProbabilityEditableField';
|
||||||
|
|
||||||
export function GenericEditableField() {
|
export function GenericEditableField() {
|
||||||
@ -22,9 +26,13 @@ export function GenericEditableField() {
|
|||||||
return <GenericEditableNumberField />;
|
return <GenericEditableNumberField />;
|
||||||
} else if (isFieldProbability(fieldDefinition)) {
|
} else if (isFieldProbability(fieldDefinition)) {
|
||||||
return <ProbabilityEditableField />;
|
return <ProbabilityEditableField />;
|
||||||
|
} else if (isFieldURL(fieldDefinition)) {
|
||||||
|
return <GenericEditableURLField />;
|
||||||
|
} else if (isFieldText(fieldDefinition)) {
|
||||||
|
return <GenericEditableTextField />;
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown field metadata type: ${fieldDefinition.metadata.type} in GenericEditableCell`,
|
`Unknown field metadata type: ${fieldDefinition.type} in GenericEditableField`,
|
||||||
);
|
);
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,6 +69,7 @@ export function GenericEditableNumberFieldEditMode() {
|
|||||||
<div ref={wrapperRef}>
|
<div ref={wrapperRef}>
|
||||||
<TextInputEdit
|
<TextInputEdit
|
||||||
autoFocus
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
value={internalValue ? internalValue.toString() : ''}
|
value={internalValue ? internalValue.toString() : ''}
|
||||||
onChange={(newValue: string) => {
|
onChange={(newValue: string) => {
|
||||||
handleChange(newValue);
|
handleChange(newValue);
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export function GenericEditableRelationField() {
|
|||||||
<RecoilScope SpecificContext={FieldContext}>
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
<RecoilScope>
|
<RecoilScope>
|
||||||
<EditableField
|
<EditableField
|
||||||
useEditButton
|
useEditButton={currentEditableFieldDefinition.metadata.useEditButton}
|
||||||
customEditHotkeyScope={{
|
customEditHotkeyScope={{
|
||||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
|
|||||||
|
|
||||||
import { PersonChip } from '@/people/components/PersonChip';
|
import { PersonChip } from '@/people/components/PersonChip';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { UserChip } from '@/users/components/UserChip';
|
||||||
|
|
||||||
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitionContext';
|
||||||
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
@ -35,6 +36,15 @@ export function GenericEditableRelationFieldDisplayMode() {
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserChip
|
||||||
|
id={fieldValue?.id ?? ''}
|
||||||
|
name={fieldValue?.displayName ?? ''}
|
||||||
|
pictureUrl={fieldValue?.avatarUrl ?? ''}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
`Unknown relation type: "${currentEditableFieldDefinition.metadata.relationType}"
|
||||||
|
|||||||
@ -3,9 +3,9 @@ import styled from '@emotion/styled';
|
|||||||
import { useRecoilState } from 'recoil';
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
import { PeoplePicker } from '@/people/components/PeoplePicker';
|
||||||
import { ViewFieldRelationValue } from '@/ui/editable-field/types/ViewField';
|
|
||||||
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
|
||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
import { UserPicker } from '@/users/components/UserPicker';
|
||||||
|
|
||||||
import { useEditableField } from '../hooks/useEditableField';
|
import { useEditableField } from '../hooks/useEditableField';
|
||||||
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
import { useUpdateGenericEntityField } from '../hooks/useUpdateGenericEntityField';
|
||||||
@ -13,7 +13,10 @@ import { EditableFieldDefinitionContext } from '../states/EditableFieldDefinitio
|
|||||||
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
import { EditableFieldEntityIdContext } from '../states/EditableFieldEntityIdContext';
|
||||||
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
import { genericEntityFieldFamilySelector } from '../states/genericEntityFieldFamilySelector';
|
||||||
import { FieldDefinition } from '../types/FieldDefinition';
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
import { FieldRelationMetadata } from '../types/FieldMetadata';
|
import {
|
||||||
|
FieldRelationMetadata,
|
||||||
|
FieldRelationValue,
|
||||||
|
} from '../types/FieldMetadata';
|
||||||
|
|
||||||
const RelationPickerContainer = styled.div`
|
const RelationPickerContainer = styled.div`
|
||||||
left: 0px;
|
left: 0px;
|
||||||
@ -28,7 +31,7 @@ function RelationPicker({
|
|||||||
handleCancel,
|
handleCancel,
|
||||||
}: {
|
}: {
|
||||||
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
fieldDefinition: FieldDefinition<FieldRelationMetadata>;
|
||||||
fieldValue: ViewFieldRelationValue;
|
fieldValue: FieldRelationValue;
|
||||||
handleEntitySubmit: (newRelationId: EntityForSelect | null) => void;
|
handleEntitySubmit: (newRelationId: EntityForSelect | null) => void;
|
||||||
handleCancel: () => void;
|
handleCancel: () => void;
|
||||||
}) {
|
}) {
|
||||||
@ -36,7 +39,16 @@ function RelationPicker({
|
|||||||
case Entity.Person: {
|
case Entity.Person: {
|
||||||
return (
|
return (
|
||||||
<PeoplePicker
|
<PeoplePicker
|
||||||
personId={fieldValue?.id ?? null}
|
personId={fieldValue ? fieldValue.id : ''}
|
||||||
|
onSubmit={handleEntitySubmit}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case Entity.User: {
|
||||||
|
return (
|
||||||
|
<UserPicker
|
||||||
|
userId={fieldValue ? fieldValue.id : ''}
|
||||||
onSubmit={handleEntitySubmit}
|
onSubmit={handleEntitySubmit}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
/>
|
/>
|
||||||
@ -46,7 +58,7 @@ function RelationPicker({
|
|||||||
console.warn(
|
console.warn(
|
||||||
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
`Unknown relation type: "${fieldDefinition.metadata.relationType}" in GenericEditableRelationField`,
|
||||||
);
|
);
|
||||||
return <> </>;
|
return <></>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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<FieldNumberMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditableTextFieldEditMode />}
|
||||||
|
displayModeContent={fieldValue}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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<FieldTextMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
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 (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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<FieldNumberMetadata>;
|
||||||
|
|
||||||
|
const fieldValue = useRecoilValue<string>(
|
||||||
|
genericEntityFieldFamilySelector({
|
||||||
|
entityId: currentEditableFieldEntityId ?? '',
|
||||||
|
fieldName: currentEditableFieldDefinition
|
||||||
|
? currentEditableFieldDefinition.metadata.fieldName
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<RecoilScope SpecificContext={FieldContext}>
|
||||||
|
<EditableField
|
||||||
|
useEditButton
|
||||||
|
iconLabel={currentEditableFieldDefinition.icon}
|
||||||
|
editModeContent={<GenericEditableURLFieldEditMode />}
|
||||||
|
displayModeContent={<FieldDisplayURL URL={fieldValue} />}
|
||||||
|
isDisplayModeContentEmpty={!fieldValue}
|
||||||
|
/>
|
||||||
|
</RecoilScope>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -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<FieldURLMetadata>;
|
||||||
|
|
||||||
|
// TODO: we could use a hook that would return the field value with the right type
|
||||||
|
const [fieldValue, setFieldValue] = useRecoilState<string>(
|
||||||
|
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 (
|
||||||
|
<div ref={wrapperRef}>
|
||||||
|
<TextInputEdit
|
||||||
|
autoFocus
|
||||||
|
placeholder={currentEditableFieldDefinition.metadata.placeHolder}
|
||||||
|
value={internalValue}
|
||||||
|
onChange={(newValue: string) => {
|
||||||
|
handleChange(newValue);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,7 +1,5 @@
|
|||||||
import { useContext } from 'react';
|
import { useContext } from 'react';
|
||||||
|
|
||||||
import { isFieldChip } from '@/ui/editable-field/types/guards/isFieldChip';
|
|
||||||
|
|
||||||
import { EditableFieldMutationContext } from '../states/EditableFieldMutationContext';
|
import { EditableFieldMutationContext } from '../states/EditableFieldMutationContext';
|
||||||
import { FieldDefinition } from '../types/FieldDefinition';
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
import {
|
import {
|
||||||
@ -27,6 +25,7 @@ import {
|
|||||||
FieldURLMetadata,
|
FieldURLMetadata,
|
||||||
FieldURLValue,
|
FieldURLValue,
|
||||||
} from '../types/FieldMetadata';
|
} from '../types/FieldMetadata';
|
||||||
|
import { isFieldChip } from '../types/guards/isFieldChip';
|
||||||
import { isFieldChipValue } from '../types/guards/isFieldChipValue';
|
import { isFieldChipValue } from '../types/guards/isFieldChipValue';
|
||||||
import { isFieldDate } from '../types/guards/isFieldDate';
|
import { isFieldDate } from '../types/guards/isFieldDate';
|
||||||
import { isFieldDateValue } from '../types/guards/isFieldDateValue';
|
import { isFieldDateValue } from '../types/guards/isFieldDateValue';
|
||||||
@ -53,31 +52,30 @@ export function useUpdateGenericEntityField() {
|
|||||||
const [updateEntity] = useUpdateEntityMutation();
|
const [updateEntity] = useUpdateEntityMutation();
|
||||||
|
|
||||||
return function updateEntityField<
|
return function updateEntityField<
|
||||||
MetadataType extends FieldMetadata,
|
ValueType extends FieldMetadata extends FieldDoubleTextMetadata
|
||||||
ValueType extends MetadataType extends FieldDoubleTextMetadata
|
|
||||||
? FieldDoubleTextValue
|
? FieldDoubleTextValue
|
||||||
: MetadataType extends FieldTextMetadata
|
: FieldMetadata extends FieldTextMetadata
|
||||||
? FieldTextValue
|
? FieldTextValue
|
||||||
: MetadataType extends FieldPhoneMetadata
|
: FieldMetadata extends FieldPhoneMetadata
|
||||||
? FieldPhoneValue
|
? FieldPhoneValue
|
||||||
: MetadataType extends FieldURLMetadata
|
: FieldMetadata extends FieldURLMetadata
|
||||||
? FieldURLValue
|
? FieldURLValue
|
||||||
: MetadataType extends FieldNumberMetadata
|
: FieldMetadata extends FieldNumberMetadata
|
||||||
? FieldNumberValue
|
? FieldNumberValue
|
||||||
: MetadataType extends FieldDateMetadata
|
: FieldMetadata extends FieldDateMetadata
|
||||||
? FieldDateValue
|
? FieldDateValue
|
||||||
: MetadataType extends FieldChipMetadata
|
: FieldMetadata extends FieldChipMetadata
|
||||||
? FieldChipValue
|
? FieldChipValue
|
||||||
: MetadataType extends FieldDoubleTextChipMetadata
|
: FieldMetadata extends FieldDoubleTextChipMetadata
|
||||||
? FieldDoubleTextChipValue
|
? FieldDoubleTextChipValue
|
||||||
: MetadataType extends FieldRelationMetadata
|
: FieldMetadata extends FieldRelationMetadata
|
||||||
? FieldRelationValue
|
? FieldRelationValue
|
||||||
: MetadataType extends FieldProbabilityMetadata
|
: FieldMetadata extends FieldProbabilityMetadata
|
||||||
? FieldProbabilityValue
|
? FieldProbabilityValue
|
||||||
: unknown,
|
: unknown,
|
||||||
>(
|
>(
|
||||||
currentEntityId: string,
|
currentEntityId: string,
|
||||||
field: FieldDefinition<MetadataType>,
|
field: FieldDefinition<FieldMetadata>,
|
||||||
newFieldValue: ValueType,
|
newFieldValue: ValueType,
|
||||||
) {
|
) {
|
||||||
const newFieldValueUnknown = newFieldValue as unknown;
|
const newFieldValueUnknown = newFieldValue as unknown;
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { createContext } from 'react';
|
import { createContext } from 'react';
|
||||||
|
|
||||||
import { FieldDefinition } from '../types/FieldDefinition';
|
import { FieldDefinition } from '../types/FieldDefinition';
|
||||||
import { ViewFieldMetadata } from '../types/ViewField';
|
import { FieldMetadata } from '../types/FieldMetadata';
|
||||||
|
|
||||||
export const EditableFieldDefinitionContext = createContext<
|
export const EditableFieldDefinitionContext = createContext<
|
||||||
FieldDefinition<ViewFieldMetadata>
|
FieldDefinition<FieldMetadata>
|
||||||
>({} as FieldDefinition<ViewFieldMetadata>);
|
>({} as FieldDefinition<FieldMetadata>);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { FieldMetadata } from './FieldMetadata';
|
import { FieldMetadata, FieldType } from './FieldMetadata';
|
||||||
|
|
||||||
export type FieldDefinition<T extends FieldMetadata | unknown> = {
|
export type FieldDefinition<T extends FieldMetadata | unknown> = {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
icon?: JSX.Element;
|
icon?: JSX.Element;
|
||||||
type: string;
|
type: FieldType;
|
||||||
metadata: T;
|
metadata: T;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelec
|
|||||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||||
|
|
||||||
export type FieldType =
|
export type FieldType =
|
||||||
|
| 'unknown'
|
||||||
| 'text'
|
| 'text'
|
||||||
| 'relation'
|
| 'relation'
|
||||||
| 'chip'
|
| 'chip'
|
||||||
@ -14,41 +15,36 @@ export type FieldType =
|
|||||||
| 'probability';
|
| 'probability';
|
||||||
|
|
||||||
export type FieldTextMetadata = {
|
export type FieldTextMetadata = {
|
||||||
type: 'text';
|
|
||||||
placeHolder: string;
|
placeHolder: string;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldPhoneMetadata = {
|
export type FieldPhoneMetadata = {
|
||||||
type: 'phone';
|
|
||||||
placeHolder: string;
|
placeHolder: string;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldURLMetadata = {
|
export type FieldURLMetadata = {
|
||||||
type: 'url';
|
|
||||||
placeHolder: string;
|
placeHolder: string;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldDateMetadata = {
|
export type FieldDateMetadata = {
|
||||||
type: 'date';
|
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldNumberMetadata = {
|
export type FieldNumberMetadata = {
|
||||||
type: 'number';
|
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
|
placeHolder: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldRelationMetadata = {
|
export type FieldRelationMetadata = {
|
||||||
type: 'relation';
|
|
||||||
relationType: Entity;
|
relationType: Entity;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
|
useEditButton?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldChipMetadata = {
|
export type FieldChipMetadata = {
|
||||||
type: 'chip';
|
|
||||||
relationType: Entity;
|
relationType: Entity;
|
||||||
contentFieldName: string;
|
contentFieldName: string;
|
||||||
urlFieldName: string;
|
urlFieldName: string;
|
||||||
@ -56,7 +52,6 @@ export type FieldChipMetadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FieldDoubleTextMetadata = {
|
export type FieldDoubleTextMetadata = {
|
||||||
type: 'double-text';
|
|
||||||
firstValueFieldName: string;
|
firstValueFieldName: string;
|
||||||
firstValuePlaceholder: string;
|
firstValuePlaceholder: string;
|
||||||
secondValueFieldName: string;
|
secondValueFieldName: string;
|
||||||
@ -64,7 +59,6 @@ export type FieldDoubleTextMetadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FieldDoubleTextChipMetadata = {
|
export type FieldDoubleTextChipMetadata = {
|
||||||
type: 'double-text-chip';
|
|
||||||
firstValueFieldName: string;
|
firstValueFieldName: string;
|
||||||
firstValuePlaceholder: string;
|
firstValuePlaceholder: string;
|
||||||
secondValueFieldName: string;
|
secondValueFieldName: string;
|
||||||
@ -74,11 +68,10 @@ export type FieldDoubleTextChipMetadata = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FieldProbabilityMetadata = {
|
export type FieldProbabilityMetadata = {
|
||||||
type: 'probability';
|
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FieldMetadata = { type: FieldType } & (
|
export type FieldMetadata =
|
||||||
| FieldTextMetadata
|
| FieldTextMetadata
|
||||||
| FieldRelationMetadata
|
| FieldRelationMetadata
|
||||||
| FieldChipMetadata
|
| FieldChipMetadata
|
||||||
@ -88,8 +81,7 @@ export type FieldMetadata = { type: FieldType } & (
|
|||||||
| FieldURLMetadata
|
| FieldURLMetadata
|
||||||
| FieldNumberMetadata
|
| FieldNumberMetadata
|
||||||
| FieldDateMetadata
|
| FieldDateMetadata
|
||||||
| FieldProbabilityMetadata
|
| FieldProbabilityMetadata;
|
||||||
);
|
|
||||||
|
|
||||||
export type FieldTextValue = string;
|
export type FieldTextValue = string;
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,7 @@ export type ViewFieldRelationMetadata = {
|
|||||||
type: 'relation';
|
type: 'relation';
|
||||||
relationType: Entity;
|
relationType: Entity;
|
||||||
fieldName: string;
|
fieldName: string;
|
||||||
|
useEditButton?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ViewFieldChipMetadata = {
|
export type ViewFieldChipMetadata = {
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
import { within } from '@storybook/testing-library';
|
import { within } from '@storybook/testing-library';
|
||||||
import { graphql } from 'msw';
|
import { graphql } from 'msw';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -16,7 +17,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Auth/CreateProfile',
|
title: 'Pages/Auth/CreateProfile',
|
||||||
component: CreateProfile,
|
component: CreateProfile,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/create/profile' },
|
args: { routePath: AppPath.CreateProfile },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: [
|
msw: [
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
|
|||||||
import { within } from '@storybook/testing-library';
|
import { within } from '@storybook/testing-library';
|
||||||
import { graphql } from 'msw';
|
import { graphql } from 'msw';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -16,7 +17,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Auth/CreateWorkspace',
|
title: 'Pages/Auth/CreateWorkspace',
|
||||||
component: CreateWorkspace,
|
component: CreateWorkspace,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/create/workspace' },
|
args: { routePath: AppPath.CreateWorkspace },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: [
|
msw: [
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { fireEvent, within } from '@storybook/testing-library';
|
import { fireEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -13,7 +14,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Auth/SignInUp',
|
title: 'Pages/Auth/SignInUp',
|
||||||
component: SignInUp,
|
component: SignInUp,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/sign-in' },
|
args: { routePath: AppPath.SignIn },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -3,25 +3,29 @@ import { useTheme } from '@emotion/react';
|
|||||||
|
|
||||||
import { Timeline } from '@/activities/timeline/components/Timeline';
|
import { Timeline } from '@/activities/timeline/components/Timeline';
|
||||||
import { CompanyTeam } from '@/companies/components/CompanyTeam';
|
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 { useCompanyQuery } from '@/companies/queries';
|
||||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
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 { 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 { IconBuildingSkyscraper } from '@/ui/icon';
|
||||||
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
|
||||||
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
|
import { ShowPageLeftContainer } from '@/ui/layout/show-page/components/ShowPageLeftContainer';
|
||||||
import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer';
|
import { ShowPageRightContainer } from '@/ui/layout/show-page/components/ShowPageRightContainer';
|
||||||
import { ShowPageSummaryCard } from '@/ui/layout/show-page/components/ShowPageSummaryCard';
|
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 { getLogoUrlFromDomainName } from '~/utils';
|
||||||
|
|
||||||
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
import { CompanyNameEditableField } from '../../modules/companies/editable-field/components/CompanyNameEditableField';
|
||||||
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageContainer';
|
||||||
|
|
||||||
|
import { companyShowFieldsDefinition } from './constants/companyShowFieldsDefinition';
|
||||||
|
|
||||||
export function CompanyShow() {
|
export function CompanyShow() {
|
||||||
const companyId = useParams().companyId ?? '';
|
const companyId = useParams().companyId ?? '';
|
||||||
const { insertCompanyFavorite, deleteCompanyFavorite } = useFavorites();
|
const { insertCompanyFavorite, deleteCompanyFavorite } = useFavorites();
|
||||||
@ -59,11 +63,22 @@ export function CompanyShow() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<PropertyBox extraPadding={true}>
|
<PropertyBox extraPadding={true}>
|
||||||
<CompanyDomainNameEditableField company={company} />
|
<EditableFieldMutationContext.Provider
|
||||||
<CompanyAccountOwnerEditableField company={company} />
|
value={useUpdateOneCompanyMutation}
|
||||||
<CompanyEmployeesEditableField company={company} />
|
>
|
||||||
<CompanyAddressEditableField company={company} />
|
<EditableFieldEntityIdContext.Provider value={company.id}>
|
||||||
<CompanyCreatedAtEditableField company={company} />
|
{companyShowFieldsDefinition.map((fieldDefinition) => {
|
||||||
|
return (
|
||||||
|
<EditableFieldDefinitionContext.Provider
|
||||||
|
value={fieldDefinition}
|
||||||
|
key={fieldDefinition.id}
|
||||||
|
>
|
||||||
|
<GenericEditableField />
|
||||||
|
</EditableFieldDefinitionContext.Provider>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</EditableFieldEntityIdContext.Provider>
|
||||||
|
</EditableFieldMutationContext.Provider>
|
||||||
</PropertyBox>
|
</PropertyBox>
|
||||||
<CompanyTeam company={company}></CompanyTeam>
|
<CompanyTeam company={company}></CompanyTeam>
|
||||||
</ShowPageLeftContainer>
|
</ShowPageLeftContainer>
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -18,7 +19,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Companies/FilterBy',
|
title: 'Pages/Companies/FilterBy',
|
||||||
component: Companies,
|
component: Companies,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/companies' },
|
args: { routePath: AppPath.CompaniesPage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { expect } from '@storybook/jest';
|
|||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -16,7 +17,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Companies/SortBy',
|
title: 'Pages/Companies/SortBy',
|
||||||
component: Companies,
|
component: Companies,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/companies' },
|
args: { routePath: AppPath.CompaniesPage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -12,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Companies',
|
title: 'Pages/Companies',
|
||||||
component: Companies,
|
component: Companies,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/companies' },
|
args: { routePath: AppPath.CompaniesPage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { graphql } from 'msw';
|
|||||||
import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '@/activities/queries';
|
import { GET_ACTIVITIES_BY_TARGETS, GET_ACTIVITY } from '@/activities/queries';
|
||||||
import { CREATE_ACTIVITY_WITH_COMMENT } from '@/activities/queries/create';
|
import { CREATE_ACTIVITY_WITH_COMMENT } from '@/activities/queries/create';
|
||||||
import { GET_COMPANY, UPDATE_ONE_COMPANY } from '@/companies/queries';
|
import { GET_COMPANY, UPDATE_ONE_COMPANY } from '@/companies/queries';
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -21,7 +22,10 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Companies/Company',
|
title: 'Pages/Companies/Company',
|
||||||
component: CompanyShow,
|
component: CompanyShow,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/companies/89bb825c-171e-4bcc-9cf7-43448d6fb278' },
|
args: {
|
||||||
|
routePath: AppPath.CompanyShowPage,
|
||||||
|
routeParams: { ':companyId': mockedCompaniesData[0].id },
|
||||||
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: [
|
msw: [
|
||||||
|
|||||||
@ -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<FieldMetadata>[] = [
|
||||||
|
{
|
||||||
|
id: 'domainName',
|
||||||
|
label: 'Domain name',
|
||||||
|
icon: <IconLink />,
|
||||||
|
type: 'url',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'domainName',
|
||||||
|
placeHolder: 'URL',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldURLMetadata>,
|
||||||
|
{
|
||||||
|
id: 'accountOwner',
|
||||||
|
label: 'Account owner',
|
||||||
|
icon: <IconUserCircle />,
|
||||||
|
type: 'relation',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'accountOwner',
|
||||||
|
relationType: Entity.User,
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldRelationMetadata>,
|
||||||
|
{
|
||||||
|
id: 'employees',
|
||||||
|
label: 'Employees',
|
||||||
|
icon: <IconUsers />,
|
||||||
|
type: 'number',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'employees',
|
||||||
|
placeHolder: 'Employees',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldNumberMetadata>,
|
||||||
|
{
|
||||||
|
id: 'address',
|
||||||
|
label: 'Address',
|
||||||
|
icon: <IconMap />,
|
||||||
|
type: 'text',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'address',
|
||||||
|
placeHolder: 'Address',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldTextMetadata>,
|
||||||
|
{
|
||||||
|
id: 'createdAt',
|
||||||
|
label: 'Created at',
|
||||||
|
icon: <IconCalendar />,
|
||||||
|
type: 'date',
|
||||||
|
metadata: {
|
||||||
|
fieldName: 'createdAt',
|
||||||
|
},
|
||||||
|
} satisfies FieldDefinition<FieldDateMetadata>,
|
||||||
|
];
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
import { within } from '@storybook/testing-library';
|
import { within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -13,7 +14,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Opportunities/Default',
|
title: 'Pages/Opportunities/Default',
|
||||||
component: Opportunities,
|
component: Opportunities,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/opportunities' },
|
args: { routePath: AppPath.OpportunitiesPage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import { ShowPageContainer } from '../../modules/ui/layout/components/ShowPageCo
|
|||||||
|
|
||||||
export function PersonShow() {
|
export function PersonShow() {
|
||||||
const personId = useParams().personId ?? '';
|
const personId = useParams().personId ?? '';
|
||||||
|
|
||||||
const { insertPersonFavorite, deletePersonFavorite } = useFavorites();
|
const { insertPersonFavorite, deletePersonFavorite } = useFavorites();
|
||||||
|
|
||||||
const { data } = usePersonQuery(personId);
|
const { data } = usePersonQuery(personId);
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import type { Meta } from '@storybook/react';
|
|||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -18,7 +19,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/People/FilterBy',
|
title: 'Pages/People/FilterBy',
|
||||||
component: People,
|
component: People,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/people' },
|
args: { routePath: AppPath.PeoplePage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { graphql } from 'msw';
|
|||||||
|
|
||||||
import { UPDATE_ONE_PERSON } from '@/people/queries';
|
import { UPDATE_ONE_PERSON } from '@/people/queries';
|
||||||
import { SEARCH_COMPANY_QUERY } from '@/search/queries/search';
|
import { SEARCH_COMPANY_QUERY } from '@/search/queries/search';
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import { Company } from '~/generated/graphql';
|
import { Company } from '~/generated/graphql';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
@ -25,7 +26,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/People/Input',
|
title: 'Pages/People/Input',
|
||||||
component: People,
|
component: People,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/people' },
|
args: { routePath: AppPath.PeoplePage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
@ -216,14 +217,6 @@ export const EditRelation: Story = {
|
|||||||
await userEvent.click(airbnbChip);
|
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 step('Check if Airbnb is in second row company cell', async () => {
|
||||||
await canvas.findByText('Airbnb');
|
await canvas.findByText('Airbnb');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { expect } from '@storybook/jest';
|
|||||||
import type { Meta } from '@storybook/react';
|
import type { Meta } from '@storybook/react';
|
||||||
import { userEvent, within } from '@storybook/testing-library';
|
import { userEvent, within } from '@storybook/testing-library';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -17,7 +18,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/People/SortBy',
|
title: 'Pages/People/SortBy',
|
||||||
component: People,
|
component: People,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/people' },
|
args: { routePath: AppPath.PeoplePage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -12,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/People',
|
title: 'Pages/People',
|
||||||
component: People,
|
component: People,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/people' },
|
args: { routePath: AppPath.PeoplePage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { Route, Routes } from 'react-router-dom';
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -13,15 +13,11 @@ import { PersonShow } from '../PersonShow';
|
|||||||
const meta: Meta<PageDecoratorArgs> = {
|
const meta: Meta<PageDecoratorArgs> = {
|
||||||
title: 'Pages/People/Person',
|
title: 'Pages/People/Person',
|
||||||
component: PersonShow,
|
component: PersonShow,
|
||||||
decorators: [
|
decorators: [PageDecorator],
|
||||||
(Story) => (
|
args: {
|
||||||
<Routes>
|
routePath: AppPath.PersonShowPage,
|
||||||
<Route path="/person/:personId" element={<Story />} />
|
routeParams: { ':personId': mockedPeopleData[0].id },
|
||||||
</Routes>
|
},
|
||||||
),
|
|
||||||
PageDecorator,
|
|
||||||
],
|
|
||||||
args: { currentPath: `/person/${mockedPeopleData[0].id}` },
|
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Settings/SettingsProfile',
|
title: 'Pages/Settings/SettingsProfile',
|
||||||
component: SettingsProfile,
|
component: SettingsProfile,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/settings/profile' },
|
args: { routePath: '/settings/profile' },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Settings/SettingsWorkspaceMembers',
|
title: 'Pages/Settings/SettingsWorkspaceMembers',
|
||||||
component: SettingsWorkspaceMembers,
|
component: SettingsWorkspaceMembers,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/settings/workspace-members' },
|
args: { routePath: '/settings/workspace-members' },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { Meta, StoryObj } from '@storybook/react';
|
import type { Meta, StoryObj } from '@storybook/react';
|
||||||
|
|
||||||
|
import { AppPath } from '@/types/AppPath';
|
||||||
import {
|
import {
|
||||||
PageDecorator,
|
PageDecorator,
|
||||||
type PageDecoratorArgs,
|
type PageDecoratorArgs,
|
||||||
@ -12,7 +13,7 @@ const meta: Meta<PageDecoratorArgs> = {
|
|||||||
title: 'Pages/Tasks/Default',
|
title: 'Pages/Tasks/Default',
|
||||||
component: Tasks,
|
component: Tasks,
|
||||||
decorators: [PageDecorator],
|
decorators: [PageDecorator],
|
||||||
args: { currentPath: '/tasks' },
|
args: { routePath: AppPath.TasksPage },
|
||||||
parameters: {
|
parameters: {
|
||||||
docs: { story: 'inline', iframeHeight: '500px' },
|
docs: { story: 'inline', iframeHeight: '500px' },
|
||||||
msw: graphqlMocks,
|
msw: graphqlMocks,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { MemoryRouter } from 'react-router-dom';
|
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||||
import { Decorator } from '@storybook/react';
|
import { Decorator } from '@storybook/react';
|
||||||
|
|
||||||
import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider';
|
import { ClientConfigProvider } from '~/modules/client-config/components/ClientConfigProvider';
|
||||||
@ -7,18 +7,32 @@ import { UserProvider } from '~/modules/users/components/UserProvider';
|
|||||||
|
|
||||||
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
|
import { FullHeightStorybookLayout } from '../FullHeightStorybookLayout';
|
||||||
|
|
||||||
export type PageDecoratorArgs = { currentPath: string };
|
export type PageDecoratorArgs = { routePath: string; routeParams: RouteParams };
|
||||||
|
|
||||||
export const PageDecorator: Decorator<{ currentPath: string }> = (
|
type RouteParams = {
|
||||||
Story,
|
[param: string]: string;
|
||||||
{ args },
|
};
|
||||||
) => (
|
|
||||||
|
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 }) => (
|
||||||
<UserProvider>
|
<UserProvider>
|
||||||
<ClientConfigProvider>
|
<ClientConfigProvider>
|
||||||
<MemoryRouter initialEntries={[args.currentPath]}>
|
<MemoryRouter
|
||||||
|
initialEntries={[computeLocation(args.routePath, args.routeParams)]}
|
||||||
|
>
|
||||||
<FullHeightStorybookLayout>
|
<FullHeightStorybookLayout>
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
<Story />
|
<Routes>
|
||||||
|
<Route path={args.routePath} element={<Story />} />
|
||||||
|
</Routes>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
</FullHeightStorybookLayout>
|
</FullHeightStorybookLayout>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
|
|||||||
Reference in New Issue
Block a user