mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-29 15:25:45 +03:00
Feat/editable fields update (#743)
* Removed console log * Used current scope as default parent scope for fields * Finished editable fields on people show page * Added stories * Console log * Lint
This commit is contained in:
parent
5ee8eaa985
commit
7ecb098c55
@ -2160,7 +2160,7 @@ export type GetPersonQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, email: string, createdAt: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string } | null } };
|
||||
export type GetPersonQuery = { __typename?: 'Query', findUniquePerson: { __typename?: 'Person', id: string, firstName: string, lastName: string, displayName: string, email: string, createdAt: string, city: string, phone: string, _commentThreadCount: number, company?: { __typename?: 'Company', id: string, name: string, domainName: string } | null } };
|
||||
|
||||
export type UpdatePeopleMutationVariables = Exact<{
|
||||
id?: InputMaybe<Scalars['String']>;
|
||||
@ -3629,9 +3629,13 @@ export const GetPersonDocument = gql`
|
||||
displayName
|
||||
email
|
||||
createdAt
|
||||
city
|
||||
phone
|
||||
_commentThreadCount
|
||||
company {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ import styled from '@emotion/styled';
|
||||
|
||||
import { CompanyAccountOwnerPicker } from '@/companies/components/CompanyAccountOwnerPicker';
|
||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
||||
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||
import { Company, User } from '~/generated/graphql';
|
||||
|
||||
const CompanyAccountOwnerPickerContainer = styled.div`
|
||||
@ -17,7 +16,6 @@ export type OwnProps = {
|
||||
};
|
||||
onSubmit?: () => void;
|
||||
onCancel?: () => void;
|
||||
parentHotkeyScope?: HotkeyScope;
|
||||
};
|
||||
|
||||
export function CompanyAccountOwnerPickerFieldEditMode({
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { EditableFieldEditModeDate } from '@/ui/editable-field/components/EditableFieldEditModeDate';
|
||||
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/recoil-scope/components/RecoilScope';
|
||||
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
|
||||
@ -56,6 +56,7 @@ export function CompanyCreatedAtEditableField({ company }: OwnProps) {
|
||||
? formatToHumanReadableDate(parseDate(internalValue).toJSDate())
|
||||
: 'No date'
|
||||
}
|
||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
|
@ -56,6 +56,7 @@ export function CompanyDomainNameEditableField({ company }: OwnProps) {
|
||||
}
|
||||
displayModeContent={<FieldDisplayURL URL={internalValue} />}
|
||||
useEditButton
|
||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
|
@ -70,6 +70,7 @@ export function CompanyEmployeesEditableField({ company }: OwnProps) {
|
||||
/>
|
||||
}
|
||||
displayModeContent={internalValue}
|
||||
isDisplayModeContentEmpty={!(internalValue && internalValue !== '0')}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
|
84
front/src/modules/people/components/PersonPropertyBox.tsx
Normal file
84
front/src/modules/people/components/PersonPropertyBox.tsx
Normal file
@ -0,0 +1,84 @@
|
||||
import {
|
||||
IconCalendar,
|
||||
IconMail,
|
||||
IconMap,
|
||||
IconPhone,
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
||||
import { DateEditableField } from '@/ui/editable-field/variants/components/DateEditableField';
|
||||
import { PhoneEditableField } from '@/ui/editable-field/variants/components/PhoneEditableField';
|
||||
import { TextEditableField } from '@/ui/editable-field/variants/components/TextEditableField';
|
||||
import { Company, Person, useUpdatePeopleMutation } from '~/generated/graphql';
|
||||
|
||||
import { PeopleCompanyEditableField } from '../editable-field/components/PeopleCompanyEditableField';
|
||||
|
||||
type OwnProps = {
|
||||
person: Pick<
|
||||
Person,
|
||||
'id' | 'city' | 'email' | 'displayName' | 'phone' | 'createdAt'
|
||||
> & {
|
||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
export function PersonPropertyBox({ person }: OwnProps) {
|
||||
const [updatePerson] = useUpdatePeopleMutation();
|
||||
|
||||
return (
|
||||
<PropertyBox extraPadding={true}>
|
||||
<TextEditableField
|
||||
value={person.email}
|
||||
icon={<IconMail />}
|
||||
placeholder={'Email'}
|
||||
onSubmit={(newEmail) => {
|
||||
updatePerson({
|
||||
variables: {
|
||||
id: person.id,
|
||||
email: newEmail,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<PhoneEditableField
|
||||
value={person.phone}
|
||||
icon={<IconPhone />}
|
||||
placeholder={'Phone'}
|
||||
onSubmit={(newPhone) => {
|
||||
updatePerson({
|
||||
variables: {
|
||||
id: person.id,
|
||||
phone: newPhone,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<DateEditableField
|
||||
value={person.createdAt}
|
||||
icon={<IconCalendar />}
|
||||
onSubmit={(newDate) => {
|
||||
updatePerson({
|
||||
variables: {
|
||||
id: person.id,
|
||||
createdAt: newDate,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
<PeopleCompanyEditableField people={person} />
|
||||
<TextEditableField
|
||||
value={person.city}
|
||||
icon={<IconMap />}
|
||||
placeholder={'City'}
|
||||
onSubmit={(newCity) => {
|
||||
updatePerson({
|
||||
variables: {
|
||||
id: person.id,
|
||||
city: newCity,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</PropertyBox>
|
||||
);
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { IconBuildingSkyscraper } from '@tabler/icons-react';
|
||||
|
||||
import { CompanyChip } from '@/companies/components/CompanyChip';
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
||||
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/relation-picker/types/RelationPickerHotkeyScope';
|
||||
import { Company, Person } from '~/generated/graphql';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
|
||||
import { PeopleCompanyEditableFieldEditMode } from './PeopleCompanyEditableFieldEditMode';
|
||||
|
||||
export type OwnProps = {
|
||||
people: Pick<Person, 'id'> & {
|
||||
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
|
||||
};
|
||||
};
|
||||
|
||||
export function PeopleCompanyEditableField({ people }: OwnProps) {
|
||||
return (
|
||||
<RecoilScope SpecificContext={FieldContext}>
|
||||
<RecoilScope>
|
||||
<EditableField
|
||||
customEditHotkeyScope={{
|
||||
scope: RelationPickerHotkeyScope.RelationPicker,
|
||||
}}
|
||||
iconLabel={<IconBuildingSkyscraper />}
|
||||
editModeContent={
|
||||
<PeopleCompanyEditableFieldEditMode people={people} />
|
||||
}
|
||||
displayModeContent={
|
||||
people.company ? (
|
||||
<CompanyChip
|
||||
id={people.company.id}
|
||||
name={people.company.name}
|
||||
picture={getLogoUrlFromDomainName(people.company.domainName)}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)
|
||||
}
|
||||
isDisplayModeContentEmpty={!people.company}
|
||||
/>
|
||||
</RecoilScope>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { useFilteredSearchCompanyQuery } from '@/companies/queries';
|
||||
import { useEditableField } from '@/ui/editable-field/hooks/useEditableField';
|
||||
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { SingleEntitySelect } from '@/ui/relation-picker/components/SingleEntitySelect';
|
||||
import { relationPickerSearchFilterScopedState } from '@/ui/relation-picker/states/relationPickerSearchFilterScopedState';
|
||||
import { EntityForSelect } from '@/ui/relation-picker/types/EntityForSelect';
|
||||
import { Company, Person, useUpdatePeopleMutation } from '~/generated/graphql';
|
||||
|
||||
export type OwnProps = {
|
||||
people: Pick<Person, 'id'> & { company?: Pick<Company, 'id'> | null };
|
||||
};
|
||||
|
||||
export function PeopleCompanyEditableFieldEditMode({ people }: OwnProps) {
|
||||
const { closeEditableField } = useEditableField();
|
||||
|
||||
const [searchFilter] = useRecoilScopedState(
|
||||
relationPickerSearchFilterScopedState,
|
||||
);
|
||||
const [updatePeople] = useUpdatePeopleMutation();
|
||||
|
||||
const companies = useFilteredSearchCompanyQuery({
|
||||
searchFilter,
|
||||
selectedIds: people.company?.id ? [people.company.id] : [],
|
||||
});
|
||||
|
||||
async function handleEntitySelected(entity: EntityForSelect) {
|
||||
await updatePeople({
|
||||
variables: {
|
||||
...people,
|
||||
companyId: entity.id,
|
||||
},
|
||||
});
|
||||
closeEditableField();
|
||||
}
|
||||
|
||||
function handleCancel() {
|
||||
closeEditableField();
|
||||
}
|
||||
|
||||
return (
|
||||
<SingleEntitySelect
|
||||
onEntitySelected={handleEntitySelected}
|
||||
entities={{
|
||||
entitiesToSelect: companies.entitiesToSelect,
|
||||
selectedEntity: companies.selectedEntities[0],
|
||||
loading: companies.loading,
|
||||
}}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
);
|
||||
}
|
@ -11,9 +11,13 @@ export const GET_PERSON = gql`
|
||||
displayName
|
||||
email
|
||||
createdAt
|
||||
city
|
||||
phone
|
||||
_commentThreadCount
|
||||
company {
|
||||
id
|
||||
name
|
||||
domainName
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { formatToHumanReadableDate } from '~/utils';
|
||||
|
||||
type OwnProps = {
|
||||
value: Date;
|
||||
value: Date | null;
|
||||
};
|
||||
|
||||
export function InplaceInputDateDisplayMode({ value }: OwnProps) {
|
||||
|
@ -16,7 +16,6 @@ export const EditableFieldNormalModeOuterContainer = styled.div<
|
||||
padding: ${({ theme }) => theme.spacing(1)};
|
||||
|
||||
${(props) => {
|
||||
console.log(props.isDisplayModeContentEmpty);
|
||||
if (props.isDisplayModeContentEmpty) {
|
||||
return css`
|
||||
min-width: 50px;
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { currentHotkeyScopeState } from '@/ui/hotkey/states/internal/currentHotkeyScopeState';
|
||||
import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||
import { isSameHotkeyScope } from '@/ui/hotkey/utils/isSameHotkeyScope';
|
||||
import { useContextScopeId } from '@/ui/recoil-scope/hooks/useContextScopeId';
|
||||
import { useRecoilScopedState } from '@/ui/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { getSnapshotScopedState } from '@/ui/recoil-scope/utils/getSnapshotScopedState';
|
||||
import { getSnapshotState } from '@/ui/recoil-scope/utils/getSnapshotState';
|
||||
|
||||
import { customEditHotkeyScopeForFieldScopedState } from '../states/customEditHotkeyScopeForFieldScopedState';
|
||||
import { FieldContext } from '../states/FieldContext';
|
||||
@ -21,8 +26,7 @@ export function useBindFieldHotkeyScope({
|
||||
FieldContext,
|
||||
);
|
||||
|
||||
const [parentHotkeyScopeForField, setParentHotkeyScopeForField] =
|
||||
useRecoilScopedState(parentHotkeyScopeForFieldScopedState, FieldContext);
|
||||
const fieldContextScopeId = useContextScopeId(FieldContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@ -37,16 +41,35 @@ export function useBindFieldHotkeyScope({
|
||||
setCustomEditHotkeyScopeForField,
|
||||
]);
|
||||
|
||||
const setParentHotkeyScopeForField = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
(parentHotkeyScopeToSet: HotkeyScope | null | undefined) => {
|
||||
const currentHotkeyScope = getSnapshotState({
|
||||
snapshot,
|
||||
state: currentHotkeyScopeState,
|
||||
});
|
||||
|
||||
const parentHotkeyScopeForField = getSnapshotScopedState({
|
||||
snapshot,
|
||||
state: parentHotkeyScopeForFieldScopedState,
|
||||
contextScopeId: fieldContextScopeId,
|
||||
});
|
||||
|
||||
if (!parentHotkeyScopeToSet) {
|
||||
set(
|
||||
parentHotkeyScopeForFieldScopedState(fieldContextScopeId),
|
||||
currentHotkeyScope,
|
||||
);
|
||||
} else if (
|
||||
!isSameHotkeyScope(parentHotkeyScopeToSet, parentHotkeyScopeForField)
|
||||
) {
|
||||
setParentHotkeyScopeForField(parentHotkeyScopeToSet);
|
||||
}
|
||||
},
|
||||
[fieldContextScopeId],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
parentHotkeyScope &&
|
||||
!isSameHotkeyScope(parentHotkeyScope, parentHotkeyScopeForField)
|
||||
) {
|
||||
setParentHotkeyScopeForField(parentHotkeyScope);
|
||||
}
|
||||
}, [
|
||||
parentHotkeyScope,
|
||||
parentHotkeyScopeForField,
|
||||
setParentHotkeyScopeForField,
|
||||
]);
|
||||
setParentHotkeyScopeForField(parentHotkeyScope);
|
||||
}, [parentHotkeyScope, setParentHotkeyScopeForField]);
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import { isFieldInEditModeScopedState } from '../states/isFieldInEditModeScopedS
|
||||
import { parentHotkeyScopeForFieldScopedState } from '../states/parentHotkeyScopeForFieldScopedState';
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
|
||||
// TODO: use atoms for hotkey scopes
|
||||
export function useEditableField() {
|
||||
const [isFieldInEditMode, setIsFieldInEditMode] = useRecoilScopedState(
|
||||
isFieldInEditModeScopedState,
|
||||
|
@ -0,0 +1,65 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { InplaceInputDateDisplayMode } from '@/ui/display/component/InplaceInputDateDisplayMode';
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
||||
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
import { EditableFieldEditModeDate } from './EditableFieldEditModeDate';
|
||||
|
||||
type OwnProps = {
|
||||
icon?: React.ReactNode;
|
||||
value: string | null | undefined;
|
||||
onSubmit?: (newValue: string) => void;
|
||||
};
|
||||
|
||||
export function DateEditableField({ icon, value, onSubmit }: OwnProps) {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(value);
|
||||
}, [value]);
|
||||
|
||||
async function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
|
||||
onSubmit?.(newValue);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!internalValue) return;
|
||||
|
||||
onSubmit?.(internalValue);
|
||||
}
|
||||
|
||||
async function handleCancel() {
|
||||
setInternalValue(value);
|
||||
}
|
||||
|
||||
const internalDateValue = internalValue
|
||||
? parseDate(internalValue).toJSDate()
|
||||
: null;
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={FieldContext}>
|
||||
<EditableField
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
iconLabel={icon}
|
||||
editModeContent={
|
||||
<EditableFieldEditModeDate
|
||||
value={internalValue ?? ''}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
displayModeContent={
|
||||
<InplaceInputDateDisplayMode value={internalDateValue} />
|
||||
}
|
||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
@ -2,7 +2,7 @@ import { HotkeyScope } from '@/ui/hotkey/types/HotkeyScope';
|
||||
import { InplaceInputDate } from '@/ui/inplace-input/components/InplaceInputDate';
|
||||
import { parseDate } from '~/utils/date-utils';
|
||||
|
||||
import { useEditableField } from '../hooks/useEditableField';
|
||||
import { useEditableField } from '../../hooks/useEditableField';
|
||||
|
||||
type OwnProps = {
|
||||
value: string;
|
@ -0,0 +1,66 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { InplaceInputPhoneDisplayMode } from '@/ui/display/component/InplaceInputPhoneDisplayMode';
|
||||
import { EditableField } from '@/ui/editable-field/components/EditableField';
|
||||
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
|
||||
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
|
||||
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
|
||||
|
||||
type OwnProps = {
|
||||
icon?: React.ReactNode;
|
||||
placeholder?: string;
|
||||
value: string | null | undefined;
|
||||
onSubmit?: (newValue: string) => void;
|
||||
};
|
||||
|
||||
export function PhoneEditableField({
|
||||
icon,
|
||||
placeholder,
|
||||
value,
|
||||
onSubmit,
|
||||
}: OwnProps) {
|
||||
const [internalValue, setInternalValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(value);
|
||||
}, [value]);
|
||||
|
||||
async function handleChange(newValue: string) {
|
||||
setInternalValue(newValue);
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
if (!internalValue) return;
|
||||
|
||||
onSubmit?.(internalValue);
|
||||
}
|
||||
|
||||
async function handleCancel() {
|
||||
setInternalValue(value);
|
||||
}
|
||||
|
||||
return (
|
||||
<RecoilScope SpecificContext={FieldContext}>
|
||||
<EditableField
|
||||
onSubmit={handleSubmit}
|
||||
onCancel={handleCancel}
|
||||
iconLabel={icon}
|
||||
editModeContent={
|
||||
<InplaceInputText
|
||||
placeholder={placeholder ?? ''}
|
||||
autoFocus
|
||||
value={internalValue ?? ''}
|
||||
onChange={(newValue: string) => {
|
||||
handleChange(newValue);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
displayModeContent={
|
||||
<InplaceInputPhoneDisplayMode value={internalValue ?? ''} />
|
||||
}
|
||||
isDisplayModeContentEmpty={!(internalValue !== '')}
|
||||
useEditButton
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { IconCalendar } from '@tabler/icons-react';
|
||||
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { DateEditableField } from '../DateEditableField';
|
||||
|
||||
const meta: Meta<typeof DateEditableField> = {
|
||||
title: 'UI/EditableField/DateEditableField',
|
||||
component: DateEditableField,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof DateEditableField>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<DateEditableField
|
||||
value={new Date().toISOString()}
|
||||
icon={<IconCalendar />}
|
||||
/>,
|
||||
),
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { IconCurrencyDollar } from '@tabler/icons-react';
|
||||
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { NumberEditableField } from '../NumberEditableField';
|
||||
|
||||
const meta: Meta<typeof NumberEditableField> = {
|
||||
title: 'UI/EditableField/NumberEditableField',
|
||||
component: NumberEditableField,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof NumberEditableField>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<NumberEditableField
|
||||
value={10}
|
||||
icon={<IconCurrencyDollar />}
|
||||
placeholder="Number"
|
||||
/>,
|
||||
),
|
||||
};
|
@ -0,0 +1,27 @@
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { IconPhone } from '@tabler/icons-react';
|
||||
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { PhoneEditableField } from '../PhoneEditableField';
|
||||
|
||||
const meta: Meta<typeof PhoneEditableField> = {
|
||||
title: 'UI/EditableField/PhoneEditableField',
|
||||
component: PhoneEditableField,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof PhoneEditableField>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<BrowserRouter>
|
||||
<PhoneEditableField
|
||||
value={'+33714446494'}
|
||||
icon={<IconPhone />}
|
||||
placeholder="Phone"
|
||||
/>
|
||||
</BrowserRouter>,
|
||||
),
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { IconUser } from '@tabler/icons-react';
|
||||
|
||||
import { getRenderWrapperForComponent } from '~/testing/renderWrappers';
|
||||
|
||||
import { TextEditableField } from '../TextEditableField';
|
||||
|
||||
const meta: Meta<typeof TextEditableField> = {
|
||||
title: 'UI/EditableField/TextEditableField',
|
||||
component: TextEditableField,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof TextEditableField>;
|
||||
|
||||
export const Default: Story = {
|
||||
render: getRenderWrapperForComponent(
|
||||
<TextEditableField
|
||||
value={'John Doe'}
|
||||
icon={<IconUser />}
|
||||
placeholder="Name"
|
||||
/>,
|
||||
),
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { currentHotkeyScopeState } from '../states/internal/currentHotkeyScopeState';
|
||||
import { CustomHotkeyScopes } from '../types/CustomHotkeyScope';
|
||||
@ -13,8 +13,6 @@ export function usePreviousHotkeyScope() {
|
||||
|
||||
const setHotkeyScope = useSetHotkeyScope();
|
||||
|
||||
const currentHotkeyScope = useRecoilValue(currentHotkeyScopeState);
|
||||
|
||||
function goBackToPreviousHotkeyScope() {
|
||||
if (previousHotkeyScope) {
|
||||
setHotkeyScope(
|
||||
@ -24,13 +22,19 @@ export function usePreviousHotkeyScope() {
|
||||
}
|
||||
}
|
||||
|
||||
function setHotkeyScopeAndMemorizePreviousScope(
|
||||
scope: string,
|
||||
customScopes?: CustomHotkeyScopes,
|
||||
) {
|
||||
setPreviousHotkeyScope(currentHotkeyScope);
|
||||
setHotkeyScope(scope, customScopes);
|
||||
}
|
||||
const setHotkeyScopeAndMemorizePreviousScope = useRecoilCallback(
|
||||
({ snapshot }) =>
|
||||
(scope: string, customScopes?: CustomHotkeyScopes) => {
|
||||
const currentHotkeyScope = snapshot
|
||||
.getLoadable(currentHotkeyScopeState)
|
||||
.valueOrThrow();
|
||||
|
||||
setHotkeyScope(scope, customScopes);
|
||||
|
||||
setPreviousHotkeyScope(currentHotkeyScope);
|
||||
},
|
||||
[setPreviousHotkeyScope],
|
||||
);
|
||||
|
||||
return {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
|
@ -19,12 +19,12 @@ function isCustomScopesEqual(
|
||||
export function useSetHotkeyScope() {
|
||||
return useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async (HotkeyScopeToSet: string, customScopes?: CustomHotkeyScopes) => {
|
||||
async (hotkeyScopeToSet: string, customScopes?: CustomHotkeyScopes) => {
|
||||
const currentHotkeyScope = await snapshot.getPromise(
|
||||
currentHotkeyScopeState,
|
||||
);
|
||||
|
||||
if (currentHotkeyScope.scope === HotkeyScopeToSet) {
|
||||
if (currentHotkeyScope.scope === hotkeyScopeToSet) {
|
||||
if (!isDefined(customScopes)) {
|
||||
if (
|
||||
isCustomScopesEqual(
|
||||
@ -47,7 +47,7 @@ export function useSetHotkeyScope() {
|
||||
}
|
||||
|
||||
set(currentHotkeyScopeState, {
|
||||
scope: HotkeyScopeToSet,
|
||||
scope: hotkeyScopeToSet,
|
||||
customScopes: {
|
||||
commandMenu: customScopes?.commandMenu ?? true,
|
||||
goto: customScopes?.goto ?? false,
|
||||
|
@ -48,7 +48,7 @@ export const DatePickerContainer = ({ children }: DatePickerContainerProps) => {
|
||||
};
|
||||
|
||||
type OwnProps = {
|
||||
value: Date;
|
||||
value: Date | null | undefined;
|
||||
onChange: (newDate: Date) => void;
|
||||
};
|
||||
|
||||
@ -56,7 +56,7 @@ export function InplaceInputDate({ onChange, value }: OwnProps) {
|
||||
return (
|
||||
<InplaceInputContainer>
|
||||
<DatePicker
|
||||
date={value}
|
||||
date={value ?? new Date()}
|
||||
onChangeHandler={onChange}
|
||||
customInput={<DateDisplay />}
|
||||
customCalendarContainer={DatePickerContainer}
|
||||
|
12
front/src/modules/ui/recoil-scope/hooks/useContextScopeId.ts
Normal file
12
front/src/modules/ui/recoil-scope/hooks/useContextScopeId.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Context, useContext } from 'react';
|
||||
|
||||
export function useContextScopeId(SpecificContext: Context<string | null>) {
|
||||
const recoilScopeId = useContext(SpecificContext);
|
||||
|
||||
if (!recoilScopeId)
|
||||
throw new Error(
|
||||
`Using useContextScopedId outside of the specified context : ${SpecificContext.displayName}, verify that you are using a RecoilScope with the specific context you want to use.`,
|
||||
);
|
||||
|
||||
return recoilScopeId;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
import { RecoilState, Snapshot } from 'recoil';
|
||||
|
||||
export function getSnapshotScopedState<T>({
|
||||
snapshot,
|
||||
state,
|
||||
contextScopeId,
|
||||
}: {
|
||||
snapshot: Snapshot;
|
||||
state: (scopeId: string) => RecoilState<T>;
|
||||
contextScopeId: string;
|
||||
}) {
|
||||
return snapshot.getLoadable(state(contextScopeId)).valueOrThrow();
|
||||
}
|
11
front/src/modules/ui/recoil-scope/utils/getSnapshotState.ts
Normal file
11
front/src/modules/ui/recoil-scope/utils/getSnapshotState.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { RecoilState, Snapshot } from 'recoil';
|
||||
|
||||
export function getSnapshotState<T>({
|
||||
snapshot,
|
||||
state,
|
||||
}: {
|
||||
snapshot: Snapshot;
|
||||
state: RecoilState<T>;
|
||||
}) {
|
||||
return snapshot.getLoadable(state).valueOrThrow();
|
||||
}
|
@ -25,6 +25,7 @@ export function EditableCellDateEditMode({
|
||||
|
||||
function handleDateChange(newDate: Date) {
|
||||
onChange(newDate);
|
||||
|
||||
closeEditableCell();
|
||||
}
|
||||
|
||||
|
@ -112,7 +112,6 @@ export function PasswordLogin() {
|
||||
}
|
||||
navigate('/auth/create/workspace');
|
||||
} catch (err: any) {
|
||||
console.log('err', err);
|
||||
enqueueSnackBar(err?.message, {
|
||||
variant: 'error',
|
||||
});
|
||||
|
@ -99,7 +99,6 @@ export const EditNote: Story = {
|
||||
graphql.query(
|
||||
getOperationName(GET_COMMENT_THREAD) ?? '',
|
||||
(req, res, ctx) => {
|
||||
console.log('coucou');
|
||||
return res(
|
||||
ctx.data({
|
||||
findManyCommentThreads: [mockedCommentThreads[0]],
|
||||
|
@ -2,10 +2,9 @@ import { useParams } from 'react-router-dom';
|
||||
import { useTheme } from '@emotion/react';
|
||||
|
||||
import { Timeline } from '@/activities/timeline/components/Timeline';
|
||||
import { PersonPropertyBox } from '@/people/components/PersonPropertyBox';
|
||||
import { usePersonQuery } from '@/people/queries';
|
||||
import { PropertyBox } from '@/ui/editable-field/property-box/components/PropertyBox';
|
||||
import { PropertyBoxItem } from '@/ui/editable-field/property-box/components/PropertyBoxItem';
|
||||
import { IconLink, IconUser } from '@/ui/icon';
|
||||
import { IconUser } 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';
|
||||
@ -33,14 +32,7 @@ export function PersonShow() {
|
||||
title={person?.displayName ?? 'No name'}
|
||||
date={person?.createdAt ?? ''}
|
||||
/>
|
||||
<PropertyBox extraPadding={true}>
|
||||
<>
|
||||
<PropertyBoxItem
|
||||
icon={<IconLink />}
|
||||
value={person?.firstName ?? 'No First name'}
|
||||
/>
|
||||
</>
|
||||
</PropertyBox>
|
||||
{person && <PersonPropertyBox person={person} />}
|
||||
</ShowPageLeftContainer>
|
||||
<ShowPageRightContainer>
|
||||
<Timeline
|
||||
|
Loading…
Reference in New Issue
Block a user