diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx index e08feba7a50d..ef129890ad02 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu.tsx @@ -1,5 +1,5 @@ -import { useCallback, useContext, useRef } from 'react'; import styled from '@emotion/styled'; +import { useCallback, useContext, useRef } from 'react'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; import { DropdownMenu } from '@/ui/layout/dropdown/components/DropdownMenu'; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx index 89ea3debc157..a730766c5ad3 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnHeader.tsx @@ -1,11 +1,16 @@ import styled from '@emotion/styled'; import { useContext, useState } from 'react'; -import { IconDotsVertical, Tag } from 'twenty-ui'; +import { IconDotsVertical, IconPlus, Tag } from 'twenty-ui'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; import { RecordBoardColumnDropdownMenu } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnDropdownMenu'; import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; +import { useAddNewOpportunity } from '@/object-record/record-board/record-board-column/hooks/useAddNewOpportunity'; import { RecordBoardColumnHotkeyScope } from '@/object-record/record-board/types/BoardColumnHotkeyScope'; import { RecordBoardColumnDefinitionType } from '@/object-record/record-board/types/RecordBoardColumnDefinition'; +import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; import { LightIconButton } from '@/ui/input/button/components/LightIconButton'; import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; @@ -38,11 +43,25 @@ const StyledHeaderActions = styled.div` display: flex; margin-left: auto; `; +const StyledHeaderContainer = styled.div` + display: flex; + justify-content: space-between; + width: 100%; +`; +const StyledLeftContainer = styled.div` + align-items: center; + display: flex; +`; + +const StyledRightContainer = styled.div` + align-items: center; + display: flex; +`; export const RecordBoardColumnHeader = () => { const [isBoardColumnMenuOpen, setIsBoardColumnMenuOpen] = useState(false); const [isHeaderHovered, setIsHeaderHovered] = useState(false); - + const { objectMetadataItem } = useContext(RecordBoardContext); const { columnDefinition, recordCount } = useContext( RecordBoardColumnContext, ); @@ -69,42 +88,75 @@ export const RecordBoardColumnHeader = () => { const boardColumnTotal = 0; + const { + isCreatingCard, + handleAddNewOpportunityClick, + handleCancel, + handleEntitySelect, + } = useAddNewOpportunity('first'); + const { handleAddNewCardClick } = useAddNewCard('first'); + + const isOpportunity = + objectMetadataItem.nameSingular === CoreObjectNameSingular.Opportunity; + + const handleClick = isOpportunity + ? handleAddNewOpportunityClick + : () => { + handleAddNewCardClick(); + }; + return ( <> setIsHeaderHovered(true)} onMouseLeave={() => setIsHeaderHovered(false)} > - - {!!boardColumnTotal && ${boardColumnTotal}} - {recordCount} - {isHeaderHovered && columnDefinition.actions.length > 0 && ( - - + + - - )} + {!!boardColumnTotal && ( + ${boardColumnTotal} + )} + {recordCount} + + + {isHeaderHovered && ( + + {columnDefinition.actions.length > 0 && ( + + )} + + + + )} + + {isBoardColumnMenuOpen && columnDefinition.actions.length > 0 && ( { stageId={columnDefinition.id} /> )} + {isCreatingCard && ( + + )} ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx index 62ce7d5f5a9b..c12e0c492374 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewButton.tsx @@ -1,10 +1,8 @@ -import { useContext } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconPlus } from 'twenty-ui'; -import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { useAddNewCard } from '@/object-record/record-board/record-board-column/hooks/useAddNewCard'; const StyledButton = styled.button` align-items: center; @@ -25,19 +23,9 @@ const StyledButton = styled.button` export const RecordBoardColumnNewButton = () => { const theme = useTheme(); - const { columnDefinition } = useContext(RecordBoardColumnContext); - const { createOneRecord, selectFieldMetadataItem } = - useContext(RecordBoardContext); - - const onNewClick = () => { - createOneRecord({ - [selectFieldMetadataItem.name]: columnDefinition.value, - position: 'last', - }); - }; - + const { handleAddNewCardClick } = useAddNewCard('last'); return ( - + New diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx index d085c69e993b..6464948dcfcc 100644 --- a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/components/RecordBoardColumnNewOpportunityButton.tsx @@ -1,16 +1,10 @@ -import { useCallback, useContext, useState } from 'react'; import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { IconPlus } from 'twenty-ui'; import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; -import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { useAddNewOpportunity } from '@/object-record/record-board/record-board-column/hooks/useAddNewOpportunity'; import { SingleEntitySelect } from '@/object-record/relation-picker/components/SingleEntitySelect'; -import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; -import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; -import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; -import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; const StyledButton = styled.button` align-items: center; @@ -30,52 +24,13 @@ const StyledButton = styled.button` `; export const RecordBoardColumnNewOpportunityButton = () => { - const [isCreatingCard, setIsCreatingCard] = useState(false); - const theme = useTheme(); - const { columnDefinition } = useContext(RecordBoardColumnContext); - const { createOneRecord, selectFieldMetadataItem } = - useContext(RecordBoardContext); - const { - goBackToPreviousHotkeyScope, - setHotkeyScopeAndMemorizePreviousScope, - } = usePreviousHotkeyScope(); - - const { resetSearchFilter } = useEntitySelectSearch({ - relationPickerScopeId: 'relation-picker', - }); - - const handleEntitySelect = (company?: EntityForSelect) => { - setIsCreatingCard(false); - goBackToPreviousHotkeyScope(); - resetSearchFilter(); - - if (!company) { - return; - } - - createOneRecord({ - name: company.name, - companyId: company.id, - position: 'last', - [selectFieldMetadataItem.name]: columnDefinition.value, - }); - }; - - const handleNewClick = useCallback(() => { - setIsCreatingCard(true); - setHotkeyScopeAndMemorizePreviousScope( - RelationPickerHotkeyScope.RelationPicker, - ); - }, [setIsCreatingCard, setHotkeyScopeAndMemorizePreviousScope]); - - const handleCancel = () => { - resetSearchFilter(); - goBackToPreviousHotkeyScope(); - setIsCreatingCard(false); - }; - + isCreatingCard, + handleAddNewOpportunityClick, + handleCancel, + handleEntitySelect, + } = useAddNewOpportunity('last'); return ( <> {isCreatingCard ? ( @@ -88,7 +43,7 @@ export const RecordBoardColumnNewOpportunityButton = () => { selectedRelationRecordIds={[]} /> ) : ( - + New diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts new file mode 100644 index 000000000000..24cb1e158548 --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewCard.ts @@ -0,0 +1,20 @@ +import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; +import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { useContext } from 'react'; + +export const useAddNewCard = (position: string) => { + const { columnDefinition } = useContext(RecordBoardColumnContext); + const { createOneRecord, selectFieldMetadataItem } = + useContext(RecordBoardContext); + + const handleAddNewCardClick = () => { + createOneRecord({ + [selectFieldMetadataItem.name]: columnDefinition.value, + position: position, + }); + }; + + return { + handleAddNewCardClick, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewOpportunity.ts b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewOpportunity.ts new file mode 100644 index 000000000000..230eedb281fb --- /dev/null +++ b/packages/twenty-front/src/modules/object-record/record-board/record-board-column/hooks/useAddNewOpportunity.ts @@ -0,0 +1,68 @@ +import { RecordBoardContext } from '@/object-record/record-board/contexts/RecordBoardContext'; +import { RecordBoardColumnContext } from '@/object-record/record-board/record-board-column/contexts/RecordBoardColumnContext'; +import { useEntitySelectSearch } from '@/object-record/relation-picker/hooks/useEntitySelectSearch'; +import { EntityForSelect } from '@/object-record/relation-picker/types/EntityForSelect'; +import { RelationPickerHotkeyScope } from '@/object-record/relation-picker/types/RelationPickerHotkeyScope'; +import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope'; +import { useCallback, useContext, useState } from 'react'; + +export const useAddNewOpportunity = (position: string) => { + const [isCreatingCard, setIsCreatingCard] = useState(false); + + const { columnDefinition } = useContext(RecordBoardColumnContext); + const { createOneRecord, selectFieldMetadataItem } = + useContext(RecordBoardContext); + + const { + goBackToPreviousHotkeyScope, + setHotkeyScopeAndMemorizePreviousScope, + } = usePreviousHotkeyScope(); + const { resetSearchFilter } = useEntitySelectSearch({ + relationPickerScopeId: 'relation-picker', + }); + + const handleEntitySelect = useCallback( + (company?: EntityForSelect) => { + setIsCreatingCard(false); + goBackToPreviousHotkeyScope(); + resetSearchFilter(); + + if (company !== undefined) { + createOneRecord({ + name: company.name, + companyId: company.id, + position: position, + [selectFieldMetadataItem.name]: columnDefinition.value, + }); + } + }, + [ + columnDefinition, + createOneRecord, + goBackToPreviousHotkeyScope, + resetSearchFilter, + selectFieldMetadataItem, + position, + ], + ); + + const handleAddNewOpportunityClick = useCallback(() => { + setIsCreatingCard(true); + setHotkeyScopeAndMemorizePreviousScope( + RelationPickerHotkeyScope.RelationPicker, + ); + }, [setHotkeyScopeAndMemorizePreviousScope]); + + const handleCancel = useCallback(() => { + resetSearchFilter(); + goBackToPreviousHotkeyScope(); + setIsCreatingCard(false); + }, [goBackToPreviousHotkeyScope, resetSearchFilter]); + + return { + isCreatingCard, + handleEntitySelect, + handleAddNewOpportunityClick, + handleCancel, + }; +}; diff --git a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx index 79c0e606817c..ba3abec91e75 100644 --- a/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx +++ b/packages/twenty-front/src/modules/object-record/record-index/components/RecordIndexBoardDataLoaderEffect.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; @@ -11,6 +11,7 @@ import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/s import { recordIndexIsCompactModeActiveState } from '@/object-record/record-index/states/recordIndexIsCompactModeActiveState'; import { recordIndexKanbanFieldMetadataIdState } from '@/object-record/record-index/states/recordIndexKanbanFieldMetadataIdState'; import { computeRecordBoardColumnDefinitionsFromObjectMetadata } from '@/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata'; +import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState'; import { FieldMetadataType } from '~/generated-metadata/graphql'; import { isDefined } from '~/utils/isDefined'; @@ -60,9 +61,21 @@ export const RecordIndexBoardDataLoaderEffect = ({ }, [recordIndexFieldDefinitions, setFieldDefinitions]); const navigate = useNavigate(); + const location = useLocation(); + const setNavigationMemorizedUrl = useSetRecoilState( + navigationMemorizedUrlState, + ); + const navigateToSelectSettings = useCallback(() => { + setNavigationMemorizedUrl(location.pathname + location.search); navigate(`/settings/objects/${getObjectSlug(objectMetadataItem)}`); - }, [navigate, objectMetadataItem]); + }, [ + navigate, + objectMetadataItem, + location.pathname, + location.search, + setNavigationMemorizedUrl, + ]); const { resetRecordSelection } = useRecordBoardSelection(recordBoardId); diff --git a/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts b/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts index 4abbba702a98..51b3fa1d3be9 100644 --- a/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts +++ b/packages/twenty-front/src/modules/object-record/utils/computeRecordBoardColumnDefinitionsFromObjectMetadata.ts @@ -1,4 +1,4 @@ -import { IconPencil } from 'twenty-ui'; +import { IconSettings } from 'twenty-ui'; import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { @@ -42,8 +42,8 @@ export const computeRecordBoardColumnDefinitionsFromObjectMetadata = ( actions: [ { id: 'edit', - label: 'Edit from settings', - icon: IconPencil, + label: 'Edit from Settings', + icon: IconSettings, position: 0, callback: navigateToSelectSettings, }, diff --git a/packages/twenty-front/vite.config.ts b/packages/twenty-front/vite.config.ts index dd1f3e330834..3d6a7fc98643 100644 --- a/packages/twenty-front/vite.config.ts +++ b/packages/twenty-front/vite.config.ts @@ -33,16 +33,19 @@ export default defineConfig(({ command, mode }) => { }; if (VITE_DISABLE_TYPESCRIPT_CHECKER === 'true') { + // eslint-disable-next-line no-console console.log( `VITE_DISABLE_TYPESCRIPT_CHECKER: ${VITE_DISABLE_TYPESCRIPT_CHECKER}`, ); } if (VITE_DISABLE_ESLINT_CHECKER === 'true') { + // eslint-disable-next-line no-console console.log(`VITE_DISABLE_ESLINT_CHECKER: ${VITE_DISABLE_ESLINT_CHECKER}`); } if (VITE_BUILD_SOURCEMAP === 'true') { + // eslint-disable-next-line no-console console.log(`VITE_BUILD_SOURCEMAP: ${VITE_BUILD_SOURCEMAP}`); }