Files
twenty_crm/packages/twenty-front/src/modules/views/view-picker/components/ViewPickerContentCreateMode.tsx
2025-03-17 16:10:52 +01:00

213 lines
8.5 KiB
TypeScript

import styled from '@emotion/styled';
import { Key } from 'ts-key-enum';
import { IconLayoutKanban, IconTable, IconX } from 'twenty-ui';
import { useObjectMetadataItemById } from '@/object-metadata/hooks/useObjectMetadataItemById';
import { IconPicker } from '@/ui/input/components/IconPicker';
import { Select } from '@/ui/input/components/Select';
import { TextInputV2 } from '@/ui/input/components/TextInputV2';
import { DropdownMenuHeader } from '@/ui/layout/dropdown/components/DropdownMenuHeader/DropdownMenuHeader';
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
import { viewObjectMetadataIdComponentState } from '@/views/states/viewObjectMetadataIdComponentState';
import { ViewsHotkeyScope } from '@/views/types/ViewsHotkeyScope';
import { ViewType } from '@/views/types/ViewType';
import { ViewPickerCreateButton } from '@/views/view-picker/components/ViewPickerCreateButton';
import { ViewPickerIconAndNameContainer } from '@/views/view-picker/components/ViewPickerIconAndNameContainer';
import { ViewPickerSaveButtonContainer } from '@/views/view-picker/components/ViewPickerSaveButtonContainer';
import { ViewPickerSelectContainer } from '@/views/view-picker/components/ViewPickerSelectContainer';
import { VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerKanbanFieldDropdownId';
import { VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID } from '@/views/view-picker/constants/ViewPickerViewTypeDropdownId';
import { useCreateViewFromCurrentState } from '@/views/view-picker/hooks/useCreateViewFromCurrentState';
import { useGetAvailableFieldsForKanban } from '@/views/view-picker/hooks/useGetAvailableFieldsForKanban';
import { useViewPickerMode } from '@/views/view-picker/hooks/useViewPickerMode';
import { viewPickerInputNameComponentState } from '@/views/view-picker/states/viewPickerInputNameComponentState';
import { viewPickerIsDirtyComponentState } from '@/views/view-picker/states/viewPickerIsDirtyComponentState';
import { viewPickerIsPersistingComponentState } from '@/views/view-picker/states/viewPickerIsPersistingComponentState';
import { viewPickerKanbanFieldMetadataIdComponentState } from '@/views/view-picker/states/viewPickerKanbanFieldMetadataIdComponentState';
import { viewPickerSelectedIconComponentState } from '@/views/view-picker/states/viewPickerSelectedIconComponentState';
import { viewPickerTypeComponentState } from '@/views/view-picker/states/viewPickerTypeComponentState';
import { useMemo, useState } from 'react';
import { useLingui } from '@lingui/react/macro';
const StyledNoKanbanFieldAvailableContainer = styled.div`
color: ${({ theme }) => theme.font.color.light};
display: flex;
margin: ${({ theme }) => theme.spacing(1, 2)};
user-select: none;
width: calc(100% - ${({ theme }) => theme.spacing(4)});
`;
export const ViewPickerContentCreateMode = () => {
const { t } = useLingui();
const { viewPickerMode, setViewPickerMode } = useViewPickerMode();
const [hasManuallySelectedIcon, setHasManuallySelectedIcon] = useState(false);
const viewObjectMetadataId = useRecoilComponentValueV2(
viewObjectMetadataIdComponentState,
);
const { objectMetadataItem } = useObjectMetadataItemById({
objectId: viewObjectMetadataId ?? '',
});
const [viewPickerInputName, setViewPickerInputName] =
useRecoilComponentStateV2(viewPickerInputNameComponentState);
const [viewPickerSelectedIcon, setViewPickerSelectedIcon] =
useRecoilComponentStateV2(viewPickerSelectedIconComponentState);
const viewPickerIsPersisting = useRecoilComponentValueV2(
viewPickerIsPersistingComponentState,
);
const setViewPickerIsDirty = useSetRecoilComponentStateV2(
viewPickerIsDirtyComponentState,
);
const [viewPickerKanbanFieldMetadataId, setViewPickerKanbanFieldMetadataId] =
useRecoilComponentStateV2(viewPickerKanbanFieldMetadataIdComponentState);
const [viewPickerType, setViewPickerType] = useRecoilComponentStateV2(
viewPickerTypeComponentState,
);
const setHotkeyScope = useSetHotkeyScope();
const { createViewFromCurrentState } = useCreateViewFromCurrentState();
const { availableFieldsForKanban } = useGetAvailableFieldsForKanban();
useScopedHotkeys(
Key.Enter,
async () => {
if (viewPickerIsPersisting) {
return;
}
if (
viewPickerType === ViewType.Kanban &&
availableFieldsForKanban.length === 0
) {
return;
}
await createViewFromCurrentState();
},
ViewsHotkeyScope.ListDropdown,
);
const defaultIcon =
viewPickerType === ViewType.Kanban ? 'IconLayoutKanban' : 'IconTable';
const selectedIcon = useMemo(() => {
if (hasManuallySelectedIcon) {
return viewPickerSelectedIcon;
}
if (viewPickerMode === 'create-from-current') {
return viewPickerSelectedIcon || defaultIcon;
}
return defaultIcon;
}, [
hasManuallySelectedIcon,
viewPickerSelectedIcon,
viewPickerMode,
defaultIcon,
]);
const onIconChange = ({ iconKey }: { iconKey: string }) => {
setViewPickerIsDirty(true);
setViewPickerSelectedIcon(iconKey);
setHasManuallySelectedIcon(true);
};
const handleClose = async () => {
setViewPickerMode('list');
};
return (
<>
<DropdownMenuHeader StartIcon={IconX} onStartIconClick={handleClose}>
{t`Create view`}
</DropdownMenuHeader>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer>
<ViewPickerIconAndNameContainer>
<IconPicker
onChange={onIconChange}
selectedIconKey={selectedIcon}
onClose={() => setHotkeyScope(ViewsHotkeyScope.ListDropdown)}
/>
<TextInputV2
value={viewPickerInputName}
onChange={(value) => {
setViewPickerIsDirty(true);
setViewPickerInputName(value);
}}
autoFocus
/>
</ViewPickerIconAndNameContainer>
<ViewPickerSelectContainer>
<Select
label={t`View type`}
fullWidth
value={viewPickerType}
onChange={(value) => {
setViewPickerIsDirty(true);
setViewPickerType(value);
}}
options={[
{ value: ViewType.Table, label: t`Table`, Icon: IconTable },
{
value: ViewType.Kanban,
label: t`Kanban`,
Icon: IconLayoutKanban,
},
]}
dropdownId={VIEW_PICKER_VIEW_TYPE_DROPDOWN_ID}
/>
</ViewPickerSelectContainer>
{viewPickerType === ViewType.Kanban && (
<>
<ViewPickerSelectContainer>
<Select
label={t`Stages`}
fullWidth
value={viewPickerKanbanFieldMetadataId}
onChange={(value) => {
setViewPickerIsDirty(true);
setViewPickerKanbanFieldMetadataId(value);
}}
options={
availableFieldsForKanban.length > 0
? availableFieldsForKanban.map((field) => ({
value: field.id,
label: field.label,
}))
: [{ value: '', label: t`No Select field` }]
}
dropdownId={VIEW_PICKER_KANBAN_FIELD_DROPDOWN_ID}
/>
</ViewPickerSelectContainer>
{availableFieldsForKanban.length === 0 && (
<StyledNoKanbanFieldAvailableContainer>
Set up a Select field on {objectMetadataItem.labelPlural} to
create a Kanban
</StyledNoKanbanFieldAvailableContainer>
)}
</>
)}
</DropdownMenuItemsContainer>
<DropdownMenuSeparator />
<DropdownMenuItemsContainer scrollable={false}>
<ViewPickerSaveButtonContainer>
<ViewPickerCreateButton />
</ViewPickerSaveButtonContainer>
</DropdownMenuItemsContainer>
</>
);
};