Added two editable fields on company board card (#738)

This commit is contained in:
Lucas Bordeau 2023-07-18 21:02:45 +02:00 committed by GitHub
parent 9378677744
commit 84018efc7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 238 additions and 23 deletions

View File

@ -10,8 +10,8 @@ import { BoardCardContext } from '@/pipeline/states/BoardCardContext';
import { pipelineProgressIdScopedState } from '@/pipeline/states/pipelineProgressIdScopedState';
import { selectedBoardCardsState } from '@/pipeline/states/selectedBoardCardsState';
import { BoardCardEditableFieldDate } from '@/ui/board/card-field/components/BoardCardEditableFieldDate';
import { BoardCardEditableFieldText } from '@/ui/board/card-field/components/BoardCardEditableFieldText';
import { ChipVariant } from '@/ui/chip/components/EntityChip';
import { NumberEditableField } from '@/ui/editable-field/variants/components/NumberEditableField';
import { IconCurrencyDollar } from '@/ui/icon';
import { IconCalendarEvent } from '@/ui/icon';
import { Checkbox } from '@/ui/input/components/Checkbox';
@ -22,6 +22,8 @@ import {
} from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { CompanyAccountOwnerEditableField } from '../editable-field/components/CompanyAccountOwnerEditableField';
import { CompanyChip } from './CompanyChip';
const StyledBoardCard = styled.div<{ selected: boolean }>`
@ -143,19 +145,18 @@ export function CompanyBoardCard() {
<Checkbox checked={selected} onChange={handleCheckboxChange} />
</StyledBoardCardHeader>
<StyledBoardCardBody>
<span>
<IconCurrencyDollar size={theme.icon.size.md} />
<BoardCardEditableFieldText
value={pipelineProgress.amount?.toString() || ''}
<NumberEditableField
icon={<IconCurrencyDollar />}
placeholder="Opportunity amount"
onChange={(value) =>
value={pipelineProgress.amount}
onSubmit={(value) =>
handleCardUpdate({
...pipelineProgress,
amount: parseInt(value),
amount: value,
})
}
/>
</span>
<CompanyAccountOwnerEditableField company={company} />
<span>
<IconCalendarEvent size={theme.icon.size.md} />
<BoardCardEditableFieldDate

View File

@ -1,5 +1,4 @@
import { PersonChip } from '@/people/components/PersonChip';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { IconUserCircle } from '@/ui/icon';
@ -23,12 +22,6 @@ export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
customEditHotkeyScope={{
scope: RelationPickerHotkeyScope.RelationPicker,
}}
parentHotkeyScope={{
scope: PageHotkeyScope.CompanyShowPage,
customScopes: {
goto: true,
},
}}
iconLabel={<IconUserCircle />}
editModeContent={
<CompanyAccountOwnerPickerFieldEditMode company={company} />
@ -44,6 +37,7 @@ export function CompanyAccountOwnerEditableField({ company }: OwnProps) {
<></>
)
}
isDisplayModeContentEmpty={!company.accountOwner}
/>
</RecoilScope>
</RecoilScope>

View File

@ -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';

View File

@ -0,0 +1,79 @@
import { useEffect, useState } from 'react';
import { EditableField } from '@/ui/editable-field/components/EditableField';
import { FieldContext } from '@/ui/editable-field/states/FieldContext';
import { IconCurrencyDollar } from '@/ui/icon';
import { InplaceInputText } from '@/ui/inplace-input/components/InplaceInputText';
import { RecoilScope } from '@/ui/recoil-scope/components/RecoilScope';
import {
PipelineProgress,
useUpdateOnePipelineProgressMutation,
} from '~/generated/graphql';
type OwnProps = {
progress: Pick<PipelineProgress, 'id' | 'amount'>;
};
export function PipelineProgressAmountEditableField({ progress }: OwnProps) {
const [internalValue, setInternalValue] = useState(
progress.amount?.toString(),
);
const [updateOnePipelineProgress] = useUpdateOnePipelineProgressMutation();
useEffect(() => {
setInternalValue(progress.amount?.toString());
}, [progress.amount]);
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 updateOnePipelineProgress({
variables: {
id: progress.id,
amount: numberValue,
},
});
setInternalValue(numberValue.toString());
} catch {
handleCancel();
}
}
async function handleCancel() {
setInternalValue(progress.amount?.toString());
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={<IconCurrencyDollar />}
editModeContent={
<InplaceInputText
placeholder={'Amount'}
autoFocus
value={internalValue ?? ''}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={internalValue}
/>
</RecoilScope>
);
}

View File

@ -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;

View File

@ -0,0 +1,79 @@
import { useEffect, useState } from 'react';
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: number | null | undefined;
onSubmit?: (newValue: number) => void;
};
export function NumberEditableField({
icon,
placeholder,
value,
onSubmit,
}: OwnProps) {
const [internalValue, setInternalValue] = useState(value?.toString());
useEffect(() => {
setInternalValue(value?.toString());
}, [value]);
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');
}
// TODO: find a way to store this better in DB
if (numberValue > 2000000000) {
throw new Error('Number too big');
}
onSubmit?.(numberValue);
setInternalValue(numberValue.toString());
} catch {
handleCancel();
}
}
async function handleCancel() {
setInternalValue(value?.toString());
}
return (
<RecoilScope SpecificContext={FieldContext}>
<EditableField
onSubmit={handleSubmit}
onCancel={handleCancel}
iconLabel={icon}
editModeContent={
<InplaceInputText
placeholder={placeholder ?? ''}
autoFocus
value={internalValue ?? ''}
onChange={(newValue: string) => {
handleChange(newValue);
}}
/>
}
displayModeContent={internalValue}
isDisplayModeContentEmpty={!(internalValue !== '')}
/>
</RecoilScope>
);
}

View File

@ -0,0 +1,62 @@
import { useEffect, useState } from 'react';
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 TextEditableField({
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={internalValue}
isDisplayModeContentEmpty={!(internalValue !== '')}
/>
</RecoilScope>
);
}