Removing Prisma and Grapql-nestjs-prisma resolvers (#2574)
* Some cleaning * Fix seeds * Fix all sign in, sign up flow and apiKey optimistic rendering * Fix
This commit is contained in:
@ -1,74 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
import { CompanyBoardCard } from '@/companies/components/CompanyBoardCard';
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
import { BoardCardIdContext } from '@/ui/layout/board/contexts/BoardCardIdContext';
|
||||
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
|
||||
import { BoardColumnRecoilScopeContext } from '@/ui/layout/board/states/recoil-scope-contexts/BoardColumnRecoilScopeContext';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';
|
||||
import { ComponentWithRecoilScopeDecorator } from '~/testing/decorators/ComponentWithRecoilScopeDecorator';
|
||||
import { graphqlMocks } from '~/testing/graphqlMocks';
|
||||
import { mockedPipelineProgressData } from '~/testing/mock-data/pipeline-progress';
|
||||
|
||||
import { HooksCompanyBoardEffect } from '../components/HooksCompanyBoardEffect';
|
||||
import { BoardContext } from '../states/contexts/BoardContext';
|
||||
import { CompanyBoardRecoilScopeContext } from '../states/recoil-scope-contexts/CompanyBoardRecoilScopeContext';
|
||||
|
||||
const meta: Meta<typeof CompanyBoardCard> = {
|
||||
title: 'Modules/Companies/CompanyBoardCard',
|
||||
component: CompanyBoardCard,
|
||||
decorators: [
|
||||
(Story, context) => {
|
||||
const [, setBoardCardFields] = useRecoilScopedState(
|
||||
boardCardFieldsScopedState,
|
||||
context.parameters.customRecoilScopeContext,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setBoardCardFields(pipelineAvailableFieldDefinitions);
|
||||
}, [setBoardCardFields]);
|
||||
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<ViewScope viewScopeId="company-board-view">
|
||||
<RecoilScope
|
||||
CustomRecoilScopeContext={BoardColumnRecoilScopeContext}
|
||||
>
|
||||
<BoardContext.Provider
|
||||
value={{
|
||||
BoardRecoilScopeContext:
|
||||
context.parameters.customRecoilScopeContext,
|
||||
}}
|
||||
>
|
||||
<HooksCompanyBoardEffect />
|
||||
<BoardCardIdContext.Provider
|
||||
value={mockedPipelineProgressData[1].id}
|
||||
>
|
||||
<Story />
|
||||
</BoardCardIdContext.Provider>
|
||||
</BoardContext.Provider>
|
||||
</RecoilScope>
|
||||
</ViewScope>
|
||||
</MemoryRouter>
|
||||
);
|
||||
},
|
||||
ComponentWithRecoilScopeDecorator,
|
||||
ComponentDecorator,
|
||||
],
|
||||
args: {},
|
||||
argTypes: {},
|
||||
parameters: {
|
||||
msw: graphqlMocks,
|
||||
customRecoilScopeContext: CompanyBoardRecoilScopeContext,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof CompanyBoardCard>;
|
||||
|
||||
export const Default: Story = {};
|
||||
@ -1,38 +1,34 @@
|
||||
import { Pipeline } from '~/generated/graphql';
|
||||
import { PipelineStep } from '@/pipeline/types/PipelineStep';
|
||||
|
||||
export const pipeline = {
|
||||
id: 'pipeline-1',
|
||||
name: 'pipeline-1',
|
||||
pipelineStages: [
|
||||
{
|
||||
id: 'pipeline-stage-1',
|
||||
name: 'New',
|
||||
position: 0,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-2',
|
||||
name: 'Screening',
|
||||
position: 1,
|
||||
color: 'purple',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-3',
|
||||
name: 'Meeting',
|
||||
position: 2,
|
||||
color: 'sky',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-4',
|
||||
name: 'Proposal',
|
||||
position: 3,
|
||||
color: 'turquoise',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-5',
|
||||
name: 'Customer',
|
||||
position: 4,
|
||||
color: 'yellow',
|
||||
},
|
||||
],
|
||||
} as Pipeline;
|
||||
export const pipelineSteps = [
|
||||
{
|
||||
id: 'pipeline-stage-1',
|
||||
name: 'New',
|
||||
position: 0,
|
||||
color: 'red',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-2',
|
||||
name: 'Screening',
|
||||
position: 1,
|
||||
color: 'purple',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-3',
|
||||
name: 'Meeting',
|
||||
position: 2,
|
||||
color: 'sky',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-4',
|
||||
name: 'Proposal',
|
||||
position: 3,
|
||||
color: 'turquoise',
|
||||
},
|
||||
{
|
||||
id: 'pipeline-stage-5',
|
||||
name: 'Customer',
|
||||
position: 4,
|
||||
color: 'yellow',
|
||||
},
|
||||
] as PipelineStep[];
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { useState } from 'react';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import styled from '@emotion/styled';
|
||||
import { flip, offset, useFloating } from '@floating-ui/react';
|
||||
import { v4 } from 'uuid';
|
||||
@ -8,7 +7,6 @@ import {
|
||||
PeoplePicker,
|
||||
PersonForSelect,
|
||||
} from '@/people/components/PeoplePicker';
|
||||
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
|
||||
import { IconPlus } from '@/ui/display/icon';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
|
||||
@ -16,10 +14,6 @@ import { DoubleTextInput } from '@/ui/object/field/meta-types/input/components/i
|
||||
import { FieldDoubleText } from '@/ui/object/field/types/FieldDoubleText';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import {
|
||||
useInsertOnePersonMutation,
|
||||
useUpdateOnePersonMutation,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
position: static;
|
||||
@ -56,8 +50,6 @@ export const AddPersonToCompany = ({
|
||||
}) => {
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
const [isCreationDropdownOpen, setIsCreationDropdownOpen] = useState(false);
|
||||
const [updatePerson] = useUpdateOnePersonMutation();
|
||||
const [insertOnePerson] = useInsertOnePersonMutation();
|
||||
const { refs, floatingStyles } = useFloating({
|
||||
open: isDropdownOpen,
|
||||
placement: 'right-start',
|
||||
@ -77,17 +69,17 @@ export const AddPersonToCompany = ({
|
||||
const handlePersonSelected =
|
||||
(companyId: string) => async (newPerson: PersonForSelect | null) => {
|
||||
if (newPerson) {
|
||||
await updatePerson({
|
||||
variables: {
|
||||
where: {
|
||||
id: newPerson.id,
|
||||
},
|
||||
data: {
|
||||
company: { connect: { id: companyId } },
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
|
||||
});
|
||||
// await updatePerson({
|
||||
// variables: {
|
||||
// where: {
|
||||
// id: newPerson.id,
|
||||
// },
|
||||
// data: {
|
||||
// company: { connect: { id: companyId } },
|
||||
// },
|
||||
// },
|
||||
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
|
||||
// });
|
||||
handleClosePicker();
|
||||
}
|
||||
};
|
||||
@ -114,17 +106,17 @@ export const AddPersonToCompany = ({
|
||||
}: FieldDoubleText) => {
|
||||
if (!firstValue && !secondValue) return;
|
||||
const newPersonId = v4();
|
||||
await insertOnePerson({
|
||||
variables: {
|
||||
data: {
|
||||
company: { connect: { id: companyId } },
|
||||
id: newPersonId,
|
||||
firstName: firstValue,
|
||||
lastName: secondValue,
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
|
||||
});
|
||||
// await insertOnePerson({
|
||||
// variables: {
|
||||
// data: {
|
||||
// company: { connect: { id: companyId } },
|
||||
// id: newPersonId,
|
||||
// firstName: firstValue,
|
||||
// lastName: secondValue,
|
||||
// },
|
||||
// },
|
||||
// refetchQueries: [getOperationName(GET_PEOPLE) ?? ''],
|
||||
// });
|
||||
setIsCreationDropdownOpen(false);
|
||||
};
|
||||
|
||||
|
||||
@ -152,7 +152,7 @@ export const CompanyBoardCard = () => {
|
||||
|
||||
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
|
||||
const { updateOneObject } = useUpdateOneObjectRecord({
|
||||
objectNameSingular: 'opportunityV2',
|
||||
objectNameSingular: 'opportunity',
|
||||
});
|
||||
|
||||
const updateEntity = ({
|
||||
@ -242,7 +242,7 @@ export const CompanyBoardCard = () => {
|
||||
fieldDefinition: {
|
||||
fieldMetadataId: viewField.fieldMetadataId,
|
||||
label: viewField.label,
|
||||
Icon: viewField.Icon,
|
||||
iconName: viewField.iconName,
|
||||
type: viewField.type,
|
||||
metadata: viewField.metadata,
|
||||
entityChipDisplayMapper:
|
||||
|
||||
@ -18,7 +18,7 @@ export const CompanyChip = ({
|
||||
}: CompanyChipProps) => (
|
||||
<EntityChip
|
||||
entityId={id}
|
||||
linkToEntity={`/companies/${id}`}
|
||||
linkToEntity={`/objects/companies/${id}`}
|
||||
name={name}
|
||||
avatarType="squared"
|
||||
pictureUrl={pictureUrl}
|
||||
|
||||
@ -33,7 +33,7 @@ export const CompanyPicker = ({
|
||||
}, [initialSearchFilter, setRelationPickerSearchFilter]);
|
||||
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNamePlural: 'companiesV2',
|
||||
objectNamePlural: 'companies',
|
||||
});
|
||||
|
||||
const useFindManyCompanies = (options: any) =>
|
||||
@ -57,7 +57,7 @@ export const CompanyPicker = ({
|
||||
originalEntity: company,
|
||||
}),
|
||||
selectedIds: companyId ? [companyId] : [],
|
||||
objectNamePlural: 'companiesV2',
|
||||
objectNamePlural: 'companies',
|
||||
});
|
||||
|
||||
const handleEntitySelected = async (
|
||||
|
||||
@ -73,7 +73,7 @@ export const CompanyProgressPicker = ({
|
||||
const selectedPipelineStage = useMemo(
|
||||
() =>
|
||||
currentPipelineStages.find(
|
||||
(pipelineStage) => pipelineStage.id === selectedPipelineStageId,
|
||||
(pipelineStage: any) => pipelineStage.id === selectedPipelineStageId,
|
||||
),
|
||||
[currentPipelineStages, selectedPipelineStageId],
|
||||
);
|
||||
@ -85,7 +85,7 @@ export const CompanyProgressPicker = ({
|
||||
>
|
||||
{isProgressSelectionUnfolded ? (
|
||||
<DropdownMenuItemsContainer>
|
||||
{currentPipelineStages.map((pipelineStage, index) => (
|
||||
{currentPipelineStages.map((pipelineStage: any, index: number) => (
|
||||
<MenuItem
|
||||
key={pipelineStage.id}
|
||||
testId={`select-pipeline-stage-${index}`}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { PeopleCard } from '@/people/components/PeopleCard';
|
||||
import { Company, useGetPeopleQuery } from '~/generated/graphql';
|
||||
import { Person } from '@/people/types/Person';
|
||||
|
||||
import { AddPersonToCompany } from './AddPersonToCompany';
|
||||
|
||||
@ -43,16 +44,19 @@ const StyledTitle = styled.div`
|
||||
`;
|
||||
|
||||
export const CompanyTeam = ({ company }: CompanyTeamProps) => {
|
||||
const { data } = useGetPeopleQuery({
|
||||
variables: {
|
||||
orderBy: [],
|
||||
where: {
|
||||
companyId: {
|
||||
equals: company.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// const { data } = useGetPeopleQuery({
|
||||
// variables: {
|
||||
// orderBy: [],
|
||||
// where: {
|
||||
// companyId: {
|
||||
// equals: company.id,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
const data = {
|
||||
people: [],
|
||||
};
|
||||
|
||||
const peopleIds = data?.people?.map(({ id }) => id);
|
||||
|
||||
@ -65,7 +69,7 @@ export const CompanyTeam = ({ company }: CompanyTeamProps) => {
|
||||
<AddPersonToCompany companyId={company.id} peopleIds={peopleIds} />
|
||||
</StyledTitleContainer>
|
||||
<StyledListContainer>
|
||||
{data?.people?.map((person, id) => (
|
||||
{data?.people?.map((person: Person, id) => (
|
||||
<PeopleCard
|
||||
key={person.id}
|
||||
person={person}
|
||||
|
||||
@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
|
||||
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
|
||||
import { pipelineAvailableFieldDefinitions } from '@/pipeline/constants/pipelineAvailableFieldDefinitions';
|
||||
@ -13,13 +14,11 @@ import { useBoardContextMenuEntries } from '@/ui/layout/board/hooks/useBoardCont
|
||||
import { availableBoardCardFieldsScopedState } from '@/ui/layout/board/states/availableBoardCardFieldsScopedState';
|
||||
import { boardCardFieldsScopedState } from '@/ui/layout/board/states/boardCardFieldsScopedState';
|
||||
import { isBoardLoadedState } from '@/ui/layout/board/states/isBoardLoadedState';
|
||||
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { mapViewFieldsToBoardFieldDefinitions } from '@/views/utils/mapViewFieldsToBoardFieldDefinitions';
|
||||
import { Company } from '~/generated-metadata/graphql';
|
||||
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
|
||||
|
||||
import { useUpdateCompanyBoardCardIds } from '../hooks/useUpdateBoardCardIds';
|
||||
@ -62,7 +61,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
const updateCompanyBoard = useUpdateCompanyBoard();
|
||||
|
||||
useFindManyObjectRecords({
|
||||
objectNamePlural: 'pipelineStepsV2',
|
||||
objectNamePlural: 'pipelineSteps',
|
||||
filter: {},
|
||||
onCompleted: useCallback(
|
||||
(data: PaginatedObjectTypeResults<PipelineStep>) => {
|
||||
@ -80,14 +79,14 @@ export const HooksCompanyBoardEffect = () => {
|
||||
in: pipelineSteps.map((pipelineStep) => pipelineStep.id),
|
||||
},
|
||||
},
|
||||
...(currentViewFilters?.map(turnFilterIntoWhereClause) || []),
|
||||
...[],
|
||||
],
|
||||
};
|
||||
}, [currentViewFilters, pipelineSteps]) as any;
|
||||
}, [pipelineSteps]) as any;
|
||||
|
||||
useFindManyObjectRecords({
|
||||
skip: !pipelineSteps.length,
|
||||
objectNamePlural: 'opportunitiesV2',
|
||||
objectNamePlural: 'opportunities',
|
||||
filter: whereFilters,
|
||||
onCompleted: useCallback(
|
||||
(_data: PaginatedObjectTypeResults<Opportunity>) => {
|
||||
@ -104,7 +103,7 @@ export const HooksCompanyBoardEffect = () => {
|
||||
|
||||
useFindManyObjectRecords({
|
||||
skip: !opportunities.length,
|
||||
objectNamePlural: 'companiesV2',
|
||||
objectNamePlural: 'companies',
|
||||
filter: {
|
||||
id: {
|
||||
in: opportunities.map((opportuntiy) => opportuntiy.companyId || ''),
|
||||
|
||||
@ -9,7 +9,6 @@ import { BoardColumnContext } from '@/ui/layout/board/contexts/BoardColumnContex
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
|
||||
|
||||
import { useCreateCompanyProgress } from '../hooks/useCreateCompanyProgress';
|
||||
import { useFilteredSearchCompanyQuery } from '../hooks/useFilteredSearchCompanyQuery';
|
||||
|
||||
export const NewCompanyProgressButton = () => {
|
||||
@ -25,8 +24,6 @@ export const NewCompanyProgressButton = () => {
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
const createCompanyProgress = useCreateCompanyProgress();
|
||||
|
||||
const handleEntitySelect = (company: any) => {
|
||||
setIsCreatingCard(false);
|
||||
goBackToPreviousHotkeyScope();
|
||||
@ -39,7 +36,7 @@ export const NewCompanyProgressButton = () => {
|
||||
throw new Error('Pipeline stage id is not defined');
|
||||
}
|
||||
|
||||
createCompanyProgress(company.id, pipelineStageId);
|
||||
//createCompanyProgress(company.id, pipelineStageId);
|
||||
};
|
||||
|
||||
const handleNewClick = useCallback(() => {
|
||||
|
||||
@ -1,182 +0,0 @@
|
||||
import {
|
||||
IconBrandLinkedin,
|
||||
IconBrandX,
|
||||
IconBuildingSkyscraper,
|
||||
IconCalendarEvent,
|
||||
IconLink,
|
||||
IconMap,
|
||||
IconMoneybag,
|
||||
IconTarget,
|
||||
IconUserCircle,
|
||||
IconUsers,
|
||||
} from '@/ui/display/icon/index';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
import {
|
||||
FieldBooleanMetadata,
|
||||
FieldChipMetadata,
|
||||
FieldDateMetadata,
|
||||
FieldMetadata,
|
||||
FieldMoneyMetadata,
|
||||
FieldNumberMetadata,
|
||||
FieldRelationMetadata,
|
||||
FieldTextMetadata,
|
||||
FieldURLMetadata,
|
||||
} from '@/ui/object/field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
|
||||
import { User } from '~/generated/graphql';
|
||||
|
||||
export const companiesAvailableFieldDefinitions: ColumnDefinition<FieldMetadata>[] =
|
||||
[
|
||||
{
|
||||
fieldMetadataId: 'name',
|
||||
label: 'Name',
|
||||
Icon: IconBuildingSkyscraper,
|
||||
size: 180,
|
||||
position: 0,
|
||||
type: 'CHIP',
|
||||
metadata: {
|
||||
urlFieldName: 'domainName',
|
||||
contentFieldName: 'name',
|
||||
relationType: Entity.Company,
|
||||
placeHolder: 'Company Name',
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'The company name.',
|
||||
basePathToShowPage: '/companies/',
|
||||
} satisfies ColumnDefinition<FieldChipMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'domainName',
|
||||
label: 'URL',
|
||||
Icon: IconLink,
|
||||
size: 100,
|
||||
position: 1,
|
||||
type: 'URL',
|
||||
metadata: {
|
||||
fieldName: 'domainName',
|
||||
placeHolder: 'example.com',
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
'The company website URL. We use this url to fetch the company icon.',
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'accountOwner',
|
||||
label: 'Account Owner',
|
||||
Icon: IconUserCircle,
|
||||
size: 150,
|
||||
position: 2,
|
||||
type: 'RELATION',
|
||||
metadata: {
|
||||
fieldName: 'accountOwner',
|
||||
relationType: Entity.User,
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent:
|
||||
'Your team member responsible for managing the company account.',
|
||||
entityChipDisplayMapper: (dataObject: User) => {
|
||||
return {
|
||||
name: dataObject?.displayName,
|
||||
pictureUrl: dataObject?.avatarUrl ?? undefined,
|
||||
avatarType: 'rounded',
|
||||
};
|
||||
},
|
||||
} satisfies ColumnDefinition<FieldRelationMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'createdAt',
|
||||
label: 'Creation',
|
||||
Icon: IconCalendarEvent,
|
||||
size: 150,
|
||||
position: 3,
|
||||
type: 'DATE',
|
||||
metadata: {
|
||||
fieldName: 'createdAt',
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent: "Date when the company's record was created.",
|
||||
} satisfies ColumnDefinition<FieldDateMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'employees',
|
||||
label: 'Employees',
|
||||
Icon: IconUsers,
|
||||
size: 150,
|
||||
position: 4,
|
||||
type: 'NUMBER',
|
||||
metadata: {
|
||||
fieldName: 'employees',
|
||||
isPositive: true,
|
||||
placeHolder: 'Employees',
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'Number of employees in the company.',
|
||||
} satisfies ColumnDefinition<FieldNumberMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'linkedin',
|
||||
label: 'LinkedIn',
|
||||
Icon: IconBrandLinkedin,
|
||||
size: 170,
|
||||
position: 5,
|
||||
type: 'URL',
|
||||
metadata: {
|
||||
fieldName: 'linkedinUrl',
|
||||
placeHolder: 'LinkedIn URL',
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'The company Linkedin account.',
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'address',
|
||||
label: 'Address',
|
||||
Icon: IconMap,
|
||||
size: 170,
|
||||
position: 6,
|
||||
type: 'TEXT',
|
||||
metadata: {
|
||||
fieldName: 'address',
|
||||
placeHolder: 'Address', // Hack: Fake character to prevent password-manager from filling the field
|
||||
},
|
||||
isVisible: true,
|
||||
infoTooltipContent: 'The company address.',
|
||||
} satisfies ColumnDefinition<FieldTextMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'idealCustomerProfile',
|
||||
label: 'ICP',
|
||||
Icon: IconTarget,
|
||||
size: 150,
|
||||
position: 7,
|
||||
type: 'BOOLEAN',
|
||||
metadata: {
|
||||
fieldName: 'idealCustomerProfile',
|
||||
},
|
||||
isVisible: false,
|
||||
infoTooltipContent:
|
||||
'Ideal Customer Profile: Indicates whether the company is the most suitable and valuable customer for you.',
|
||||
} satisfies ColumnDefinition<FieldBooleanMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'annualRecurringRevenue',
|
||||
label: 'ARR',
|
||||
Icon: IconMoneybag,
|
||||
size: 150,
|
||||
position: 8,
|
||||
type: 'MONEY_AMOUNT',
|
||||
metadata: {
|
||||
fieldName: 'annualRecurringRevenue',
|
||||
placeHolder: 'ARR',
|
||||
},
|
||||
infoTooltipContent:
|
||||
'Annual Recurring Revenue: The actual or estimated annual revenue of the company.',
|
||||
} satisfies ColumnDefinition<FieldMoneyMetadata>,
|
||||
{
|
||||
fieldMetadataId: 'xUrl',
|
||||
label: 'Twitter',
|
||||
Icon: IconBrandX,
|
||||
size: 150,
|
||||
position: 9,
|
||||
type: 'URL',
|
||||
metadata: {
|
||||
fieldName: 'xUrl',
|
||||
placeHolder: 'X',
|
||||
},
|
||||
isVisible: false,
|
||||
infoTooltipContent: 'The company Twitter account.',
|
||||
} satisfies ColumnDefinition<FieldURLMetadata>,
|
||||
];
|
||||
@ -1,90 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
|
||||
import { usePreviousHotkeyScope } from '@/ui/utilities/hotkey/hooks/usePreviousHotkeyScope';
|
||||
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
|
||||
import { Company, useUpdateOneCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
import { EditableFieldHotkeyScope } from '../types/EditableFieldHotkeyScope';
|
||||
|
||||
type CompanyNameEditableFieldProps = {
|
||||
company: Pick<Company, 'id' | 'name'>;
|
||||
};
|
||||
|
||||
const StyledEditableTitleInput = styled.input<{
|
||||
value: string;
|
||||
}>`
|
||||
background: transparent;
|
||||
|
||||
border: none;
|
||||
color: ${({ theme, value }) =>
|
||||
value ? theme.font.color.primary : theme.font.color.light};
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
font-size: ${({ theme }) => theme.font.size.xl};
|
||||
font-weight: ${({ theme }) => theme.font.weight.semiBold};
|
||||
justify-content: center;
|
||||
|
||||
line-height: ${({ theme }) => theme.text.lineHeight.md};
|
||||
outline: none;
|
||||
&::placeholder {
|
||||
color: ${({ theme }) => theme.font.color.light};
|
||||
}
|
||||
text-align: center;
|
||||
width: calc(100% - ${({ theme }) => theme.spacing(2)});
|
||||
`;
|
||||
|
||||
export const CompanyNameEditableField = ({
|
||||
company,
|
||||
}: CompanyNameEditableFieldProps) => {
|
||||
const [internalValue, setInternalValue] = useState(company.name);
|
||||
|
||||
const [updateCompany] = useUpdateOneCompanyMutation();
|
||||
|
||||
const {
|
||||
goBackToPreviousHotkeyScope,
|
||||
setHotkeyScopeAndMemorizePreviousScope,
|
||||
} = usePreviousHotkeyScope();
|
||||
|
||||
useEffect(() => {
|
||||
setInternalValue(company.name);
|
||||
}, [company.name]);
|
||||
|
||||
const handleChange = async (newValue: string) => {
|
||||
setInternalValue(newValue);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
goBackToPreviousHotkeyScope();
|
||||
await updateCompany({
|
||||
variables: {
|
||||
where: {
|
||||
id: company.id,
|
||||
},
|
||||
data: {
|
||||
name: internalValue ?? '',
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleFocus = async () => {
|
||||
setHotkeyScopeAndMemorizePreviousScope(
|
||||
EditableFieldHotkeyScope.EditableField,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<RecoilScope CustomRecoilScopeContext={FieldRecoilScopeContext}>
|
||||
<StyledEditableTitleInput
|
||||
autoComplete="off"
|
||||
onChange={(event) => handleChange(event.target.value)}
|
||||
onBlur={handleSubmit}
|
||||
onFocus={handleFocus}
|
||||
value={internalValue}
|
||||
/>
|
||||
</RecoilScope>
|
||||
);
|
||||
};
|
||||
@ -1,39 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const BASE_COMPANY_FIELDS_FRAGMENT = gql`
|
||||
fragment baseCompanyFieldsFragment on Company {
|
||||
address
|
||||
annualRecurringRevenue
|
||||
createdAt
|
||||
domainName
|
||||
employees
|
||||
id
|
||||
idealCustomerProfile
|
||||
linkedinUrl
|
||||
name
|
||||
xUrl
|
||||
_activityCount
|
||||
}
|
||||
`;
|
||||
|
||||
export const BASE_ACCOUNT_OWNER_FRAGMENT = gql`
|
||||
fragment baseAccountOwnerFragment on User {
|
||||
id
|
||||
email
|
||||
displayName
|
||||
avatarUrl
|
||||
firstName
|
||||
lastName
|
||||
}
|
||||
`;
|
||||
|
||||
export const COMPANY_FIELDS_FRAGMENT = gql`
|
||||
${BASE_COMPANY_FIELDS_FRAGMENT}
|
||||
${BASE_ACCOUNT_OWNER_FRAGMENT}
|
||||
fragment companyFieldsFragment on Company {
|
||||
accountOwner {
|
||||
...baseAccountOwnerFragment
|
||||
}
|
||||
...baseCompanyFieldsFragment
|
||||
}
|
||||
`;
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const DELETE_MANY_COMPANIES = gql`
|
||||
mutation DeleteManyCompanies($ids: [String!]) {
|
||||
deleteManyCompany(where: { id: { in: $ids } }) {
|
||||
count
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const INSERT_MANY_COMPANY = gql`
|
||||
mutation InsertManyCompany($data: [CompanyCreateManyInput!]!) {
|
||||
createManyCompany(data: $data) {
|
||||
count
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const INSERT_ONE_COMPANY = gql`
|
||||
mutation InsertOneCompany($data: CompanyCreateInput!) {
|
||||
createOneCompany(data: $data) {
|
||||
...companyFieldsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,12 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPDATE_ONE_COMPANY = gql`
|
||||
mutation UpdateOneCompany(
|
||||
$where: CompanyWhereUniqueInput!
|
||||
$data: CompanyUpdateInput!
|
||||
) {
|
||||
updateOneCompany(data: $data, where: $where) {
|
||||
...companyFieldsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,18 +0,0 @@
|
||||
import { Company } from '~/generated/graphql';
|
||||
|
||||
import { GET_COMPANIES } from '../queries/getCompanies';
|
||||
|
||||
export const getCompaniesOptimisticEffectDefinition = {
|
||||
key: 'generic-entity-table-data-companies',
|
||||
typename: 'Company',
|
||||
query: GET_COMPANIES,
|
||||
resolver: ({
|
||||
currentData,
|
||||
newData,
|
||||
}: {
|
||||
currentData: Company[];
|
||||
newData: Company[];
|
||||
}) => {
|
||||
return [...newData, ...currentData];
|
||||
},
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
import { COMPANY_FIELDS_FRAGMENT } from '../fragments/companyFieldsFragment';
|
||||
|
||||
export const GET_COMPANIES = gql`
|
||||
${COMPANY_FIELDS_FRAGMENT}
|
||||
query GetCompanies(
|
||||
$orderBy: [CompanyOrderByWithRelationInput!]
|
||||
$where: CompanyWhereInput
|
||||
) {
|
||||
companies: findManyCompany(orderBy: $orderBy, where: $where) {
|
||||
...companyFieldsFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,18 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_COMPANY = gql`
|
||||
query GetCompany($where: CompanyWhereUniqueInput!) {
|
||||
findUniqueCompany(where: $where) {
|
||||
...companyFieldsFragment
|
||||
Favorite {
|
||||
id
|
||||
person {
|
||||
id
|
||||
}
|
||||
company {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,15 +0,0 @@
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { entityFieldsFamilyState } from '@/ui/object/field/states/entityFieldsFamilyState';
|
||||
import { useGetCompanyQuery } from '~/generated/graphql';
|
||||
|
||||
export const useCompanyQuery = (id: string) => {
|
||||
const updateCompanyShowPage = useSetRecoilState(entityFieldsFamilyState(id));
|
||||
|
||||
return useGetCompanyQuery({
|
||||
variables: { where: { id } },
|
||||
onCompleted: (data) => {
|
||||
updateCompanyShowPage(data?.findUniqueCompany);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1,157 +0,0 @@
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import {
|
||||
IconCheckbox,
|
||||
IconHeart,
|
||||
IconHeartOff,
|
||||
IconNotes,
|
||||
IconTrash,
|
||||
} from '@/ui/display/icon';
|
||||
import { actionBarEntriesState } from '@/ui/navigation/action-bar/states/actionBarEntriesState';
|
||||
import { contextMenuEntriesState } from '@/ui/navigation/context-menu/states/contextMenuEntriesState';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { selectedRowIdsSelector } from '@/ui/object/record-table/states/selectors/selectedRowIdsSelector';
|
||||
import { tableRowIdsState } from '@/ui/object/record-table/states/tableRowIdsState';
|
||||
import {
|
||||
ActivityType,
|
||||
useDeleteManyCompaniesMutation,
|
||||
useGetFavoritesQuery,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { GET_COMPANY } from '../graphql/queries/getCompany';
|
||||
|
||||
import { useCreateActivityForCompany } from './useCreateActivityForCompany';
|
||||
|
||||
export const useCompanyTableContextMenuEntries = () => {
|
||||
const setContextMenuEntries = useSetRecoilState(contextMenuEntriesState);
|
||||
const setActionBarEntriesState = useSetRecoilState(actionBarEntriesState);
|
||||
const createActivityForCompany = useCreateActivityForCompany();
|
||||
|
||||
const setTableRowIds = useSetRecoilState(tableRowIdsState);
|
||||
const { resetTableRowSelection } = useRecordTable({
|
||||
recordTableScopeId: 'companies',
|
||||
});
|
||||
|
||||
const { data } = useGetFavoritesQuery();
|
||||
const favorites = data?.findFavorites;
|
||||
const { createFavorite, deleteFavorite } = useFavorites({
|
||||
objectNamePlural: 'companies',
|
||||
});
|
||||
|
||||
const handleFavoriteButtonClick = useRecoilCallback(({ snapshot }) => () => {
|
||||
const selectedRowIds = snapshot
|
||||
.getLoadable(selectedRowIdsSelector)
|
||||
.getValue();
|
||||
|
||||
const selectedCompanyId =
|
||||
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
|
||||
|
||||
const isFavorite =
|
||||
!!selectedCompanyId &&
|
||||
!!favorites?.find(
|
||||
(favorite) => favorite.company?.id === selectedCompanyId,
|
||||
);
|
||||
|
||||
resetTableRowSelection();
|
||||
if (isFavorite) deleteFavorite(selectedCompanyId);
|
||||
else createFavorite('company', selectedCompanyId);
|
||||
});
|
||||
|
||||
const [deleteManyCompany] = useDeleteManyCompaniesMutation({
|
||||
refetchQueries: [getOperationName(GET_COMPANY) ?? ''],
|
||||
});
|
||||
|
||||
const handleDeleteClick = useRecoilCallback(({ snapshot }) => async () => {
|
||||
const rowIdsToDelete = snapshot
|
||||
.getLoadable(selectedRowIdsSelector)
|
||||
.getValue();
|
||||
|
||||
resetTableRowSelection();
|
||||
|
||||
await deleteManyCompany({
|
||||
variables: {
|
||||
ids: rowIdsToDelete,
|
||||
},
|
||||
optimisticResponse: {
|
||||
__typename: 'Mutation',
|
||||
deleteManyCompany: {
|
||||
count: rowIdsToDelete.length,
|
||||
},
|
||||
},
|
||||
update: () => {
|
||||
setTableRowIds((tableRowIds) =>
|
||||
tableRowIds.filter((id) => !rowIdsToDelete.includes(id)),
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
setContextMenuEntries: useRecoilCallback(({ snapshot }) => () => {
|
||||
const selectedRowIds = snapshot
|
||||
.getLoadable(selectedRowIdsSelector)
|
||||
.getValue();
|
||||
|
||||
const selectedCompanyId =
|
||||
selectedRowIds.length === 1 ? selectedRowIds[0] : '';
|
||||
|
||||
const isFavorite =
|
||||
!!selectedCompanyId &&
|
||||
!!favorites?.find(
|
||||
(favorite) => favorite.company?.id === selectedCompanyId,
|
||||
);
|
||||
|
||||
setContextMenuEntries([
|
||||
{
|
||||
label: 'New task',
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => createActivityForCompany(ActivityType.Task),
|
||||
},
|
||||
{
|
||||
label: 'New note',
|
||||
Icon: IconNotes,
|
||||
onClick: () => createActivityForCompany(ActivityType.Note),
|
||||
},
|
||||
...(!!selectedCompanyId
|
||||
? [
|
||||
{
|
||||
label: isFavorite
|
||||
? 'Remove from favorites'
|
||||
: 'Add to favorites',
|
||||
Icon: isFavorite ? IconHeartOff : IconHeart,
|
||||
onClick: () => handleFavoriteButtonClick(),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: () => handleDeleteClick(),
|
||||
},
|
||||
]);
|
||||
}),
|
||||
setActionBarEntries: useRecoilCallback(() => () => {
|
||||
setActionBarEntriesState([
|
||||
{
|
||||
label: 'Task',
|
||||
Icon: IconCheckbox,
|
||||
onClick: () => createActivityForCompany(ActivityType.Task),
|
||||
},
|
||||
{
|
||||
label: 'Note',
|
||||
Icon: IconNotes,
|
||||
onClick: () => createActivityForCompany(ActivityType.Note),
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
Icon: IconTrash,
|
||||
accent: 'danger',
|
||||
onClick: () => handleDeleteClick(),
|
||||
},
|
||||
]);
|
||||
}),
|
||||
};
|
||||
};
|
||||
@ -1,16 +0,0 @@
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useOpenCreateActivityDrawerForSelectedRowIds } from '@/activities/hooks/useOpenCreateActivityDrawerForSelectedRowIds';
|
||||
import { ActivityType } from '@/activities/types/Activity';
|
||||
|
||||
export const useCreateActivityForCompany = () => {
|
||||
const openCreateActivityRightDrawer =
|
||||
useOpenCreateActivityDrawerForSelectedRowIds();
|
||||
|
||||
return useRecoilCallback(
|
||||
() => (type: ActivityType) => {
|
||||
openCreateActivityRightDrawer(type, 'Company');
|
||||
},
|
||||
[openCreateActivityRightDrawer],
|
||||
);
|
||||
};
|
||||
@ -1,38 +0,0 @@
|
||||
import { useRecoilCallback, useRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { useCreateOneObjectRecord } from '@/object-record/hooks/useCreateOneObjectRecord';
|
||||
import { currentPipelineState } from '@/pipeline/states/currentPipelineState';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { boardCardIdsByColumnIdFamilyState } from '@/ui/layout/board/states/boardCardIdsByColumnIdFamilyState';
|
||||
|
||||
export const useCreateCompanyProgress = () => {
|
||||
const { createOneObject: createOneOpportunity } =
|
||||
useCreateOneObjectRecord<Opportunity>({
|
||||
objectNameSingular: 'opportunityV2',
|
||||
});
|
||||
|
||||
const [currentPipeline] = useRecoilState(currentPipelineState);
|
||||
|
||||
return useRecoilCallback(
|
||||
({ set }) =>
|
||||
async (companyId: string, pipelineStageId: string) => {
|
||||
if (!currentPipeline?.id) {
|
||||
throw new Error('Pipeline not found');
|
||||
}
|
||||
|
||||
const newUuid = v4();
|
||||
|
||||
set(boardCardIdsByColumnIdFamilyState(pipelineStageId), (oldValue) => [
|
||||
...oldValue,
|
||||
newUuid,
|
||||
]);
|
||||
|
||||
await createOneOpportunity?.({
|
||||
pipelineStepId: pipelineStageId,
|
||||
companyId: companyId,
|
||||
});
|
||||
},
|
||||
[createOneOpportunity, currentPipeline?.id],
|
||||
);
|
||||
};
|
||||
@ -1,6 +1,8 @@
|
||||
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
|
||||
import { useSearchCompanyQuery } from '~/generated/graphql';
|
||||
import { getLogoUrlFromDomainName } from '~/utils';
|
||||
import { useQuery } from '@apollo/client';
|
||||
|
||||
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
|
||||
import { useFilteredSearchEntityQueryV2 } from '@/search/hooks/useFilteredSearchEntityQueryV2';
|
||||
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
|
||||
|
||||
export const useFilteredSearchCompanyQuery = ({
|
||||
searchFilter,
|
||||
@ -11,25 +13,32 @@ export const useFilteredSearchCompanyQuery = ({
|
||||
selectedIds?: string[];
|
||||
limit?: number;
|
||||
}) => {
|
||||
return useFilteredSearchEntityQuery({
|
||||
queryHook: useSearchCompanyQuery,
|
||||
const { findManyQuery } = useFindOneObjectMetadataItem({
|
||||
objectNameSingular: 'company',
|
||||
});
|
||||
|
||||
const useFindManyCompanies = (options: any) =>
|
||||
useQuery(findManyQuery, options);
|
||||
|
||||
return useFilteredSearchEntityQueryV2({
|
||||
queryHook: useFindManyCompanies,
|
||||
filters: [
|
||||
{
|
||||
fieldNames: ['name'],
|
||||
fieldNames: ['name.firstName', 'name.lastName'],
|
||||
filter: searchFilter,
|
||||
},
|
||||
],
|
||||
orderByField: 'name',
|
||||
orderByField: 'createdAt',
|
||||
mappingFunction: (company) => ({
|
||||
entityType: Entity.Company,
|
||||
id: company.id,
|
||||
entityType: 'Company',
|
||||
name: company.name,
|
||||
avatarUrl: getLogoUrlFromDomainName(company.domainName),
|
||||
domainName: company.domainName,
|
||||
avatarType: 'squared',
|
||||
avatarUrl: '',
|
||||
originalEntity: company,
|
||||
}),
|
||||
selectedIds: selectedIds,
|
||||
objectNamePlural: 'workspaceMembers',
|
||||
limit,
|
||||
});
|
||||
};
|
||||
|
||||
@ -1,59 +0,0 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useSpreadsheetImport } from '@/spreadsheet-import/hooks/useSpreadsheetImport';
|
||||
import { SpreadsheetOptions } from '@/spreadsheet-import/types';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
|
||||
import { useInsertManyCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
import { fieldsForCompany } from '../utils/fieldsForCompany';
|
||||
|
||||
export type FieldCompanyMapping = (typeof fieldsForCompany)[number]['key'];
|
||||
|
||||
export const useSpreadsheetCompanyImport = () => {
|
||||
const { openSpreadsheetImport } = useSpreadsheetImport<FieldCompanyMapping>();
|
||||
const { enqueueSnackBar } = useSnackBar();
|
||||
|
||||
const [createManyCompany] = useInsertManyCompanyMutation();
|
||||
|
||||
const openCompanySpreadsheetImport = (
|
||||
options?: Omit<
|
||||
SpreadsheetOptions<FieldCompanyMapping>,
|
||||
'fields' | 'isOpen' | 'onClose'
|
||||
>,
|
||||
) => {
|
||||
openSpreadsheetImport({
|
||||
...options,
|
||||
onSubmit: async (data) => {
|
||||
// TODO: Add better type checking in spreadsheet import later
|
||||
const createInputs = data.validData.map((company) => ({
|
||||
id: uuidv4(),
|
||||
name: (company.name ?? '') as string,
|
||||
domainName: (company.domainName ?? '') as string,
|
||||
address: (company.address ?? '') as string,
|
||||
employees: parseInt((company.employees ?? '') as string, 10),
|
||||
linkedinUrl: (company.linkedinUrl ?? '') as string | undefined,
|
||||
}));
|
||||
|
||||
try {
|
||||
const result = await createManyCompany({
|
||||
variables: {
|
||||
data: createInputs,
|
||||
},
|
||||
refetchQueries: 'active',
|
||||
});
|
||||
|
||||
if (result.errors) {
|
||||
throw result.errors;
|
||||
}
|
||||
} catch (error: any) {
|
||||
enqueueSnackBar(error?.message || 'Something went wrong', {
|
||||
variant: 'error',
|
||||
});
|
||||
}
|
||||
},
|
||||
fields: fieldsForCompany,
|
||||
});
|
||||
};
|
||||
|
||||
return { openCompanySpreadsheetImport };
|
||||
};
|
||||
@ -1,149 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
|
||||
import { getCompaniesOptimisticEffectDefinition } from '@/companies/graphql/optimistic-effect-definitions/getCompaniesOptimisticEffectDefinition';
|
||||
import { useCompanyTableContextMenuEntries } from '@/companies/hooks/useCompanyTableContextMenuEntries';
|
||||
import { useSpreadsheetCompanyImport } from '@/companies/hooks/useSpreadsheetCompanyImport';
|
||||
import { RecordTableEffect } from '@/ui/object/record-table/components/RecordTableEffect';
|
||||
import { RecordTableV1 } from '@/ui/object/record-table/components/RecordTableV1';
|
||||
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
|
||||
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
|
||||
import { ViewBar } from '@/views/components/ViewBar';
|
||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { mapColumnDefinitionsToViewFields } from '@/views/utils/mapColumnDefinitionToViewField';
|
||||
import { mapViewFieldsToColumnDefinitions } from '@/views/utils/mapViewFieldsToColumnDefinitions';
|
||||
import { mapViewFiltersToFilters } from '@/views/utils/mapViewFiltersToFilters';
|
||||
import { mapViewSortsToSorts } from '@/views/utils/mapViewSortsToSorts';
|
||||
import {
|
||||
UpdateOneCompanyMutationVariables,
|
||||
useGetCompaniesQuery,
|
||||
useGetWorkspaceMembersLazyQuery,
|
||||
useUpdateOneCompanyMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
|
||||
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
|
||||
|
||||
import CompanyTableEffect from './CompanyTableEffect';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const CompanyTable = () => {
|
||||
const viewScopeId = 'company-table-view';
|
||||
const tableScopeId = 'companies';
|
||||
|
||||
const {
|
||||
setTableFilters,
|
||||
setTableSorts,
|
||||
setTableColumns,
|
||||
upsertRecordTableItem,
|
||||
} = useRecordTable({
|
||||
recordTableScopeId: tableScopeId,
|
||||
});
|
||||
|
||||
const [updateEntityMutation] = useUpdateOneCompanyMutation();
|
||||
|
||||
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
|
||||
const { persistViewFields } = useViewFields(viewScopeId);
|
||||
const { setEntityCountInCurrentView } = useView({ viewScopeId });
|
||||
|
||||
const { setContextMenuEntries, setActionBarEntries } =
|
||||
useCompanyTableContextMenuEntries();
|
||||
|
||||
const updateCompany = async (
|
||||
variables: UpdateOneCompanyMutationVariables,
|
||||
) => {
|
||||
if (variables.data.accountOwner?.connect?.id) {
|
||||
const workspaceMemberAccountOwner = (
|
||||
await getWorkspaceMember({
|
||||
variables: {
|
||||
where: {
|
||||
userId: { equals: variables.data.accountOwner.connect?.id },
|
||||
},
|
||||
},
|
||||
})
|
||||
).data?.workspaceMembers?.[0];
|
||||
variables.data.workspaceMemberAccountOwner = {
|
||||
connect: { id: workspaceMemberAccountOwner?.id },
|
||||
};
|
||||
}
|
||||
|
||||
updateEntityMutation({
|
||||
variables: variables,
|
||||
onCompleted: (data) => {
|
||||
if (!data.updateOneCompany) {
|
||||
return;
|
||||
}
|
||||
upsertRecordTableItem(data.updateOneCompany);
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const { openCompanySpreadsheetImport: onImport } =
|
||||
useSpreadsheetCompanyImport();
|
||||
|
||||
return (
|
||||
<ViewScope
|
||||
viewScopeId={viewScopeId}
|
||||
onViewFieldsChange={(viewFields) => {
|
||||
setTableColumns(
|
||||
mapViewFieldsToColumnDefinitions(
|
||||
viewFields,
|
||||
companiesAvailableFieldDefinitions,
|
||||
),
|
||||
);
|
||||
}}
|
||||
onViewFiltersChange={(viewFilters) => {
|
||||
setTableFilters(mapViewFiltersToFilters(viewFilters));
|
||||
}}
|
||||
onViewSortsChange={(viewSorts) => {
|
||||
setTableSorts(mapViewSortsToSorts(viewSorts));
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<RecordTableScope
|
||||
recordTableScopeId={tableScopeId}
|
||||
onColumnsChange={useRecoilCallback(() => (columns) => {
|
||||
persistViewFields(mapColumnDefinitionsToViewFields(columns));
|
||||
})}
|
||||
onEntityCountChange={useRecoilCallback(() => (entityCount) => {
|
||||
setEntityCountInCurrentView(entityCount);
|
||||
})}
|
||||
>
|
||||
<ViewBar
|
||||
optionsDropdownButton={<TableOptionsDropdown onImport={onImport} />}
|
||||
optionsDropdownScopeId={TableOptionsDropdownId}
|
||||
/>
|
||||
<CompanyTableEffect />
|
||||
<RecordTableEffect
|
||||
getRequestResultKey="companies"
|
||||
useGetRequest={useGetCompaniesQuery}
|
||||
getRequestOptimisticEffectDefinition={
|
||||
getCompaniesOptimisticEffectDefinition
|
||||
}
|
||||
filterDefinitionArray={companyTableFilterDefinitions}
|
||||
sortDefinitionArray={companyTableSortDefinitions}
|
||||
setContextMenuEntries={setContextMenuEntries}
|
||||
setActionBarEntries={setActionBarEntries}
|
||||
/>
|
||||
<RecordTableV1
|
||||
updateEntityMutation={({
|
||||
variables,
|
||||
}: {
|
||||
variables: UpdateOneCompanyMutationVariables;
|
||||
}) => updateCompany(variables)}
|
||||
/>
|
||||
</RecordTableScope>
|
||||
</StyledContainer>
|
||||
</ViewScope>
|
||||
);
|
||||
};
|
||||
@ -1,41 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
import { useView } from '@/views/hooks/useView';
|
||||
import { ViewType } from '@/views/types/ViewType';
|
||||
import { companyTableFilterDefinitions } from '~/pages/companies/constants/companyTableFilterDefinitions';
|
||||
import { companyTableSortDefinitions } from '~/pages/companies/constants/companyTableSortDefinitions';
|
||||
|
||||
const CompanyTableEffect = () => {
|
||||
const {
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableFieldDefinitions,
|
||||
setViewType,
|
||||
setViewObjectMetadataId,
|
||||
} = useView();
|
||||
|
||||
const { setAvailableTableColumns } = useRecordTable();
|
||||
|
||||
useEffect(() => {
|
||||
setAvailableSortDefinitions?.(companyTableSortDefinitions);
|
||||
setAvailableFilterDefinitions?.(companyTableFilterDefinitions);
|
||||
setAvailableFieldDefinitions?.(companiesAvailableFieldDefinitions);
|
||||
setViewObjectMetadataId?.('company');
|
||||
setViewType?.(ViewType.Table);
|
||||
|
||||
setAvailableTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [
|
||||
setAvailableFieldDefinitions,
|
||||
setAvailableFilterDefinitions,
|
||||
setAvailableSortDefinitions,
|
||||
setAvailableTableColumns,
|
||||
setViewObjectMetadataId,
|
||||
setViewType,
|
||||
]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export default CompanyTableEffect;
|
||||
@ -1,17 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
import { companiesAvailableFieldDefinitions } from '@/companies/constants/companiesAvailableFieldDefinitions';
|
||||
import { useRecordTable } from '@/ui/object/record-table/hooks/useRecordTable';
|
||||
|
||||
import { mockedCompaniesData } from './companies-mock-data';
|
||||
|
||||
export const CompanyTableMockDataEffect = () => {
|
||||
const { setRecordTableData, setTableColumns } = useRecordTable();
|
||||
|
||||
useEffect(() => {
|
||||
setRecordTableData(mockedCompaniesData);
|
||||
setTableColumns(companiesAvailableFieldDefinitions);
|
||||
}, [setRecordTableData, setTableColumns]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
@ -1,42 +0,0 @@
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
|
||||
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
|
||||
import { TableOptionsDropdown } from '@/ui/object/record-table/options/components/TableOptionsDropdown';
|
||||
import { RecordTableScope } from '@/ui/object/record-table/scopes/RecordTableScope';
|
||||
import { ViewBar } from '@/views/components/ViewBar';
|
||||
import { ViewScope } from '@/views/scopes/ViewScope';
|
||||
import { useUpdateOneCompanyMutation } from '~/generated/graphql';
|
||||
|
||||
import CompanyTableEffect from './CompanyTableEffect';
|
||||
import { CompanyTableMockDataEffect } from './CompanyTableMockDataEffect';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export const CompanyTableMockMode = () => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<ViewScope viewScopeId="company-table-mock-mode">
|
||||
<RecordTableScope
|
||||
recordTableScopeId="company-table-mock-mode-table"
|
||||
onColumnsChange={() => {}}
|
||||
onEntityCountChange={() => {}}
|
||||
>
|
||||
<CompanyTableEffect />
|
||||
<CompanyTableMockDataEffect />
|
||||
<ViewBar
|
||||
optionsDropdownButton={<TableOptionsDropdown />}
|
||||
optionsDropdownScopeId={TableOptionsDropdownId}
|
||||
/>
|
||||
|
||||
<RecordTable updateEntityMutation={useUpdateOneCompanyMutation} />
|
||||
</RecordTableScope>
|
||||
</ViewScope>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
@ -1,112 +0,0 @@
|
||||
import { Favorite } from '@/favorites/types/Favorite';
|
||||
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
|
||||
|
||||
import { Company } from '../../../../generated/graphql';
|
||||
|
||||
type MockedCompany = Pick<
|
||||
Company,
|
||||
| 'id'
|
||||
| 'name'
|
||||
| 'domainName'
|
||||
| '__typename'
|
||||
| 'createdAt'
|
||||
| 'address'
|
||||
| 'employees'
|
||||
| 'linkedinUrl'
|
||||
| 'xUrl'
|
||||
| 'annualRecurringRevenue'
|
||||
| 'idealCustomerProfile'
|
||||
| '_activityCount'
|
||||
> & {
|
||||
accountOwner: Pick<WorkspaceMember, 'id' | 'avatarUrl' | 'name'> | null;
|
||||
} & { Favorite: Pick<Favorite, 'id'> | null };
|
||||
|
||||
export const mockedCompaniesData: Array<MockedCompany> = [
|
||||
{
|
||||
id: '89bb825c-171e-4bcc-9cf7-43448d6fb278',
|
||||
domainName: 'airbnb.com',
|
||||
name: 'Airbnb',
|
||||
linkedinUrl: 'https://www.linkedin.com/company/airbnb/',
|
||||
xUrl: 'https://twitter.com/airbnb',
|
||||
annualRecurringRevenue: 500000,
|
||||
idealCustomerProfile: true,
|
||||
createdAt: '2023-04-26T10:08:54.724515+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 5000,
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: {
|
||||
name: {
|
||||
firstName: 'Charles',
|
||||
lastName: 'Test',
|
||||
},
|
||||
avatarUrl: null,
|
||||
id: '7dfbc3f7-6e5e-4128-957e-8d86808cdf6b',
|
||||
},
|
||||
__typename: 'Company',
|
||||
},
|
||||
{
|
||||
id: 'b396e6b9-dc5c-4643-bcff-61b6cf7523ae',
|
||||
domainName: 'qonto.com',
|
||||
name: 'Qonto',
|
||||
linkedinUrl: 'https://www.linkedin.com/company/qonto/',
|
||||
xUrl: 'https://twitter.com/qonto',
|
||||
annualRecurringRevenue: 500000,
|
||||
idealCustomerProfile: false,
|
||||
createdAt: '2023-04-26T10:12:42.33625+00:00',
|
||||
address: 'Paris, France',
|
||||
employees: 800,
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
{
|
||||
id: 'a674fa6c-1455-4c57-afaf-dd5dc086361d',
|
||||
domainName: 'stripe.com',
|
||||
name: 'Stripe',
|
||||
linkedinUrl: 'https://www.linkedin.com/company/stripe/',
|
||||
xUrl: 'https://twitter.com/stripe',
|
||||
annualRecurringRevenue: 5000000,
|
||||
idealCustomerProfile: false,
|
||||
createdAt: '2023-04-26T10:10:32.530184+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 8000,
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
{
|
||||
id: 'b1cfd51b-a831-455f-ba07-4e30671e1dc3',
|
||||
domainName: 'figma.com',
|
||||
linkedinUrl: 'https://www.linkedin.com/company/figma/',
|
||||
xUrl: 'https://twitter.com/figma',
|
||||
annualRecurringRevenue: 50000,
|
||||
idealCustomerProfile: true,
|
||||
name: 'Figma',
|
||||
createdAt: '2023-03-21T06:30:25.39474+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 800,
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
{
|
||||
id: '5c21e19e-e049-4393-8c09-3e3f8fb09ecb',
|
||||
domainName: 'notion.com',
|
||||
linkedinUrl: 'https://www.linkedin.com/company/notion/',
|
||||
xUrl: 'https://twitter.com/notion',
|
||||
annualRecurringRevenue: 500000,
|
||||
idealCustomerProfile: false,
|
||||
name: 'Notion',
|
||||
createdAt: '2023-04-26T10:13:29.712485+00:00',
|
||||
address: 'San Francisco, CA',
|
||||
employees: 400,
|
||||
Favorite: null,
|
||||
_activityCount: 0,
|
||||
accountOwner: null,
|
||||
__typename: 'Company',
|
||||
},
|
||||
];
|
||||
15
front/src/modules/companies/types/Company.ts
Normal file
15
front/src/modules/companies/types/Company.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export type Company = {
|
||||
id: string;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
deletedAt: string | null;
|
||||
name: string;
|
||||
domainName: string;
|
||||
address: string;
|
||||
accountOwnerId: string | null;
|
||||
linkedinUrl: { url: string; label: string };
|
||||
xUrl: { url: string; label: string };
|
||||
annualRecurringRevenue: { amountMicros: number | null; currencyCode: string };
|
||||
employees: number | null;
|
||||
idealCustomerProfile: boolean;
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import { Company } from '@/companies/types/Company';
|
||||
import { Opportunity } from '@/pipeline/types/Opportunity';
|
||||
import { Company } from '~/generated/graphql';
|
||||
|
||||
export type CompanyForBoard = Pick<Company, 'id' | 'name' | 'domainName'>;
|
||||
export type PipelineProgressForBoard = Opportunity;
|
||||
|
||||
Reference in New Issue
Block a user