Feat/company card fields (#686)

* wip

* Ok

* asd

* Fixed cancel submit

* Renamed

* Fixed
This commit is contained in:
Lucas Bordeau
2023-07-16 04:17:31 +02:00
committed by GitHub
parent 7959308e0b
commit be21392737
35 changed files with 1041 additions and 95 deletions

View File

@ -1,6 +1,7 @@
import { PersonChip } from '@/people/components/PersonChip';
import { RelationPickerHotkeyScope } from '@/relation-picker/types/RelationPickerHotkeyScope';
import { EditableCell } from '@/ui/components/editable-cell/EditableCell';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useEditableCell';
import { Company, User } from '~/generated/graphql';
import { CompanyAccountOwnerPicker } from './CompanyAccountOwnerPicker';
@ -14,10 +15,26 @@ export type OwnProps = {
};
export function CompanyAccountOwnerCell({ company }: OwnProps) {
const { closeEditableCell } = useEditableCell();
function handleCancel() {
closeEditableCell();
}
function handleSubmit() {
closeEditableCell();
}
return (
<EditableCell
editHotkeyScope={{ scope: RelationPickerHotkeyScope.RelationPicker }}
editModeContent={<CompanyAccountOwnerPicker company={company} />}
editModeContent={
<CompanyAccountOwnerPicker
onCancel={handleCancel}
onSubmit={handleSubmit}
company={company}
/>
}
nonEditModeContent={
company.accountOwner?.displayName ? (
<PersonChip

View File

@ -4,7 +4,6 @@ import { relationPickerSearchFilterScopedState } from '@/relation-picker/states/
import { EntityForSelect } from '@/relation-picker/types/EntityForSelect';
import { Entity } from '@/relation-picker/types/EntityTypeForSelect';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { useEditableCell } from '@/ui/components/editable-cell/hooks/useEditableCell';
import {
Company,
User,
@ -16,20 +15,24 @@ export type OwnProps = {
company: Pick<Company, 'id'> & {
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
};
type UserForSelect = EntityForSelect & {
entityType: Entity.User;
};
export function CompanyAccountOwnerPicker({ company }: OwnProps) {
export function CompanyAccountOwnerPicker({
company,
onSubmit,
onCancel,
}: OwnProps) {
const [searchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const [updateCompany] = useUpdateCompanyMutation();
const { closeEditableCell } = useEditableCell();
const companies = useFilteredSearchEntityQuery({
queryHook: useSearchUserQuery,
selectedIds: [company?.accountOwner?.id ?? ''],
@ -52,12 +55,13 @@ export function CompanyAccountOwnerPicker({ company }: OwnProps) {
},
});
closeEditableCell();
onSubmit?.();
}
return (
<SingleEntitySelect
onEntitySelected={handleEntitySelected}
onCancel={onCancel}
entities={{
loading: companies.loading,
entitiesToSelect: companies.entitiesToSelect,

View File

@ -0,0 +1,53 @@
import { IconUserCircle } from '@tabler/icons-react';
import { PersonChip } from '@/people/components/PersonChip';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { RelationPickerHotkeyScope } from '@/relation-picker/types/RelationPickerHotkeyScope';
import { EditableField } from '@/ui/editable-fields/components/EditableField';
import { FieldContext } from '@/ui/editable-fields/states/FieldContext';
import { Company, User } from '~/generated/graphql';
import { PageHotkeyScope } from '~/sync-hooks/types/PageHotkeyScope';
import { CompanyAccountOwnerPickerFieldEditMode } from './CompanyAccountOwnerPickerFieldEditMode';
type OwnProps = {
company: Pick<Company, 'id' | 'accountOwnerId'> & {
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
};
};
export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
return (
<RecoilScope SpecificContext={FieldContext}>
<RecoilScope>
<EditableField
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
parentHotkeyScope={{
scope: PageHotkeyScope.CompanyShowPage,
}}
iconLabel={<IconUserCircle />}
editModeContent={
<CompanyAccountOwnerPickerFieldEditMode
parentHotkeyScope={{
scope: PageHotkeyScope.CompanyShowPage,
}}
company={company}
/>
}
displayModeContent={
company.accountOwner?.displayName ? (
<PersonChip
id={company.accountOwner.id}
name={company.accountOwner?.displayName ?? ''}
/>
) : (
<></>
)
}
/>
</RecoilScope>
</RecoilScope>
);
}

View File

@ -0,0 +1,50 @@
import styled from '@emotion/styled';
import { CompanyAccountOwnerPicker } from '@/companies/components/CompanyAccountOwnerPicker';
import { HotkeyScope } from '@/lib/hotkeys/types/HotkeyScope';
import { useEditableField } from '@/ui/editable-fields/hooks/useEditableField';
import { Company, User } from '~/generated/graphql';
const CompanyAccountOwnerPickerContainer = styled.div`
left: 24px;
position: absolute;
top: -8px;
`;
export type OwnProps = {
company: Pick<Company, 'id'> & {
accountOwner?: Pick<User, 'id' | 'displayName'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
parentHotkeyScope?: HotkeyScope;
};
export function CompanyAccountOwnerPickerFieldEditMode({
company,
onSubmit,
onCancel,
parentHotkeyScope,
}: OwnProps) {
const { closeEditableField } = useEditableField(parentHotkeyScope);
function handleSubmit() {
closeEditableField();
onSubmit?.();
}
function handleCancel() {
closeEditableField();
onCancel?.();
}
return (
<CompanyAccountOwnerPickerContainer>
<CompanyAccountOwnerPicker
company={company}
onCancel={handleCancel}
onSubmit={handleSubmit}
/>
</CompanyAccountOwnerPickerContainer>
);
}

View File

@ -0,0 +1,60 @@
import { useEffect, useState } from 'react';
import { IconMap } from '@tabler/icons-react';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EditableField } from '@/ui/editable-fields/components/EditableField';
import { FieldContext } from '@/ui/editable-fields/states/FieldContext';
import { InplaceInputText } from '@/ui/inplace-inputs/components/InplaceInputText';
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
type OwnProps = {
company: Pick<Company, 'id' | 'address'>;
};
export function CompanyAddressEditableField({ company }: OwnProps) {
const [internalValue, setInternalValue] = useState(company.address);
const [updateCompany] = useUpdateCompanyMutation();
useEffect(() => {
setInternalValue(company.address);
}, [company.address]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
}
async function handleSubmit() {
await updateCompany({
variables: {
id: company.id,
address: internalValue ?? '',
},
});
}
async function handleCancel() {
setInternalValue(company.address);
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={<IconMap />}
editModeContent={
<InplaceInputText
placeholder={'Address'}
autoFocus
value={internalValue}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={internalValue ?? ''}
/>
</RecoilScope>
);
}

View File

@ -0,0 +1,62 @@
import { useEffect, useState } from 'react';
import { IconCalendar } from '@tabler/icons-react';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EditableField } from '@/ui/editable-fields/components/EditableField';
import { FieldContext } from '@/ui/editable-fields/states/FieldContext';
import { EditableFieldEditModeDate } from '@/ui/editable-fields/variants/components/EditableFieldEditModeDate';
import { parseDate } from '@/utils/datetime/date-utils';
import { formatToHumanReadableDate } from '@/utils/utils';
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
type OwnProps = {
company: Pick<Company, 'id' | 'createdAt'>;
};
export function CompanyCreatedAtEditableField({ company }: OwnProps) {
const [internalValue, setInternalValue] = useState(company.createdAt);
const [updateCompany] = useUpdateCompanyMutation();
useEffect(() => {
setInternalValue(company.createdAt);
}, [company.createdAt]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
}
async function handleSubmit() {
await updateCompany({
variables: {
id: company.id,
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'
}
/>
</RecoilScope>
);
}

View File

@ -0,0 +1,62 @@
import { useEffect, useState } from 'react';
import { IconLink } from '@tabler/icons-react';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EditableField } from '@/ui/editable-fields/components/EditableField';
import { FieldDisplayURL } from '@/ui/editable-fields/components/FieldDisplayURL';
import { FieldContext } from '@/ui/editable-fields/states/FieldContext';
import { InplaceInputText } from '@/ui/inplace-inputs/components/InplaceInputText';
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
type OwnProps = {
company: Pick<Company, 'id' | 'domainName'>;
};
export function CompanyDomainNameEditableField({ company }: OwnProps) {
const [internalValue, setInternalValue] = useState(company.domainName);
const [updateCompany] = useUpdateCompanyMutation();
useEffect(() => {
setInternalValue(company.domainName);
}, [company.domainName]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
}
async function handleSubmit() {
await updateCompany({
variables: {
id: company.id,
domainName: internalValue ?? '',
},
});
}
async function handleCancel() {
setInternalValue(company.domainName);
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
iconLabel={<IconLink />}
onCancel={handleCancel}
onSubmit={handleSubmit}
editModeContent={
<InplaceInputText
placeholder={'URL'}
autoFocus
value={internalValue}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={<FieldDisplayURL URL={internalValue} />}
useEditButton
/>
</RecoilScope>
);
}

View File

@ -0,0 +1,76 @@
import { useEffect, useState } from 'react';
import { IconUsers } from '@tabler/icons-react';
import { RecoilScope } from '@/recoil-scope/components/RecoilScope';
import { EditableField } from '@/ui/editable-fields/components/EditableField';
import { FieldContext } from '@/ui/editable-fields/states/FieldContext';
import { InplaceInputText } from '@/ui/inplace-inputs/components/InplaceInputText';
import { Company, useUpdateCompanyMutation } from '~/generated/graphql';
type OwnProps = {
company: Pick<Company, 'id' | 'employees'>;
};
export function CompanyEmployeesEditableField({ company }: OwnProps) {
const [internalValue, setInternalValue] = useState(
company.employees?.toString(),
);
const [updateCompany] = useUpdateCompanyMutation();
useEffect(() => {
setInternalValue(company.employees?.toString());
}, [company.employees]);
async function handleChange(newValue: string) {
setInternalValue(newValue);
}
async function handleSubmit() {
if (!internalValue) return;
try {
const numberValue = parseInt(internalValue);
if (isNaN(numberValue)) {
throw new Error('Not a number');
}
await updateCompany({
variables: {
id: company.id,
employees: numberValue,
},
});
setInternalValue(numberValue.toString());
} catch {
handleCancel();
}
}
async function handleCancel() {
setInternalValue(company.employees?.toString());
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={<IconUsers />}
editModeContent={
<InplaceInputText
placeholder={'Employees'}
autoFocus
value={internalValue ?? ''}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={internalValue}
/>
</RecoilScope>
);
}