diff --git a/front/.eslintrc.js b/front/.eslintrc.js
index 51c37fcc1..154da81b2 100644
--- a/front/.eslintrc.js
+++ b/front/.eslintrc.js
@@ -21,6 +21,7 @@ module.exports = {
{
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
rules: {
+ 'no-control-regex': 0,
'simple-import-sort/imports': [
'error',
{
diff --git a/front/src/modules/command-menu/components/CommandMenu.tsx b/front/src/modules/command-menu/components/CommandMenu.tsx
index d4d3ed5ea..feaeb2de7 100644
--- a/front/src/modules/command-menu/components/CommandMenu.tsx
+++ b/front/src/modules/command-menu/components/CommandMenu.tsx
@@ -92,43 +92,6 @@ export function CommandMenu() {
cmd.type === CommandType.Create,
);
- /*
- TODO: Allow performing actions on page through CommandBar
-
- import { useMatch, useResolvedPath } from 'react-router-dom';
- import { IconBuildingSkyscraper, IconUser } from '@/ui/icon';
-
- const createSection = (
-
- }
- shortcuts={
- !!useMatch({
- path: useResolvedPath('/people').pathname,
- end: true,
- })
- ? ['C']
- : []
- }
- />
- }
- shortcuts={
- !!useMatch({
- path: useResolvedPath('/companies').pathname,
- end: true,
- })
- ? ['C']
- : []
- }
- />
-
- );*/
-
return (
)}
@@ -245,7 +209,7 @@ export function CommandMenu() {
)
.map((cmd) => (
void;
icon?: ReactNode;
shortcuts?: Array;
diff --git a/front/src/modules/companies/components/CompanyBoardCard.tsx b/front/src/modules/companies/components/CompanyBoardCard.tsx
index 9becd2c03..9af126435 100644
--- a/front/src/modules/companies/components/CompanyBoardCard.tsx
+++ b/front/src/modules/companies/components/CompanyBoardCard.tsx
@@ -2,8 +2,8 @@ import { ReactNode, useContext } from 'react';
import styled from '@emotion/styled';
import { useRecoilState, useRecoilValue } from 'recoil';
+import { useCurrentCardSelected } from '@/ui/board/hooks/useCurrentCardSelected';
import { BoardCardIdContext } from '@/ui/board/states/BoardCardIdContext';
-import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
import { EntityChipVariant } from '@/ui/chip/components/EntityChip';
import { GenericEditableField } from '@/ui/editable-field/components/GenericEditableField';
@@ -102,6 +102,8 @@ const StyledFieldContainer = styled.div`
`;
export function CompanyBoardCard() {
+ const { currentCardSelected, setCurrentCardSelected } =
+ useCurrentCardSelected();
const boardCardId = useContext(BoardCardIdContext);
const [companyProgress] = useRecoilState(
@@ -109,23 +111,8 @@ export function CompanyBoardCard() {
);
const { pipelineProgress, company } = companyProgress ?? {};
- const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
- selectedBoardCardIdsState,
- );
const viewFieldsDefinitions = useRecoilValue(viewFieldsDefinitionsState);
- const selected = selectedBoardCards.includes(boardCardId ?? '');
-
- function setSelected(isSelected: boolean) {
- if (isSelected) {
- setSelectedBoardCards([...selectedBoardCards, boardCardId ?? '']);
- } else {
- setSelectedBoardCards(
- selectedBoardCards.filter((id) => id !== boardCardId),
- );
- }
- }
-
// boardCardId check can be moved to a wrapper to avoid unnecessary logic above
if (!company || !pipelineProgress || !boardCardId) {
return null;
@@ -150,8 +137,8 @@ export function CompanyBoardCard() {
return (
setSelected(!selected)}
+ selected={currentCardSelected}
+ onClick={() => setCurrentCardSelected(!currentCardSelected)}
>
setSelected(!selected)}
+ checked={currentCardSelected}
+ onChange={() => setCurrentCardSelected(!currentCardSelected)}
variant={CheckboxVariant.Secondary}
/>
diff --git a/front/src/modules/companies/components/HooksCompanyBoard.tsx b/front/src/modules/companies/components/HooksCompanyBoard.tsx
index ffda2cf6e..5c86847f3 100644
--- a/front/src/modules/companies/components/HooksCompanyBoard.tsx
+++ b/front/src/modules/companies/components/HooksCompanyBoard.tsx
@@ -4,8 +4,10 @@ import { useRecoilState, useSetRecoilState } from 'recoil';
import { pipelineViewFields } from '@/pipeline/constants/pipelineViewFields';
import { isBoardLoadedState } from '@/ui/board/states/isBoardLoadedState';
import { viewFieldsDefinitionsState } from '@/ui/board/states/viewFieldsDefinitionsState';
+import { availableFiltersScopedState } from '@/ui/filter-n-sort/states/availableFiltersScopedState';
import { filtersScopedState } from '@/ui/filter-n-sort/states/filtersScopedState';
import { turnFilterIntoWhereClause } from '@/ui/filter-n-sort/utils/turnFilterIntoWhereClause';
+import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { useRecoilScopedValue } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedValue';
import {
PipelineProgressableType,
@@ -17,6 +19,7 @@ import {
useGetPipelineProgressQuery,
useGetPipelinesQuery,
} from '~/generated/graphql';
+import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
import { useUpdateCompanyBoard } from '../hooks/useUpdateCompanyBoardColumns';
@@ -30,8 +33,13 @@ export function HooksCompanyBoard({
const setFieldsDefinitionsState = useSetRecoilState(
viewFieldsDefinitionsState,
);
+ const [, setAvailableFilters] = useRecoilScopedState(
+ availableFiltersScopedState,
+ CompanyBoardContext,
+ );
useEffect(() => {
+ setAvailableFilters(opportunitiesBoardOptions.filters);
setFieldsDefinitionsState(pipelineViewFields);
});
diff --git a/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx b/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx
index ed68fe778..40864f980 100644
--- a/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx
+++ b/front/src/modules/ui/board/components/BoardActionBarButtonDeleteBoardCard.tsx
@@ -1,53 +1,40 @@
-import { useRecoilCallback } from 'recoil';
+import { getOperationName } from '@apollo/client/utilities';
+import { useRecoilValue } from 'recoil';
-import { boardCardIdsByColumnIdFamilyState } from '@/ui/board/states/boardCardIdsByColumnIdFamilyState';
-import { boardColumnsState } from '@/ui/board/states/boardColumnsState';
-import { selectedBoardCardIdsState } from '@/ui/board/states/selectedBoardCardIdsState';
+import { GET_PIPELINES } from '@/pipeline/queries';
import { IconTrash } from '@/ui/icon/index';
import { EntityTableActionBarButton } from '@/ui/table/action-bar/components/EntityTableActionBarButton';
+import { useDeleteManyPipelineProgressMutation } from '~/generated/graphql';
-export function BoardActionBarButtonDeleteBoardCard({
- onDelete,
-}: {
- onDelete: (deletedCardIds: string[]) => void;
-}) {
- const deleteBoardCardIds = useRecoilCallback(
- ({ set, snapshot }) =>
- () => {
- const boardCardIdsToDelete = snapshot
- .getLoadable(selectedBoardCardIdsState)
- .getValue();
+import { useRemoveCardIds } from '../hooks/useRemoveCardIds';
+import { selectedCardIdsSelector } from '../states/selectedCardIdsSelector';
- const boardColumns = snapshot.getLoadable(boardColumnsState).getValue();
+export function BoardActionBarButtonDeleteBoardCard() {
+ const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
+ const removeCardIds = useRemoveCardIds();
- for (const boardColumn of boardColumns) {
- const boardColumnCardIds = snapshot
- .getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
- .getValue();
+ const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
+ refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
+ });
- const newBoardColumnCardIds = boardColumnCardIds.filter(
- (cardId) => !boardCardIdsToDelete.includes(cardId),
- );
-
- if (newBoardColumnCardIds.length !== boardColumnCardIds.length) {
- set(
- boardCardIdsByColumnIdFamilyState(boardColumn.id),
- newBoardColumnCardIds,
- );
- }
- }
-
- set(selectedBoardCardIdsState, []);
-
- return boardCardIdsToDelete;
+ async function handleDelete() {
+ await deletePipelineProgress({
+ variables: {
+ ids: selectedCardIds,
},
- [],
- );
-
- async function handleDeleteClick() {
- const deletedCardIds = deleteBoardCardIds();
-
- onDelete(deletedCardIds);
+ optimisticResponse: {
+ __typename: 'Mutation',
+ deleteManyPipelineProgress: {
+ count: selectedCardIds.length,
+ },
+ },
+ update: (cache) => {
+ removeCardIds(selectedCardIds);
+ selectedCardIds.forEach((id) => {
+ cache.evict({ id: `PipelineProgress:${id}` });
+ });
+ },
+ });
}
return (
@@ -55,7 +42,7 @@ export function BoardActionBarButtonDeleteBoardCard({
label="Delete"
icon={}
type="warning"
- onClick={handleDeleteClick}
+ onClick={() => handleDelete()}
/>
);
}
diff --git a/front/src/modules/ui/board/components/EntityBoard.tsx b/front/src/modules/ui/board/components/EntityBoard.tsx
index d681c0e62..6bc6e33b6 100644
--- a/front/src/modules/ui/board/components/EntityBoard.tsx
+++ b/front/src/modules/ui/board/components/EntityBoard.tsx
@@ -10,7 +10,6 @@ import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
import { GET_PIPELINE_PROGRESS } from '@/pipeline/queries';
import { BoardHeader } from '@/ui/board/components/BoardHeader';
import { StyledBoard } from '@/ui/board/components/StyledBoard';
-import { useUpdateBoardCardIds } from '@/ui/board/hooks/useUpdateBoardCardIds';
import { BoardColumnIdContext } from '@/ui/board/states/BoardColumnIdContext';
import { SelectedSortType } from '@/ui/filter-n-sort/types/interface';
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
@@ -22,9 +21,10 @@ import {
useUpdateOnePipelineProgressStageMutation,
} from '~/generated/graphql';
+import { useSetCardSelected } from '../hooks/useSetCardSelected';
+import { useUpdateBoardCardIds } from '../hooks/useUpdateBoardCardIds';
import { BoardColumnContext } from '../states/BoardColumnContext';
import { boardColumnsState } from '../states/boardColumnsState';
-import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState';
import { BoardOptions } from '../types/BoardOptions';
import { EntityBoardColumn } from './EntityBoardColumn';
@@ -48,6 +48,7 @@ export function EntityBoard({
onEditColumnTitle: (columnId: string, title: string, color: string) => void;
}) {
const [boardColumns] = useRecoilState(boardColumnsState);
+ const setCardSelected = useSetCardSelected();
const theme = useTheme();
const [updatePipelineProgressStage] =
@@ -104,19 +105,6 @@ export function EntityBoard({
});
const boardRef = useRef(null);
- const [selectedBoardCards, setSelectedBoardCards] = useRecoilState(
- selectedBoardCardIdsState,
- );
-
- function setRowSelectedState(boardCardId: string, selected: boolean) {
- if (selected && !selectedBoardCards.includes(boardCardId)) {
- setSelectedBoardCards([...selectedBoardCards, boardCardId ?? '']);
- } else if (!selected && selectedBoardCards.includes(boardCardId)) {
- setSelectedBoardCards(
- selectedBoardCards.filter((id) => id !== boardCardId),
- );
- }
- }
return (boardColumns?.length ?? 0) > 0 ? (
@@ -144,7 +132,7 @@ export function EntityBoard({
) : (
diff --git a/front/src/modules/ui/board/components/EntityBoardActionBar.tsx b/front/src/modules/ui/board/components/EntityBoardActionBar.tsx
index 749fc9547..4e2ff52f5 100644
--- a/front/src/modules/ui/board/components/EntityBoardActionBar.tsx
+++ b/front/src/modules/ui/board/components/EntityBoardActionBar.tsx
@@ -3,13 +3,13 @@ import { useRecoilValue } from 'recoil';
import { ActionBar } from '@/ui/action-bar/components/ActionBar';
-import { selectedBoardCardIdsState } from '../states/selectedBoardCardIdsState';
+import { selectedCardIdsSelector } from '../states/selectedCardIdsSelector';
type OwnProps = {
children: React.ReactNode | React.ReactNode[];
};
export function EntityBoardActionBar({ children }: OwnProps) {
- const selectedBoardCards = useRecoilValue(selectedBoardCardIdsState);
- return {children};
+ const selectedCardIds = useRecoilValue(selectedCardIdsSelector);
+ return {children};
}
diff --git a/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts b/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts
new file mode 100644
index 000000000..78a17f38d
--- /dev/null
+++ b/front/src/modules/ui/board/hooks/useCurrentCardSelected.ts
@@ -0,0 +1,28 @@
+import { useContext } from 'react';
+import { useRecoilCallback, useRecoilState } from 'recoil';
+
+import { BoardCardIdContext } from '../states/BoardCardIdContext';
+import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
+
+export function useCurrentCardSelected() {
+ const currentCardId = useContext(BoardCardIdContext);
+
+ const [isCardSelected] = useRecoilState(
+ isCardSelectedFamilyState(currentCardId ?? ''),
+ );
+
+ const setCurrentCardSelected = useRecoilCallback(
+ ({ set }) =>
+ (selected: boolean) => {
+ if (!currentCardId) return;
+
+ set(isCardSelectedFamilyState(currentCardId), selected);
+ },
+ [],
+ );
+
+ return {
+ currentCardSelected: isCardSelected,
+ setCurrentCardSelected,
+ };
+}
diff --git a/front/src/modules/ui/board/hooks/useRemoveCardIds.ts b/front/src/modules/ui/board/hooks/useRemoveCardIds.ts
new file mode 100644
index 000000000..871cc887f
--- /dev/null
+++ b/front/src/modules/ui/board/hooks/useRemoveCardIds.ts
@@ -0,0 +1,27 @@
+// Atlassian dnd does not support StrictMode from RN 18, so we use a fork @hello-pangea/dnd https://github.com/atlassian/react-beautiful-dnd/issues/2350
+import { useRecoilCallback } from 'recoil';
+
+import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
+import { boardColumnsState } from '../states/boardColumnsState';
+
+export function useRemoveCardIds() {
+ return useRecoilCallback(
+ ({ snapshot, set }) =>
+ (cardIdToRemove: string[]) => {
+ const boardColumns = snapshot
+ .getLoadable(boardColumnsState)
+ .valueOrThrow();
+
+ boardColumns.forEach((boardColumn) => {
+ const columnCardIds = snapshot
+ .getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
+ .valueOrThrow();
+ set(
+ boardCardIdsByColumnIdFamilyState(boardColumn.id),
+ columnCardIds.filter((cardId) => !cardIdToRemove.includes(cardId)),
+ );
+ });
+ },
+ [],
+ );
+}
diff --git a/front/src/modules/ui/board/hooks/useResetCardSelection.ts b/front/src/modules/ui/board/hooks/useResetCardSelection.ts
new file mode 100644
index 000000000..7bb92cbd6
--- /dev/null
+++ b/front/src/modules/ui/board/hooks/useResetCardSelection.ts
@@ -0,0 +1,27 @@
+import { useRecoilCallback } from 'recoil';
+
+import { boardCardIdsByColumnIdFamilyState } from '../states/boardCardIdsByColumnIdFamilyState';
+import { boardColumnsState } from '../states/boardColumnsState';
+import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
+
+export function useResetCardSelection() {
+ return useRecoilCallback(
+ ({ snapshot, set }) =>
+ () => {
+ const boardColumns = snapshot
+ .getLoadable(boardColumnsState)
+ .valueOrThrow();
+
+ const cardIds = boardColumns.flatMap((boardColumn) =>
+ snapshot
+ .getLoadable(boardCardIdsByColumnIdFamilyState(boardColumn.id))
+ .valueOrThrow(),
+ );
+
+ for (const cardId of cardIds) {
+ set(isCardSelectedFamilyState(cardId), false);
+ }
+ },
+ [],
+ );
+}
diff --git a/front/src/modules/ui/board/hooks/useSetCardSelected.ts b/front/src/modules/ui/board/hooks/useSetCardSelected.ts
new file mode 100644
index 000000000..2bb3763f2
--- /dev/null
+++ b/front/src/modules/ui/board/hooks/useSetCardSelected.ts
@@ -0,0 +1,9 @@
+import { useRecoilCallback } from 'recoil';
+
+import { isCardSelectedFamilyState } from '../states/isCardSelectedFamilyState';
+
+export function useSetCardSelected() {
+ return useRecoilCallback(({ set }) => (cardId: string, selected: boolean) => {
+ set(isCardSelectedFamilyState(cardId), selected);
+ });
+}
diff --git a/front/src/modules/ui/board/states/isCardSelectedFamilyState.ts b/front/src/modules/ui/board/states/isCardSelectedFamilyState.ts
new file mode 100644
index 000000000..a6402a88e
--- /dev/null
+++ b/front/src/modules/ui/board/states/isCardSelectedFamilyState.ts
@@ -0,0 +1,6 @@
+import { atomFamily } from 'recoil';
+
+export const isCardSelectedFamilyState = atomFamily({
+ key: 'isCardSelectedFamilyState',
+ default: false,
+});
diff --git a/front/src/modules/ui/board/states/selectedBoardCardIdsState.ts b/front/src/modules/ui/board/states/selectedBoardCardIdsState.ts
deleted file mode 100644
index bb2beee46..000000000
--- a/front/src/modules/ui/board/states/selectedBoardCardIdsState.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { atom } from 'recoil';
-
-export const selectedBoardCardIdsState = atom({
- key: 'selectedBoardCardIdsState',
- default: [],
-});
diff --git a/front/src/modules/ui/board/states/selectedCardIdsSelector.ts b/front/src/modules/ui/board/states/selectedCardIdsSelector.ts
new file mode 100644
index 000000000..ff604d6ef
--- /dev/null
+++ b/front/src/modules/ui/board/states/selectedCardIdsSelector.ts
@@ -0,0 +1,22 @@
+import { selector } from 'recoil';
+
+import { boardCardIdsByColumnIdFamilyState } from './boardCardIdsByColumnIdFamilyState';
+import { boardColumnsState } from './boardColumnsState';
+import { isCardSelectedFamilyState } from './isCardSelectedFamilyState';
+
+export const selectedCardIdsSelector = selector({
+ key: 'selectedCardIdsSelector',
+ get: ({ get }) => {
+ const boardColumns = get(boardColumnsState);
+
+ const cardIds = boardColumns.flatMap((boardColumn) =>
+ get(boardCardIdsByColumnIdFamilyState(boardColumn.id)),
+ );
+
+ const selectedCardIds = cardIds.filter(
+ (cardId) => get(isCardSelectedFamilyState(cardId)) === true,
+ );
+
+ return selectedCardIds;
+ },
+});
diff --git a/front/src/modules/ui/filter-n-sort/components/DropdownMenuContainer.tsx b/front/src/modules/ui/filter-n-sort/components/DropdownMenuContainer.tsx
index 430c73959..b1f01e1f6 100644
--- a/front/src/modules/ui/filter-n-sort/components/DropdownMenuContainer.tsx
+++ b/front/src/modules/ui/filter-n-sort/components/DropdownMenuContainer.tsx
@@ -28,7 +28,7 @@ export function DropdownMenuContainer({
});
return (
-
+
{children}
);
diff --git a/front/src/modules/ui/link/components/SocialLink.tsx b/front/src/modules/ui/link/components/SocialLink.tsx
index dd4acb392..04603b314 100644
--- a/front/src/modules/ui/link/components/SocialLink.tsx
+++ b/front/src/modules/ui/link/components/SocialLink.tsx
@@ -30,16 +30,25 @@ export function SocialLink({ children, href, onClick, type }: OwnProps) {
let displayValue = children;
if (type === 'linkedin') {
- const splitUrl = href.split('/');
- const splitName = splitUrl[4].split('-');
- displayValue = splitName[2]
- ? `${splitName[0]}-${splitName[1]}`
- : splitName[0];
+ const matches = href.match(
+ /(?:https?:\/\/)?(?:www.)?linkedin.com\/(?:in|company)\/([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
+ );
+ if (matches && matches[1]) {
+ displayValue = matches[1];
+ } else {
+ displayValue = 'LinkedIn';
+ }
}
if (type === 'twitter') {
- const splitUrl = href.split('/');
- displayValue = `@${splitUrl[3]}`;
+ const matches = href.match(
+ /(?:https?:\/\/)?(?:www.)?twitter.com\/([-a-zA-Z0-9@:%_+.~#?&//=]*)/,
+ );
+ if (matches && matches[1]) {
+ displayValue = `@${matches[1]}`;
+ } else {
+ displayValue = '@twitter';
+ }
}
return (
diff --git a/front/src/modules/ui/table/editable-cell/hooks/useEditableCell.ts b/front/src/modules/ui/table/editable-cell/hooks/useEditableCell.ts
index 8a514f1a8..8da2ee7a5 100644
--- a/front/src/modules/ui/table/editable-cell/hooks/useEditableCell.ts
+++ b/front/src/modules/ui/table/editable-cell/hooks/useEditableCell.ts
@@ -1,5 +1,6 @@
import { useContext } from 'react';
+import { useDragSelect } from '@/ui/utilities/drag-select/hooks/useDragSelect';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import { HotkeyScope } from '@/ui/utilities/hotkey/types/HotkeyScope';
@@ -17,17 +18,20 @@ export function useEditableCell() {
const { setCurrentCellInEditMode } = useCurrentCellEditMode();
const setHotkeyScope = useSetHotkeyScope();
+ const { setDragSelectionStartEnabled } = useDragSelect();
const closeCurrentCellInEditMode = useCloseCurrentCellInEditMode();
const customCellHotkeyScope = useContext(CellHotkeyScopeContext);
function closeEditableCell() {
+ setDragSelectionStartEnabled(true);
closeCurrentCellInEditMode();
setHotkeyScope(TableHotkeyScope.TableSoftFocus);
}
function openEditableCell() {
+ setDragSelectionStartEnabled(false);
setCurrentCellInEditMode();
if (customCellHotkeyScope) {
diff --git a/front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx b/front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx
index b0b7c499e..e7dad4f1f 100644
--- a/front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx
+++ b/front/src/modules/ui/utilities/drag-select/components/DragSelect.tsx
@@ -4,6 +4,8 @@ import {
useSelectionContainer,
} from '@air/react-drag-to-select';
+import { useDragSelect } from '../hooks/useDragSelect';
+
type OwnProps = {
dragSelectable: RefObject;
onDragSelectionChange: (id: string, selected: boolean) => void;
@@ -15,8 +17,12 @@ export function DragSelect({
onDragSelectionChange,
onDragSelectionStart,
}: OwnProps) {
+ const { isDragSelectionStartEnabled } = useDragSelect();
const { DragSelection } = useSelectionContainer({
shouldStartSelecting: (target) => {
+ if (!isDragSelectionStartEnabled()) {
+ return false;
+ }
if (target instanceof HTMLElement) {
let el = target;
while (el.parentElement && !el.dataset.selectDisable) {
diff --git a/front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts b/front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts
new file mode 100644
index 000000000..6fd600163
--- /dev/null
+++ b/front/src/modules/ui/utilities/drag-select/hooks/useDragSelect.ts
@@ -0,0 +1,28 @@
+import { useRecoilCallback, useRecoilState } from 'recoil';
+
+import { isDragSelectionStartEnabledState } from '../states/internal/isDragSelectionStartEnabledState';
+
+export function useDragSelect() {
+ const [, setIsDragSelectionStartEnabledInternal] = useRecoilState(
+ isDragSelectionStartEnabledState,
+ );
+
+ function setDragSelectionStartEnabled(isEnabled: boolean) {
+ setIsDragSelectionStartEnabledInternal(isEnabled);
+ }
+
+ const isDragSelectionStartEnabled = useRecoilCallback(
+ ({ snapshot }) =>
+ () => {
+ return snapshot
+ .getLoadable(isDragSelectionStartEnabledState)
+ .getValue();
+ },
+ [],
+ );
+
+ return {
+ isDragSelectionStartEnabled,
+ setDragSelectionStartEnabled,
+ };
+}
diff --git a/front/src/modules/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState.ts b/front/src/modules/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState.ts
new file mode 100644
index 000000000..a8fb73232
--- /dev/null
+++ b/front/src/modules/ui/utilities/drag-select/states/internal/isDragSelectionStartEnabledState.ts
@@ -0,0 +1,6 @@
+import { atom } from 'recoil';
+
+export const isDragSelectionStartEnabledState = atom({
+ key: 'drag-select/isDragSelectionStartEnabledState',
+ default: true,
+});
diff --git a/front/src/pages/opportunities/Opportunities.tsx b/front/src/pages/opportunities/Opportunities.tsx
index 4cf3efbb5..c6c599cb3 100644
--- a/front/src/pages/opportunities/Opportunities.tsx
+++ b/front/src/pages/opportunities/Opportunities.tsx
@@ -1,12 +1,10 @@
import { useCallback, useState } from 'react';
-import { getOperationName } from '@apollo/client/utilities';
import { useTheme } from '@emotion/react';
import { HooksCompanyBoard } from '@/companies/components/HooksCompanyBoard';
import { CompanyBoardContext } from '@/companies/states/CompanyBoardContext';
import {
defaultPipelineProgressOrderBy,
- GET_PIPELINES,
PipelineProgressesSelectedSortType,
} from '@/pipeline/queries';
import { BoardActionBarButtonDeleteBoardCard } from '@/ui/board/components/BoardActionBarButtonDeleteBoardCard';
@@ -14,13 +12,11 @@ import { EntityBoard } from '@/ui/board/components/EntityBoard';
import { EntityBoardActionBar } from '@/ui/board/components/EntityBoardActionBar';
import { BoardOptionsContext } from '@/ui/board/states/BoardOptionsContext';
import { reduceSortsToOrderBy } from '@/ui/filter-n-sort/helpers';
-import { AvailableFiltersContext } from '@/ui/filter-n-sort/states/AvailableFiltersContext';
import { IconTargetArrow } from '@/ui/icon/index';
import { WithTopBarContainer } from '@/ui/layout/components/WithTopBarContainer';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import {
PipelineProgressOrderByWithRelationInput,
- useDeleteManyPipelineProgressMutation,
useUpdatePipelineStageMutation,
} from '~/generated/graphql';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
@@ -67,18 +63,6 @@ export function Opportunities() {
});
}
- const [deletePipelineProgress] = useDeleteManyPipelineProgressMutation({
- refetchQueries: [getOperationName(GET_PIPELINES) ?? ''],
- });
-
- async function handleDelete(cardIdsToDelete: string[]) {
- await deletePipelineProgress({
- variables: {
- ids: cardIdsToDelete,
- },
- });
- }
-
return (
-
-
-
-
-
-
-
+
+
+
+
+
diff --git a/front/src/sync-hooks/CommandMenuHook.tsx b/front/src/sync-hooks/CommandMenuHook.tsx
index 42860ae3c..b056093ed 100644
--- a/front/src/sync-hooks/CommandMenuHook.tsx
+++ b/front/src/sync-hooks/CommandMenuHook.tsx
@@ -1,3 +1,4 @@
+import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { commandMenuCommands } from '@/command-menu/constants/commandMenuCommands';
@@ -7,7 +8,9 @@ export function CommandMenuHook() {
const setCommands = useSetRecoilState(commandMenuCommand);
const commands = commandMenuCommands;
- setCommands(commands);
+ useEffect(() => {
+ setCommands(commands);
+ }, [commands, setCommands]);
return <>>;
}
diff --git a/front/src/utils/__tests__/is-url.test.ts b/front/src/utils/__tests__/is-url.test.ts
index a5ec5081c..69b106f87 100644
--- a/front/src/utils/__tests__/is-url.test.ts
+++ b/front/src/utils/__tests__/is-url.test.ts
@@ -37,7 +37,11 @@ describe('isURL', () => {
expect(isURL('https://2.com/test/')).toBeTruthy();
});
- it(`should return false if string https://2.com/test/sldkfj!?`, () => {
- expect(isURL('https://2.com/test/sldkfj!?')).toBeFalsy();
+ it(`should return true if string is https://www.linkedin.com/company/b%C3%B6ke-&-partner-sdft-partmbb/`, () => {
+ expect(
+ isURL(
+ 'https://www.linkedin.com/company/b%C3%B6ke-&-partner-sdft-partmbb/',
+ ),
+ ).toBeTruthy();
});
});
diff --git a/front/src/utils/is-url.ts b/front/src/utils/is-url.ts
index c43b41870..0b9fe22e9 100644
--- a/front/src/utils/is-url.ts
+++ b/front/src/utils/is-url.ts
@@ -1,14 +1,10 @@
import { isDefined } from './isDefined';
export function isURL(url: string | undefined | null) {
- const pattern = new RegExp(
- '^(https?:\\/\\/)?' +
- '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' +
- '((\\d{1,3}\\.){3}\\d{1,3}))' +
- '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' +
- '(\\?[;&a-z\\d%_.~+=-]*)?' +
- '(\\#[-a-z\\d_]*)?$',
- 'i',
+ return (
+ isDefined(url) &&
+ url.match(
+ /^(https?:\/\/)?(www.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-z]{2,4}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/i,
+ )
);
- return isDefined(url) && !!pattern.test(url);
}