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:
Charles Bochet
2023-11-19 18:25:47 +01:00
committed by GitHub
parent 18dac1a2b6
commit f5e1d7825a
616 changed files with 2220 additions and 23073 deletions

View File

@ -7,7 +7,6 @@
"ms-vscode.makefile-tools",
"esbenp.prettier-vscode",
"GraphQL.vscode-graphql",
"prisma.prisma",
"yoavbls.pretty-ts-errors",
"graphql.vscode-graphql-syntax",
"graphql.vscode-graphql",

View File

@ -25,10 +25,6 @@
"[json]": {
"editor.formatOnSave": true
},
"[prisma]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "Prisma.prisma"
},
"javascript.format.enable": false,
"typescript.format.enable": false,
"cSpell.enableFiletypes": [

View File

@ -94,7 +94,7 @@ Start the containers:
make up
```
Setup database, generate prisma client, run migrations, and seed:
Setup database, run migrations, and seed:
```bash
make server-database-init
```

View File

@ -12,13 +12,9 @@ import { CreateProfile } from '~/pages/auth/CreateProfile';
import { CreateWorkspace } from '~/pages/auth/CreateWorkspace';
import { SignInUp } from '~/pages/auth/SignInUp';
import { VerifyEffect } from '~/pages/auth/VerifyEffect';
import { Companies } from '~/pages/companies/Companies';
import { CompanyShow } from '~/pages/companies/CompanyShow';
import { ImpersonateEffect } from '~/pages/impersonate/ImpersonateEffect';
import { NotFound } from '~/pages/not-found/NotFound';
import { Opportunities } from '~/pages/opportunities/Opportunities';
import { People } from '~/pages/people/People';
import { PersonShow } from '~/pages/people/PersonShow';
import { SettingsNewObject } from '~/pages/settings/data-model/SettingsNewObject';
import { SettingsObjectDetail } from '~/pages/settings/data-model/SettingsObjectDetail';
import { SettingsObjectEdit } from '~/pages/settings/data-model/SettingsObjectEdit';
@ -40,7 +36,6 @@ import { SettingsObjectNewFieldStep2 } from './pages/settings/data-model/Setting
export const App = () => {
const { pathname } = useLocation();
const pageTitle = getPageTitleFromPath(pathname);
return (
<>
<PageTitle title={pageTitle} />
@ -54,11 +49,7 @@ export const App = () => {
<Route path={AppPath.Invite} element={<SignInUp />} />
<Route path={AppPath.CreateWorkspace} element={<CreateWorkspace />} />
<Route path={AppPath.CreateProfile} element={<CreateProfile />} />
<Route path="/" element={<Navigate to={AppPath.CompaniesPage} />} />
<Route path={AppPath.PeoplePage} element={<People />} />
<Route path={AppPath.PersonShowPage} element={<PersonShow />} />
<Route path={AppPath.CompaniesPage} element={<Companies />} />
<Route path={AppPath.CompanyShowPage} element={<CompanyShow />} />
<Route path="/" element={<Navigate to="/objects/companies" />} />
<Route path={AppPath.TasksPage} element={<Tasks />} />
<Route path={AppPath.Impersonate} element={<ImpersonateEffect />} />

View File

@ -59,8 +59,8 @@ export const AppNavbar = () => {
<ObjectMetadataNavItems />
<NavItem
label="Opportunities"
to="/opportunities"
active={currentPath === '/opportunities'}
to="/objects/opportunities"
active={currentPath === '/objects/opportunities'}
Icon={IconTargetArrow}
/>
</MainNavbar>

View File

@ -1,9 +1,9 @@
import { useGoToHotkeys } from '@/ui/utilities/hotkey/hooks/useGoToHotkeys';
export const GotoHotkeysEffect = () => {
useGoToHotkeys('p', '/people');
useGoToHotkeys('c', '/companies');
useGoToHotkeys('o', '/opportunities');
useGoToHotkeys('p', '/objects/people');
useGoToHotkeys('c', '/objects/companies');
useGoToHotkeys('o', '/objects/opportunities');
useGoToHotkeys('s', '/settings/profile');
useGoToHotkeys('t', '/tasks');

View File

@ -2,7 +2,6 @@ import { useEffect, useState } from 'react';
import { matchPath, useLocation, useNavigate } from 'react-router-dom';
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { ActivityTargetableEntityType } from '@/activities/types/ActivityTargetableEntity';
import { useEventTracker } from '@/analytics/hooks/useEventTracker';
import { useOnboardingStatus } from '@/auth/hooks/useOnboardingStatus';
import { OnboardingStatus } from '@/auth/utils/getOnboardingStatus';
@ -12,14 +11,11 @@ import { AppBasePath } from '@/types/AppBasePath';
import { AppPath } from '@/types/AppPath';
import { PageHotkeyScope } from '@/types/PageHotkeyScope';
import { SettingsPath } from '@/types/SettingsPath';
import { IconCheckbox, IconNotes } from '@/ui/display/icon';
import { IconCheckbox } from '@/ui/display/icon';
import { useSnackBar } from '@/ui/feedback/snack-bar/hooks/useSnackBar';
import { TableHotkeyScope } from '@/ui/object/record-table/types/TableHotkeyScope';
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
import {
ActivityType,
useGetWorkspaceFromInviteHashLazyQuery,
} from '~/generated/graphql';
import { useGetWorkspaceFromInviteHashLazyQuery } from '~/generated/graphql';
import { useIsMatchingLocation } from '../hooks/useIsMatchingLocation';
@ -114,22 +110,14 @@ export const PageChangeEffect = () => {
}
switch (true) {
case isMatchingLocation(AppPath.CompaniesPage): {
case isMatchingLocation(AppPath.RecordTablePage): {
setHotkeyScope(TableHotkeyScope.Table, { goto: true });
break;
}
case isMatchingLocation(AppPath.PeoplePage): {
setHotkeyScope(TableHotkeyScope.Table, { goto: true });
break;
}
case isMatchingLocation(AppPath.CompanyShowPage): {
case isMatchingLocation(AppPath.RecordShowPage): {
setHotkeyScope(PageHotkeyScope.CompanyShowPage, { goto: true });
break;
}
case isMatchingLocation(AppPath.PersonShowPage): {
setHotkeyScope(PageHotkeyScope.PersonShowPage, { goto: true });
break;
}
case isMatchingLocation(AppPath.OpportunitiesPage): {
setHotkeyScope(PageHotkeyScope.OpportunitiesPage, { goto: true });
break;
@ -173,93 +161,16 @@ export const PageChangeEffect = () => {
}
setToIntitialCommandMenu();
switch (true) {
case isMatchingLocation(AppPath.CompanyShowPage): {
const companyId = matchPath(
{ path: '/companies/:id' },
location.pathname,
)?.params.id;
const entity = !!companyId
? {
id: companyId,
type: 'Company' as ActivityTargetableEntityType,
}
: undefined;
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
Icon: IconCheckbox,
onCommandClick: () =>
openCreateActivity({
type: 'Task',
targetableEntities: entity ? [entity] : undefined,
}),
},
{
to: '',
label: 'Create Note',
type: CommandType.Create,
Icon: IconNotes,
onCommandClick: () =>
openCreateActivity({
type: ActivityType.Note,
targetableEntities: entity ? [entity] : undefined,
}),
},
]);
break;
}
case isMatchingLocation(AppPath.PersonShowPage): {
const personId = matchPath({ path: '/person/:id' }, location.pathname)
?.params.id;
const entity = !!personId
? { id: personId, type: 'Person' as ActivityTargetableEntityType }
: undefined;
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
Icon: IconCheckbox,
onCommandClick: () =>
openCreateActivity({
type: ActivityType.Task,
targetableEntities: entity ? [entity] : undefined,
}),
},
{
to: '',
label: 'Create Note',
type: CommandType.Create,
Icon: IconNotes,
onCommandClick: () =>
openCreateActivity({
type: ActivityType.Note,
targetableEntities: entity ? [entity] : undefined,
}),
},
]);
break;
}
default:
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
Icon: IconCheckbox,
onCommandClick: () =>
openCreateActivity({ type: ActivityType.Task }),
},
]);
break;
}
addToCommandMenu([
{
to: '',
label: 'Create Task',
type: CommandType.Create,
Icon: IconCheckbox,
onCommandClick: () => openCreateActivity({ type: 'Task' }),
},
]);
setTimeout(() => {
eventTracker('pageview', {

View File

@ -20,92 +20,8 @@ export type Scalars = {
DateTime: { input: any; output: any; }
/** The `JSON` scalar type represents JSON values as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
JSON: { input: any; output: any; }
/** The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf). */
JSONObject: { input: any; output: any; }
};
export type Activity = {
__typename?: 'Activity';
activityTargets?: Maybe<Array<ActivityTarget>>;
assignee?: Maybe<User>;
assigneeId?: Maybe<Scalars['String']['output']>;
attachments?: Maybe<Array<Attachment>>;
author: User;
authorId: Scalars['String']['output'];
body?: Maybe<Scalars['String']['output']>;
comments?: Maybe<Array<Comment>>;
completedAt?: Maybe<Scalars['DateTime']['output']>;
createdAt: Scalars['DateTime']['output'];
dueAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['ID']['output'];
reminderAt?: Maybe<Scalars['DateTime']['output']>;
title?: Maybe<Scalars['String']['output']>;
type: ActivityType;
updatedAt: Scalars['DateTime']['output'];
workspaceMemberAssignee?: Maybe<WorkspaceMember>;
workspaceMemberAssigneeId?: Maybe<Scalars['String']['output']>;
workspaceMemberAuthor?: Maybe<WorkspaceMember>;
workspaceMemberAuthorId?: Maybe<Scalars['String']['output']>;
};
export type ActivityTarget = {
__typename?: 'ActivityTarget';
activity: Activity;
activityId: Scalars['String']['output'];
company?: Maybe<Company>;
companyId?: Maybe<Scalars['String']['output']>;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
person?: Maybe<Person>;
personId?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
};
export enum ActivityType {
Note = 'Note',
Task = 'Task'
}
export type ApiKey = {
__typename?: 'ApiKey';
createdAt: Scalars['DateTime']['output'];
expiresAt?: Maybe<Scalars['DateTime']['output']>;
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type Attachment = {
__typename?: 'Attachment';
activity?: Maybe<Activity>;
activityId?: Maybe<Scalars['String']['output']>;
author: User;
authorId: Scalars['String']['output'];
company?: Maybe<Company>;
companyId?: Maybe<Scalars['String']['output']>;
createdAt: Scalars['DateTime']['output'];
fullPath: Scalars['String']['output'];
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
person?: Maybe<Person>;
personId?: Maybe<Scalars['String']['output']>;
type: AttachmentType;
updatedAt: Scalars['DateTime']['output'];
workspace: Workspace;
workspaceMemberAuthor?: Maybe<WorkspaceMember>;
workspaceMemberAuthorId?: Maybe<Scalars['String']['output']>;
};
export enum AttachmentType {
Archive = 'Archive',
Audio = 'Audio',
Image = 'Image',
Other = 'Other',
Spreadsheet = 'Spreadsheet',
TextDocument = 'TextDocument',
Video = 'Video'
}
export type AuthProviders = {
__typename?: 'AuthProviders';
google: Scalars['Boolean']['output'];
@ -130,53 +46,8 @@ export type BooleanFieldComparison = {
isNot?: InputMaybe<Scalars['Boolean']['input']>;
};
export enum ColorScheme {
Dark = 'Dark',
Light = 'Light',
System = 'System'
}
export type Comment = {
__typename?: 'Comment';
activity?: Maybe<Activity>;
activityId?: Maybe<Scalars['String']['output']>;
author: User;
authorId: Scalars['String']['output'];
body: Scalars['String']['output'];
commentThreadId?: Maybe<Scalars['String']['output']>;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
updatedAt: Scalars['DateTime']['output'];
workspaceMemberAuthor?: Maybe<WorkspaceMember>;
workspaceMemberAuthorId?: Maybe<Scalars['String']['output']>;
};
export type Company = {
__typename?: 'Company';
ActivityTarget?: Maybe<Array<ActivityTarget>>;
Attachment?: Maybe<Array<Attachment>>;
Favorite?: Maybe<Array<Favorite>>;
PipelineProgress?: Maybe<Array<PipelineProgress>>;
accountOwner?: Maybe<User>;
accountOwnerId?: Maybe<Scalars['String']['output']>;
address: Scalars['String']['output'];
annualRecurringRevenue?: Maybe<Scalars['Int']['output']>;
createdAt: Scalars['DateTime']['output'];
domainName: Scalars['String']['output'];
employees?: Maybe<Scalars['Int']['output']>;
id: Scalars['ID']['output'];
idealCustomerProfile: Scalars['Boolean']['output'];
linkedinUrl?: Maybe<Scalars['String']['output']>;
name: Scalars['String']['output'];
people?: Maybe<Array<Person>>;
updatedAt: Scalars['DateTime']['output'];
workspaceMemberAccountOwner?: Maybe<WorkspaceMember>;
workspaceMemberAccountOwnerId?: Maybe<Scalars['String']['output']>;
xUrl?: Maybe<Scalars['String']['output']>;
};
export type CreateFieldInput = {
defaultValue?: InputMaybe<Scalars['JSONObject']['input']>;
defaultValue?: InputMaybe<Scalars['JSON']['input']>;
description?: InputMaybe<Scalars['String']['input']>;
icon?: InputMaybe<Scalars['String']['input']>;
isNullable?: InputMaybe<Scalars['Boolean']['input']>;
@ -223,177 +94,6 @@ export type CreateRelationInput = {
toObjectMetadataId: Scalars['String']['input'];
};
export enum CurrencyCode {
Aed = 'AED',
Afn = 'AFN',
All = 'ALL',
Amd = 'AMD',
Ang = 'ANG',
Aoa = 'AOA',
Ars = 'ARS',
Aud = 'AUD',
Awg = 'AWG',
Azn = 'AZN',
Bam = 'BAM',
Bbd = 'BBD',
Bdt = 'BDT',
Bgn = 'BGN',
Bhd = 'BHD',
Bif = 'BIF',
Bmd = 'BMD',
Bnd = 'BND',
Bob = 'BOB',
Bov = 'BOV',
Brl = 'BRL',
Bsd = 'BSD',
Btn = 'BTN',
Bwp = 'BWP',
Byn = 'BYN',
Bzd = 'BZD',
Cad = 'CAD',
Cdf = 'CDF',
Chf = 'CHF',
Clf = 'CLF',
Clp = 'CLP',
Cny = 'CNY',
Cop = 'COP',
Cou = 'COU',
Crc = 'CRC',
Cuc = 'CUC',
Cup = 'CUP',
Cve = 'CVE',
Czk = 'CZK',
Djf = 'DJF',
Dkk = 'DKK',
Dop = 'DOP',
Dzd = 'DZD',
Egp = 'EGP',
Ern = 'ERN',
Etb = 'ETB',
Eur = 'EUR',
Fjd = 'FJD',
Fkp = 'FKP',
Gbp = 'GBP',
Gel = 'GEL',
Ghs = 'GHS',
Gip = 'GIP',
Gmd = 'GMD',
Gnf = 'GNF',
Gtq = 'GTQ',
Gyd = 'GYD',
Hkd = 'HKD',
Hnl = 'HNL',
Hrk = 'HRK',
Htg = 'HTG',
Huf = 'HUF',
Idr = 'IDR',
Ils = 'ILS',
Inr = 'INR',
Iqd = 'IQD',
Irr = 'IRR',
Isk = 'ISK',
Jmd = 'JMD',
Jod = 'JOD',
Jpy = 'JPY',
Kes = 'KES',
Kgs = 'KGS',
Khr = 'KHR',
Kmf = 'KMF',
Kpw = 'KPW',
Krw = 'KRW',
Kwd = 'KWD',
Kyd = 'KYD',
Kzt = 'KZT',
Lak = 'LAK',
Lbp = 'LBP',
Lkr = 'LKR',
Lrd = 'LRD',
Lsl = 'LSL',
Lyd = 'LYD',
Mad = 'MAD',
Mdl = 'MDL',
Mga = 'MGA',
Mkd = 'MKD',
Mmk = 'MMK',
Mnt = 'MNT',
Mop = 'MOP',
Mro = 'MRO',
Mru = 'MRU',
Mur = 'MUR',
Mvr = 'MVR',
Mwk = 'MWK',
Mxn = 'MXN',
Mxv = 'MXV',
Myr = 'MYR',
Mzn = 'MZN',
Nad = 'NAD',
Ngn = 'NGN',
Nio = 'NIO',
Nok = 'NOK',
Npr = 'NPR',
Nzd = 'NZD',
Omr = 'OMR',
Pab = 'PAB',
Pen = 'PEN',
Pgk = 'PGK',
Php = 'PHP',
Pkr = 'PKR',
Pln = 'PLN',
Pyg = 'PYG',
Qar = 'QAR',
Ron = 'RON',
Rsd = 'RSD',
Rub = 'RUB',
Rwf = 'RWF',
Sar = 'SAR',
Sbd = 'SBD',
Scr = 'SCR',
Sdd = 'SDD',
Sdg = 'SDG',
Sek = 'SEK',
Sgd = 'SGD',
Shp = 'SHP',
Sll = 'SLL',
Sos = 'SOS',
Srd = 'SRD',
Ssp = 'SSP',
Std = 'STD',
Stn = 'STN',
Svc = 'SVC',
Syp = 'SYP',
Szl = 'SZL',
Thb = 'THB',
Tjs = 'TJS',
Tmm = 'TMM',
Tmt = 'TMT',
Tnd = 'TND',
Top = 'TOP',
Try = 'TRY',
Ttd = 'TTD',
Twd = 'TWD',
Tzs = 'TZS',
Uah = 'UAH',
Ugx = 'UGX',
Usd = 'USD',
Uyu = 'UYU',
Uzs = 'UZS',
Vef = 'VEF',
Ves = 'VES',
Vnd = 'VND',
Vuv = 'VUV',
Wst = 'WST',
Xaf = 'XAF',
Xcd = 'XCD',
Xof = 'XOF',
Xpf = 'XPF',
Xsu = 'XSU',
Xua = 'XUA',
Yer = 'YER',
Zar = 'ZAR',
Zmw = 'ZMW',
Zwl = 'ZWL'
}
export type CursorPaging = {
/** Paginate after opaque cursor */
after?: InputMaybe<Scalars['ConnectionCursor']['input']>;
@ -420,19 +120,6 @@ export type DeleteOneRelationInput = {
id: Scalars['ID']['input'];
};
export type Favorite = {
__typename?: 'Favorite';
company?: Maybe<Company>;
companyId?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
person?: Maybe<Person>;
personId?: Maybe<Scalars['String']['output']>;
position: Scalars['Float']['output'];
workspaceId?: Maybe<Scalars['String']['output']>;
workspaceMember?: Maybe<WorkspaceMember>;
workspaceMemberId?: Maybe<Scalars['String']['output']>;
};
export type FieldConnection = {
__typename?: 'FieldConnection';
/** Array of edges. */
@ -596,81 +283,6 @@ export type PageInfo = {
startCursor?: Maybe<Scalars['ConnectionCursor']['output']>;
};
export type Person = {
__typename?: 'Person';
ActivityTarget?: Maybe<Array<ActivityTarget>>;
Attachment?: Maybe<Array<Attachment>>;
Favorite?: Maybe<Array<Favorite>>;
PipelineProgress?: Maybe<Array<PipelineProgress>>;
avatarUrl?: Maybe<Scalars['String']['output']>;
city?: Maybe<Scalars['String']['output']>;
company?: Maybe<Company>;
companyId?: Maybe<Scalars['String']['output']>;
contactPipelineProgresses?: Maybe<Array<PipelineProgress>>;
createdAt: Scalars['DateTime']['output'];
email?: Maybe<Scalars['String']['output']>;
firstName?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
jobTitle?: Maybe<Scalars['String']['output']>;
lastName?: Maybe<Scalars['String']['output']>;
linkedinUrl?: Maybe<Scalars['String']['output']>;
phone?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
xUrl?: Maybe<Scalars['String']['output']>;
};
export type Pipeline = {
__typename?: 'Pipeline';
createdAt: Scalars['DateTime']['output'];
currency: CurrencyCode;
icon: Scalars['String']['output'];
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
pipelineProgressableType: PipelineProgressableType;
pipelineProgresses?: Maybe<Array<PipelineProgress>>;
pipelineStages?: Maybe<Array<PipelineStage>>;
updatedAt: Scalars['DateTime']['output'];
};
export type PipelineProgress = {
__typename?: 'PipelineProgress';
amount?: Maybe<Scalars['Int']['output']>;
closeDate?: Maybe<Scalars['DateTime']['output']>;
company?: Maybe<Company>;
companyId?: Maybe<Scalars['String']['output']>;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
person?: Maybe<Person>;
personId?: Maybe<Scalars['String']['output']>;
pipeline: Pipeline;
pipelineId: Scalars['String']['output'];
pipelineStage: PipelineStage;
pipelineStageId: Scalars['String']['output'];
pointOfContact?: Maybe<Person>;
pointOfContactId?: Maybe<Scalars['String']['output']>;
probability?: Maybe<Scalars['Int']['output']>;
updatedAt: Scalars['DateTime']['output'];
};
export enum PipelineProgressableType {
Company = 'Company',
Person = 'Person'
}
export type PipelineStage = {
__typename?: 'PipelineStage';
color: Scalars['String']['output'];
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
name: Scalars['String']['output'];
pipeline: Pipeline;
pipelineId: Scalars['String']['output'];
pipelineProgresses?: Maybe<Array<PipelineProgress>>;
position?: Maybe<Scalars['Int']['output']>;
type: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type Query = {
__typename?: 'Query';
field: Field;
@ -713,6 +325,22 @@ export type QueryRelationsArgs = {
paging?: CursorPaging;
};
export type RefreshToken = {
__typename?: 'RefreshToken';
createdAt: Scalars['DateTime']['output'];
expiresAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type RefreshTokenEdge = {
__typename?: 'RefreshTokenEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the RefreshToken */
node: RefreshToken;
};
export type RelationConnection = {
__typename?: 'RelationConnection';
/** Array of edges. */
@ -788,86 +416,49 @@ export type UpdateOneObjectInput = {
export type User = {
__typename?: 'User';
assignedActivities?: Maybe<Array<Activity>>;
authoredActivities?: Maybe<Array<Activity>>;
authoredAttachments?: Maybe<Array<Attachment>>;
avatarUrl?: Maybe<Scalars['String']['output']>;
avatarUrl: Scalars['String']['output'];
canImpersonate: Scalars['Boolean']['output'];
comments?: Maybe<Array<Comment>>;
companies?: Maybe<Array<Company>>;
createdAt: Scalars['DateTime']['output'];
defaultWorkspaceId?: Maybe<Scalars['String']['output']>;
disabled: Scalars['Boolean']['output'];
deletedAt?: Maybe<Scalars['DateTime']['output']>;
disabled?: Maybe<Scalars['Boolean']['output']>;
email: Scalars['String']['output'];
emailVerified: Scalars['Boolean']['output'];
firstName?: Maybe<Scalars['String']['output']>;
firstName: Scalars['String']['output'];
id: Scalars['ID']['output'];
lastName?: Maybe<Scalars['String']['output']>;
lastName: Scalars['String']['output'];
lastSeen?: Maybe<Scalars['DateTime']['output']>;
locale: Scalars['String']['output'];
metadata?: Maybe<Scalars['JSON']['output']>;
passwordHash?: Maybe<Scalars['String']['output']>;
phoneNumber?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
};
export type UserSettings = {
__typename?: 'UserSettings';
WorkspaceMember?: Maybe<Array<WorkspaceMember>>;
colorScheme: ColorScheme;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
locale: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type WebHook = {
__typename?: 'WebHook';
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
operation: Scalars['String']['output'];
targetUrl: Scalars['String']['output'];
updatedAt: Scalars['DateTime']['output'];
export type UserEdge = {
__typename?: 'UserEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the User */
node: User;
};
export type Workspace = {
__typename?: 'Workspace';
Attachment?: Maybe<Array<Attachment>>;
activities?: Maybe<Array<Activity>>;
activityTargets?: Maybe<Array<ActivityTarget>>;
apiKeys?: Maybe<Array<ApiKey>>;
comments?: Maybe<Array<Comment>>;
companies?: Maybe<Array<Company>>;
createdAt: Scalars['DateTime']['output'];
deletedAt?: Maybe<Scalars['DateTime']['output']>;
displayName?: Maybe<Scalars['String']['output']>;
domainName?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
inviteHash?: Maybe<Scalars['String']['output']>;
logo?: Maybe<Scalars['String']['output']>;
people?: Maybe<Array<Person>>;
pipelineProgresses?: Maybe<Array<PipelineProgress>>;
pipelineStages?: Maybe<Array<PipelineStage>>;
pipelines?: Maybe<Array<Pipeline>>;
updatedAt: Scalars['DateTime']['output'];
webHooks?: Maybe<Array<WebHook>>;
workspaceMember?: Maybe<Array<WorkspaceMember>>;
};
export type WorkspaceMember = {
__typename?: 'WorkspaceMember';
Favorite?: Maybe<Array<Favorite>>;
allowImpersonation: Scalars['Boolean']['output'];
assignedActivities?: Maybe<Array<Activity>>;
authoredActivities?: Maybe<Array<Activity>>;
authoredAttachments?: Maybe<Array<Attachment>>;
comments?: Maybe<Array<Comment>>;
companies?: Maybe<Array<Company>>;
createdAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
settings?: Maybe<UserSettings>;
settingsId?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
userId: Scalars['String']['output'];
workspace: Workspace;
export type WorkspaceEdge = {
__typename?: 'WorkspaceEdge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the Workspace */
node: Workspace;
};
export type Field = {
@ -948,22 +539,6 @@ export type ObjectFilter = {
or?: InputMaybe<Array<ObjectFilter>>;
};
export type RefreshTokenV2 = {
__typename?: 'refreshTokenV2';
createdAt: Scalars['DateTime']['output'];
expiresAt: Scalars['DateTime']['output'];
id: Scalars['ID']['output'];
updatedAt: Scalars['DateTime']['output'];
};
export type RefreshTokenV2Edge = {
__typename?: 'refreshTokenV2Edge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the refreshTokenV2 */
node: RefreshTokenV2;
};
export type Relation = {
__typename?: 'relation';
createdAt: Scalars['DateTime']['output'];
@ -986,54 +561,6 @@ export type RelationEdge = {
node: Relation;
};
export type UserV2 = {
__typename?: 'userV2';
avatarUrl: Scalars['String']['output'];
canImpersonate: Scalars['Boolean']['output'];
createdAt: Scalars['DateTime']['output'];
deletedAt?: Maybe<Scalars['DateTime']['output']>;
disabled?: Maybe<Scalars['Boolean']['output']>;
email: Scalars['String']['output'];
emailVerified: Scalars['Boolean']['output'];
firstName: Scalars['String']['output'];
id: Scalars['ID']['output'];
lastName: Scalars['String']['output'];
lastSeen?: Maybe<Scalars['DateTime']['output']>;
locale: Scalars['String']['output'];
metadata?: Maybe<Scalars['JSONObject']['output']>;
passwordHash?: Maybe<Scalars['String']['output']>;
phoneNumber?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
};
export type UserV2Edge = {
__typename?: 'userV2Edge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the userV2 */
node: UserV2;
};
export type WorkspaceV2 = {
__typename?: 'workspaceV2';
createdAt: Scalars['DateTime']['output'];
deletedAt?: Maybe<Scalars['DateTime']['output']>;
displayName?: Maybe<Scalars['String']['output']>;
domainName?: Maybe<Scalars['String']['output']>;
id: Scalars['ID']['output'];
inviteHash?: Maybe<Scalars['String']['output']>;
logo?: Maybe<Scalars['String']['output']>;
updatedAt: Scalars['DateTime']['output'];
};
export type WorkspaceV2Edge = {
__typename?: 'workspaceV2Edge';
/** Cursor for this node. */
cursor: Scalars['ConnectionCursor']['output'];
/** The node containing the workspaceV2 */
node: WorkspaceV2;
};
export type CreateOneObjectMetadataItemMutationVariables = Exact<{
input: CreateOneObjectInput;
}>;

File diff suppressed because it is too large Load Diff

View File

@ -8,6 +8,7 @@ import { ApolloProvider } from '@/apollo/components/ApolloProvider';
import { ClientConfigProvider } from '@/client-config/components/ClientConfigProvider';
import { RecoilDebugObserverEffect } from '@/debug/components/RecoilDebugObserver';
import { ApolloMetadataClientProvider } from '@/object-metadata/components/ApolloMetadataClientProvider';
import { ObjectMetadataItemsProvider } from '@/object-metadata/components/ObjectMetadataItemsProvider';
import { DialogManager } from '@/ui/feedback/dialog-manager/components/DialogManager';
import { DialogManagerScope } from '@/ui/feedback/dialog-manager/scopes/DialogManagerScope';
import { SnackBarProvider } from '@/ui/feedback/snack-bar/components/SnackBarProvider';
@ -36,18 +37,20 @@ root.render(
<ClientConfigProvider>
<UserProvider>
<ApolloMetadataClientProvider>
<PageChangeEffect />
<AppThemeProvider>
<SnackBarProvider>
<DialogManagerScope dialogManagerScopeId="dialog-manager">
<DialogManager>
<StrictMode>
<App />
</StrictMode>
</DialogManager>
</DialogManagerScope>
</SnackBarProvider>
</AppThemeProvider>
<ObjectMetadataItemsProvider>
<PageChangeEffect />
<AppThemeProvider>
<SnackBarProvider>
<DialogManagerScope dialogManagerScopeId="dialog-manager">
<DialogManager>
<StrictMode>
<App />
</StrictMode>
</DialogManager>
</DialogManagerScope>
</SnackBarProvider>
</AppThemeProvider>
</ObjectMetadataItemsProvider>
</ApolloMetadataClientProvider>
</UserProvider>
</ClientConfigProvider>

View File

@ -1,99 +0,0 @@
import { Activity } from '@/activities/types/Activity';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { useFilteredSearchEntityQuery } from '@/search/hooks/useFilteredSearchEntityQuery';
import { SingleEntitySelect } from '@/ui/input/relation-picker/components/SingleEntitySelect';
import { relationPickerSearchFilterScopedState } from '@/ui/input/relation-picker/states/relationPickerSearchFilterScopedState';
import { EntityForSelect } from '@/ui/input/relation-picker/types/EntityForSelect';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import {
useGetWorkspaceMembersLazyQuery,
useSearchUserQuery,
} from '~/generated/graphql';
export type ActivityAssigneePickerProps = {
activity: Pick<Activity, 'id'> & {
accountOwner?: Pick<WorkspaceMember, 'id' | 'name'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
};
type UserForSelect = EntityForSelect & {
entityType: Entity.User;
};
export const ActivityAssigneePicker = ({
activity,
onSubmit,
onCancel,
}: ActivityAssigneePickerProps) => {
const [relationPickerSearchFilter] = useRecoilScopedState(
relationPickerSearchFilterScopedState,
);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'ActivityV2',
});
const [getWorkspaceMember] = useGetWorkspaceMembersLazyQuery();
const users = useFilteredSearchEntityQuery({
queryHook: useSearchUserQuery,
filters: [
{
fieldNames: ['firstName', 'lastName'],
filter: relationPickerSearchFilter,
},
],
orderByField: 'firstName',
mappingFunction: (user) => ({
entityType: Entity.User,
id: user.id,
name: user.displayName,
firstName: user.firstName,
lastName: user.lastName,
avatarType: 'rounded',
avatarUrl: user.avatarUrl ?? '',
originalEntity: user,
}),
selectedIds: activity?.accountOwner?.id ? [activity?.accountOwner?.id] : [],
});
const handleEntitySelected = async (
selectedUser: UserForSelect | null | undefined,
) => {
if (selectedUser) {
const workspaceMemberAssignee = (
await getWorkspaceMember({
variables: {
where: {
userId: { equals: selectedUser.id },
},
},
})
).data?.workspaceMembers?.[0];
updateOneObject?.({
idToUpdate: activity.id,
input: {
assignee: { connect: { id: selectedUser.id } },
workspaceMemberAssignee: {
connect: { id: workspaceMemberAssignee?.id },
},
},
});
}
onSubmit?.();
};
return (
<SingleEntitySelect
entitiesToSelect={users.entitiesToSelect}
loading={users.loading}
onCancel={onCancel}
onEntitySelected={handleEntitySelected}
selectedEntity={users.selectedEntities[0]}
/>
);
};

View File

@ -23,7 +23,7 @@ export const ActivityBodyEditor = ({
}: ActivityBodyEditorProps) => {
const [body, setBody] = useState<string | null>(null);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'ActivityV2',
objectNameSingular: 'Activity',
});
useEffect(() => {

View File

@ -63,7 +63,7 @@ export const ActivityComments = ({
}: ActivityCommentsProps) => {
const currentUser = useRecoilValue(currentUserState);
const { createOneObject } = useCreateOneObjectRecord({
objectNameSingular: 'commentV2',
objectNameSingular: 'comment',
});
if (!currentUser) {

View File

@ -9,13 +9,10 @@ import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { Comment } from '@/activities/types/Comment';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { PropertyBox } from '@/ui/object/record-inline-cell/property-box/components/PropertyBox';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { debounce } from '~/utils/debounce';
import { ActivityAssigneeEditableField } from '../editable-fields/components/ActivityAssigneeEditableField';
import { ActivityEditorDateField } from '../editable-fields/components/ActivityEditorDateField';
import { ActivityRelationEditableField } from '../editable-fields/components/ActivityRelationEditableField';
import { ActivityTitle } from './ActivityTitle';
@ -84,7 +81,7 @@ export const ActivityEditor = ({
);
const containerRef = useRef<HTMLDivElement>(null);
const { updateOneObject } = useUpdateOneObjectRecord<Activity>({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const updateTitle = useCallback(
@ -144,12 +141,12 @@ export const ActivityEditor = ({
<PropertyBox>
{activity.type === 'Task' && (
<>
<RecoilScope>
{/* <RecoilScope>
<ActivityEditorDateField activityId={activity.id} />
</RecoilScope>
<RecoilScope>
<ActivityAssigneeEditableField activity={activity} />
</RecoilScope>
</RecoilScope> */}
</>
)}
<ActivityRelationEditableField activity={activity} />

View File

@ -2,8 +2,9 @@ import styled from '@emotion/styled';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { CompanyChip } from '@/companies/components/CompanyChip';
import { Company } from '@/companies/types/Company';
import { PersonChip } from '@/people/components/PersonChip';
import { Company, Person } from '~/generated/graphql';
import { Person } from '@/people/types/Person';
import { getLogoUrlFromDomainName } from '~/utils';
const StyledContainer = styled.div`
@ -17,10 +18,7 @@ export const ActivityTargetChips = ({
}: {
targets?: Array<
Pick<ActivityTarget, 'id'> & {
person?: Pick<
Person,
'id' | 'firstName' | 'lastName' | 'avatarUrl'
> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
}
> | null;
@ -47,7 +45,7 @@ export const ActivityTargetChips = ({
<PersonChip
key={person.id}
id={person.id}
name={person.firstName + ' ' + person.lastName}
name={person.name.firstName + ' ' + person.name.lastName}
pictureUrl={person.avatarUrl ?? undefined}
/>
);

View File

@ -1,78 +0,0 @@
import React, { useMemo } from 'react';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { IconUserCircle } from '@/ui/display/icon';
import { Entity } from '@/ui/input/relation-picker/types/EntityTypeForSelect';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldRelationMetadata } from '@/ui/object/field/types/FieldMetadata';
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
import { WorkspaceMember } from '@/workspace-member/types/WorkspaceMember';
import { Company, User } from '~/generated/graphql';
type ActivityAssigneeEditableFieldProps = {
activity: Pick<Company, 'id' | 'accountOwnerId'> & {
assignee?: Pick<WorkspaceMember, 'id' | 'name' | 'avatarUrl'> | null;
};
};
export const ActivityAssigneeEditableField = ({
activity,
}: ActivityAssigneeEditableFieldProps) => {
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
});
const updateEntity = ({
variables,
}: {
variables: {
where: { id: string };
data: {
[fieldName: string]: any;
};
};
}) => {
updateOneObject?.({
idToUpdate: variables.where.id,
input: variables.data,
});
};
return [updateEntity, { loading: false }];
};
const value = useMemo(
() => ({
entityId: activity.id,
recoilScopeId: 'assignee',
fieldDefinition: {
fieldMetadataId: 'assignee',
label: 'Assignee',
Icon: IconUserCircle,
type: 'RELATION',
metadata: {
fieldName: 'assignee',
relationType: Entity.User,
},
entityChipDisplayMapper: (dataObject: User) => {
return {
name: dataObject?.displayName,
pictureUrl: dataObject?.avatarUrl ?? undefined,
avatarType: 'rounded',
};
},
} satisfies FieldDefinition<FieldRelationMetadata>,
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}),
[activity.id],
);
return (
<FieldContext.Provider value={value}>
<RecordInlineCell />
</FieldContext.Provider>
);
};

View File

@ -1,47 +0,0 @@
import styled from '@emotion/styled';
import { ActivityAssigneePicker } from '@/activities/components/ActivityAssigneePicker';
import { useInlineCell } from '@/ui/object/record-inline-cell/hooks/useInlineCell';
import { Activity, User } from '~/generated/graphql';
const StyledContainer = styled.div`
left: 0px;
position: absolute;
top: -8px;
`;
export type ActivityAssigneeEditableFieldEditModeProps = {
activity: Pick<Activity, 'id'> & {
assignee?: Pick<User, 'id' | 'displayName'> | null;
};
onSubmit?: () => void;
onCancel?: () => void;
};
export const ActivityAssigneeEditableFieldEditMode = ({
activity,
onSubmit,
onCancel,
}: ActivityAssigneeEditableFieldEditModeProps) => {
const { closeInlineCell: closeEditableField } = useInlineCell();
const handleSubmit = () => {
closeEditableField();
onSubmit?.();
};
const handleCancel = () => {
closeEditableField();
onCancel?.();
};
return (
<StyledContainer>
<ActivityAssigneePicker
activity={activity}
onCancel={handleCancel}
onSubmit={handleSubmit}
/>
</StyledContainer>
);
};

View File

@ -1,64 +0,0 @@
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { IconCalendar } from '@/ui/display/icon/index';
import { FieldContext } from '@/ui/object/field/contexts/FieldContext';
import { FieldDefinition } from '@/ui/object/field/types/FieldDefinition';
import { FieldDateMetadata } from '@/ui/object/field/types/FieldMetadata';
import { RecordInlineCell } from '@/ui/object/record-inline-cell/components/RecordInlineCell';
import { InlineCellHotkeyScope } from '@/ui/object/record-inline-cell/types/InlineCellHotkeyScope';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
type ActivityEditorDateFieldProps = {
activityId: string;
};
export const ActivityEditorDateField = ({
activityId,
}: ActivityEditorDateFieldProps) => {
const useUpdateOneObjectMutation: () => [(params: any) => any, any] = () => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
});
const updateEntity = ({
variables,
}: {
variables: {
where: { id: string };
data: {
[fieldName: string]: any;
};
};
}) => {
updateOneObject?.({
idToUpdate: variables.where.id,
input: variables.data,
});
};
return [updateEntity, { loading: false }];
};
return (
<RecoilScope>
<FieldContext.Provider
value={{
entityId: activityId,
recoilScopeId: 'activityDueAt',
fieldDefinition: {
fieldMetadataId: 'activityDueAt',
label: 'Due date',
Icon: IconCalendar,
type: 'DATE',
metadata: {
fieldName: 'dueAt',
},
} satisfies FieldDefinition<FieldDateMetadata>,
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,
}}
>
<RecordInlineCell />
</FieldContext.Provider>
</RecoilScope>
);
};

View File

@ -1,12 +1,13 @@
import { ActivityTargetChips } from '@/activities/components/ActivityTargetChips';
import { Activity } from '@/activities/types/Activity';
import { ActivityTarget } from '@/activities/types/ActivityTarget';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
import { IconArrowUpRight, IconPencil } from '@/ui/display/icon';
import { RelationPickerHotkeyScope } from '@/ui/input/relation-picker/types/RelationPickerHotkeyScope';
import { RecordInlineCellContainer } from '@/ui/object/record-inline-cell/components/RecordInlineCellContainer';
import { FieldRecoilScopeContext } from '@/ui/object/record-inline-cell/states/recoil-scope-contexts/FieldRecoilScopeContext';
import { RecoilScope } from '@/ui/utilities/recoil-scope/components/RecoilScope';
import { Company, Person } from '~/generated/graphql';
import { ActivityRelationEditableFieldEditMode } from './ActivityRelationEditableFieldEditMode';
@ -14,7 +15,7 @@ type ActivityRelationEditableFieldProps = {
activity?: Pick<Activity, 'id'> & {
activityTargets?: Array<
Pick<ActivityTarget, 'id' | 'personId' | 'companyId'> & {
person?: Pick<Person, 'id' | 'firstName' | 'lastName'> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'domainName' | 'name'> | null;
}
> | null;

View File

@ -92,14 +92,9 @@ export const ActivityRelationEditableFieldEditMode = ({
const { closeInlineCell: closeEditableField } = useInlineCell();
const handleSubmit = useCallback(() => {
handleCheckItemsChange(selectedEntityIds, entitiesToSelect);
//handleCheckItemsChange(selectedEntityIds, entitiesToSelect);
closeEditableField();
}, [
handleCheckItemsChange,
selectedEntityIds,
entitiesToSelect,
closeEditableField,
]);
}, [closeEditableField]);
const handleCancel = () => {
closeEditableField();

View File

@ -17,10 +17,10 @@ export const useHandleCheckableActivityTargetChange = ({
};
}) => {
const { createOneObject } = useCreateOneObjectRecord<ActivityTarget>({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
return async (

View File

@ -18,11 +18,11 @@ export const useOpenCreateActivityDrawer = () => {
const { openRightDrawer } = useRightDrawer();
const { createOneObject: createOneActivityTarget } =
useCreateOneObjectRecord<ActivityTarget>({
objectNameSingular: 'activityTargetV2',
objectNameSingular: 'activityTarget',
});
const { createOneObject: createOneActivity } =
useCreateOneObjectRecord<Activity>({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const setHotkeyScope = useSetHotkeyScope();

View File

@ -6,7 +6,6 @@ import { useNotes } from '@/activities/notes/hooks/useNotes';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { ActivityType } from '~/generated/graphql';
const StyledTaskGroupEmptyContainer = styled.div`
align-items: center;
@ -61,7 +60,7 @@ export const Notes = ({ entity }: { entity: ActivityTargetableEntity }) => {
variant="secondary"
onClick={() =>
openCreateActivity({
type: ActivityType.Note,
type: 'Note',
targetableEntities: [entity],
})
}
@ -83,7 +82,7 @@ export const Notes = ({ entity }: { entity: ActivityTargetableEntity }) => {
title="Add note"
onClick={() =>
openCreateActivity({
type: ActivityType.Note,
type: 'Note',
targetableEntities: [entity],
})
}

View File

@ -5,7 +5,7 @@ import { ActivityTargetableEntity } from '../../types/ActivityTargetableEntity';
export const useNotes = (entity: ActivityTargetableEntity) => {
const { objects: notes } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
type: { equals: 'None' },
activityTargets: {

View File

@ -12,7 +12,7 @@ type ActivityActionBarProps = {
export const ActivityActionBar = ({ activityId }: ActivityActionBarProps) => {
const [, setIsRightDrawerOpen] = useRecoilState(isRightDrawerOpenState);
const { deleteOneObject } = useDeleteOneObjectRecord({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const deleteActivity = () => {

View File

@ -2,7 +2,6 @@ import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateAct
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { ActivityType } from '~/generated/graphql';
export const AddTaskButton = ({
activityTargetEntity,
@ -23,7 +22,7 @@ export const AddTaskButton = ({
title="Add task"
onClick={() =>
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
targetableEntities: [activityTargetEntity],
})
}

View File

@ -1,7 +1,6 @@
import { useOpenCreateActivityDrawer } from '@/activities/hooks/useOpenCreateActivityDrawer';
import { PageAddButton } from '@/ui/layout/page/PageAddButton';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { ActivityType } from '~/generated/graphql';
export const PageAddTaskButton = () => {
const { selectedFilter } = useFilter();
@ -9,7 +8,7 @@ export const PageAddTaskButton = () => {
const handleClick = () => {
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
assigneeId: selectedFilter?.value,
});
};

View File

@ -8,7 +8,6 @@ import { IconPlus } from '@/ui/display/icon';
import { Button } from '@/ui/input/button/components/Button';
import { activeTabIdScopedState } from '@/ui/layout/tab/states/activeTabIdScopedState';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
import { ActivityType } from '~/generated/graphql';
import { AddTaskButton } from './AddTaskButton';
import { TaskList } from './TaskList';
@ -84,7 +83,7 @@ export const TaskGroups = ({ entity, showAddButton }: TaskGroupsProps) => {
variant={'secondary'}
onClick={() =>
openCreateActivity({
type: ActivityType.Task,
type: 'Task',
targetableEntities: entity ? [entity] : undefined,
})
}

View File

@ -1,13 +1,13 @@
import { useCallback } from 'react';
import { Activity } from '@/activities/types/Activity';
import { useUpdateOneObjectRecord } from '@/object-record/hooks/useUpdateOneObjectRecord';
import { Activity } from '~/generated/graphql';
type Task = Pick<Activity, 'id' | 'completedAt'>;
export const useCompleteTask = (task: Task) => {
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: 'activityV2',
objectNameSingular: 'activity',
});
const completeTask = useCallback(

View File

@ -9,7 +9,7 @@ export const useCurrentUserTaskCount = () => {
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { objects } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
type: { eq: 'Task' },
completedAt: { eq: null },

View File

@ -4,8 +4,7 @@ import { Activity } from '@/activities/types/Activity';
import { ActivityTargetableEntity } from '@/activities/types/ActivityTargetableEntity';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { useFilter } from '@/ui/object/object-filter-dropdown/hooks/useFilter';
import { turnFilterIntoWhereClause } from '@/ui/object/object-filter-dropdown/utils/turnFilterIntoWhereClause';
import { SortOrder } from '~/generated/graphql';
import { turnFiltersIntoWhereClauseV2 } from '@/ui/object/object-filter-dropdown/utils/turnFiltersIntoWhereClauseV2';
import { parseDate } from '~/utils/date-utils';
export const useTasks = (entity?: ActivityTargetableEntity) => {
@ -22,10 +21,10 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
},
}
: Object.assign({}, turnFilterIntoWhereClause(selectedFilter));
: Object.assign({}, turnFiltersIntoWhereClauseV2([], []));
const { objects: completeTasksData } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
skip: !entity && !selectedFilter,
filter: {
type: { equals: 'Task' },
@ -34,13 +33,13 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
orderBy: [
{
createdAt: SortOrder.Desc,
createdAt: 'AscNullIsFirst',
},
],
});
const { objects: incompleteTaskData } = useFindManyObjectRecords({
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
skip: !entity && !selectedFilter,
filter: {
type: { equals: 'Task' },
@ -49,7 +48,7 @@ export const useTasks = (entity?: ActivityTargetableEntity) => {
},
orderBy: [
{
createdAt: SortOrder.Desc,
createdAt: 'DescNullIsFirst',
},
],
});

View File

@ -49,7 +49,7 @@ const StyledEmptyTimelineSubTitle = styled.div`
export const Timeline = ({ entity }: { entity: ActivityTargetableEntity }) => {
const { objects: activityTargets, loading } = useFindManyObjectRecords({
objectNamePlural: 'activityTargetsV2',
objectNamePlural: 'activityTargets',
filter: {
or: {
companyId: { eq: entity.id },
@ -60,7 +60,7 @@ export const Timeline = ({ entity }: { entity: ActivityTargetableEntity }) => {
const { objects: activities } = useFindManyObjectRecords({
skip: !activityTargets?.length,
objectNamePlural: 'activitiesV2',
objectNamePlural: 'activities',
filter: {
activityTargets: { in: activityTargets?.map((at) => at.id) },
},

View File

@ -1,5 +1,6 @@
import { Activity } from '@/activities/types/Activity';
import { Company, Person } from '~/generated-metadata/graphql';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
export type ActivityTarget = {
id: string;
@ -8,7 +9,7 @@ export type ActivityTarget = {
companyId: string | null;
personId: string | null;
activity: Pick<Activity, 'id' | 'createdAt' | 'updatedAt'>;
person?: Pick<Person, 'id' | 'firstName' | 'lastName' | 'avatarUrl'> | null;
person?: Pick<Person, 'id' | 'name' | 'avatarUrl'> | null;
company?: Pick<Company, 'id' | 'name' | 'domainName'> | null;
[key: string]: any;
};

View File

@ -7,7 +7,6 @@ import { tokenPairState } from '@/auth/states/tokenPairState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
import { AppPath } from '@/types/AppPath';
import { REACT_APP_SERVER_BASE_URL } from '~/config';
import { ActivityTarget } from '~/generated/graphql';
import { useIsMatchingLocation } from '~/hooks/useIsMatchingLocation';
import { useUpdateEffect } from '~/hooks/useUpdateEffect';
@ -25,23 +24,7 @@ export const useApolloFactory = () => {
const apolloClient = useMemo(() => {
apolloRef.current = new ApolloFactory({
uri: `${REACT_APP_SERVER_BASE_URL}/graphql`,
cache: new InMemoryCache({
typePolicies: {
Activity: {
fields: {
activityTargets: {
merge: (
_existing: ActivityTarget[] = [],
incoming: ActivityTarget[],
) => {
return [...incoming];
},
},
},
},
ViewField: { keyFields: ['viewId', 'key'] },
},
}),
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'cache-first',

View File

@ -7,19 +7,11 @@ import {
import { isNonEmptyArray } from '@sniptt/guards';
import { useRecoilCallback } from 'recoil';
import { GET_COMPANIES } from '@/companies/graphql/queries/getCompanies';
import {
EMPTY_QUERY,
useFindOneObjectMetadataItem,
} from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { GET_PEOPLE } from '@/people/graphql/queries/getPeople';
import { GET_API_KEYS } from '@/settings/developers/graphql/queries/getApiKeys';
import {
GetApiKeysQuery,
GetCompaniesQuery,
GetPeopleQuery,
} from '~/generated/graphql';
import { optimisticEffectState } from '../states/optimisticEffectState';
import { OptimisticEffect } from '../types/internal/OptimisticEffect';
@ -59,7 +51,6 @@ export const useOptimisticEffect = ({
newData,
query,
variables,
isUsingFlexibleBackend,
objectMetadataItem,
}: {
cache: ApolloCache<unknown>;
@ -69,7 +60,7 @@ export const useOptimisticEffect = ({
isUsingFlexibleBackend?: boolean;
objectMetadataItem?: ObjectMetadataItem;
}) => {
if (isUsingFlexibleBackend && objectMetadataItem) {
if (objectMetadataItem) {
const existingData = cache.readQuery({
query: findManyQuery,
variables,
@ -104,50 +95,6 @@ export const useOptimisticEffect = ({
if (!existingData) {
return;
}
if (query === GET_PEOPLE) {
cache.writeQuery({
query,
variables,
data: {
people: definition.resolver({
currentData: (existingData as GetPeopleQuery).people as T[],
newData: newData as T[],
variables,
}),
},
});
}
if (query === GET_COMPANIES) {
cache.writeQuery({
query,
variables,
data: {
companies: definition.resolver({
currentData: (existingData as GetCompaniesQuery)
.companies as T[],
newData: newData as T[],
variables,
}),
},
});
}
if (query === GET_API_KEYS) {
cache.writeQuery({
query,
variables,
data: {
findManyApiKey: definition.resolver({
currentData: (existingData as GetApiKeysQuery)
.findManyApiKey as T[],
newData: newData as T[],
variables,
}),
},
});
}
};
const optimisticEffect = {

View File

@ -0,0 +1,6 @@
export type Attachment = {
id: string;
createdAt: Date;
updatedAt: Date;
deletedAt: Date | null;
};

View File

@ -1,13 +0,0 @@
import { gql } from '@apollo/client';
export const USER_QUERY_FRAGMENT = gql`
fragment UserQueryFragment on User {
id
email
displayName
firstName
lastName
canImpersonate
supportUserHash
}
`;

View File

@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const GENERATE_ONE_API_KEY_TOKEN = gql`
mutation GenerateApiKeyToken($apiKeyId: String!, $expiresAt: String!) {
generateApiKeyToken(apiKeyId: $apiKeyId, expiresAt: $expiresAt) {
token
}
}
`;

View File

@ -10,13 +10,11 @@ import {
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { isVerifyPendingState } from '@/auth/states/isVerifyPendingState';
import { CREATE_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/mutation/createOneWorkspaceMember';
import { FIND_ONE_WORKSPACE_MEMBER_V2 } from '@/object-record/graphql/queries/findOneWorkspaceMember';
import { ColorScheme } from '@/workspace-member/types/WorkspaceMember';
import { REACT_APP_SERVER_AUTH_URL } from '~/config';
import {
useChallengeMutation,
useCheckUserExistsLazyQuery,
useGetCurrentWorkspaceLazyQuery,
useSignUpMutation,
useVerifyMutation,
} from '~/generated/graphql';
@ -38,7 +36,6 @@ export const useAuth = () => {
const [verify] = useVerifyMutation();
const [checkUserExistsQuery, { data: checkUserExistsData }] =
useCheckUserExistsLazyQuery();
const [getCurrentWorkspaceQuery] = useGetCurrentWorkspaceLazyQuery();
const client = useApolloClient();
@ -81,31 +78,26 @@ export const useAuth = () => {
}
setTokenPair(verifyResult.data?.verify.tokens);
const workspaceMember = await client.query({
query: FIND_ONE_WORKSPACE_MEMBER_V2,
variables: {
filter: {
userId: { eq: verifyResult.data?.verify.user.id },
},
},
});
const currentWorkspace = await getCurrentWorkspaceQuery();
setCurrentUser(verifyResult.data?.verify.user);
setCurrentWorkspaceMember(workspaceMember.data?.findMany);
setCurrentWorkspace(currentWorkspace.data?.currentWorkspace ?? null);
const user = verifyResult.data?.verify.user;
const workspaceMember = {
...user.workspaceMember,
colorScheme: user.workspaceMember?.colorScheme as ColorScheme,
};
const workspace = user.defaultWorkspace ?? null;
setCurrentUser(user);
setCurrentWorkspaceMember(workspaceMember);
setCurrentWorkspace(workspace);
return {
user: verifyResult.data?.verify.user,
workspaceMember: workspaceMember.data?.findMany,
workspace: currentWorkspace.data?.currentWorkspace,
user,
workspaceMember,
workspace,
tokens: verifyResult.data?.verify.tokens,
};
},
[
verify,
setTokenPair,
client,
getCurrentWorkspaceQuery,
setCurrentUser,
setCurrentWorkspaceMember,
setCurrentWorkspace,
@ -161,38 +153,15 @@ export const useAuth = () => {
throw new Error('No login token');
}
const { user, workspace } = await handleVerify(
const { user, workspace, workspaceMember } = await handleVerify(
signUpResult.data?.signUp.loginToken.token,
);
const workspaceMember = await client.mutate({
mutation: CREATE_ONE_WORKSPACE_MEMBER_V2,
variables: {
input: {
name: {
firstName: user.firstName ?? '',
lastName: user.lastName ?? '',
},
colorScheme: 'Light',
userId: user.id,
allowImpersonation: true,
locale: 'en',
},
},
});
setCurrentWorkspaceMember(workspaceMember.data?.createWorkspaceMemberV2);
setIsVerifyPendingState(false);
return { user, workspaceMember, workspace };
},
[
setIsVerifyPendingState,
signUp,
handleVerify,
client,
setCurrentWorkspaceMember,
],
[setIsVerifyPendingState, signUp, handleVerify],
);
const handleGoogleLogin = useCallback((workspaceInviteHash?: string) => {

View File

@ -1,5 +1,5 @@
import { useEffect, useState } from 'react';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { authProvidersState } from '@/client-config/states/authProvidersState';
import { isDebugModeState } from '@/client-config/states/isDebugModeState';
@ -11,13 +11,12 @@ import { useGetClientConfigQuery } from '~/generated/graphql';
export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
children,
}) => {
const [, setAuthProviders] = useRecoilState(authProvidersState);
const [, setIsDebugMode] = useRecoilState(isDebugModeState);
const setAuthProviders = useSetRecoilState(authProvidersState);
const setIsDebugMode = useSetRecoilState(isDebugModeState);
const [, setIsSignInPrefilled] = useRecoilState(isSignInPrefilledState);
const setIsSignInPrefilled = useSetRecoilState(isSignInPrefilledState);
const [, setTelemetry] = useRecoilState(telemetryState);
const [setIsLoading] = useState(true);
const setTelemetry = useSetRecoilState(telemetryState);
const setSupportChat = useSetRecoilState(supportChatState);
const { data, loading } = useGetClientConfigQuery();
@ -41,10 +40,8 @@ export const ClientConfigProvider: React.FC<React.PropsWithChildren> = ({
setIsDebugMode,
setIsSignInPrefilled,
setTelemetry,
setIsLoading,
loading,
setSupportChat,
]);
return <>{children}</>;
return loading ? <></> : <>{children}</>;
};

View File

@ -2,17 +2,8 @@ import { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useOpenActivityRightDrawer } from '@/activities/hooks/useOpenActivityRightDrawer';
import { IconNotes } from '@/ui/display/icon';
import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
import { AppHotkeyScope } from '@/ui/utilities/hotkey/types/AppHotkeyScope';
import { Avatar } from '@/users/components/Avatar';
import {
QueryMode,
useSearchActivityQuery,
useSearchCompanyQuery,
useSearchPeopleQuery,
} from '~/generated/graphql';
import { getLogoUrlFromDomainName } from '~/utils';
import { useCommandMenu } from '../hooks/useCommandMenu';
import { commandMenuCommandsState } from '../states/commandMenuCommandsState';
@ -45,47 +36,47 @@ export const CommandMenu = () => {
[openCommandMenu, setSearch],
);
const { data: peopleData } = useSearchPeopleQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [
{ firstName: { contains: search, mode: QueryMode.Insensitive } },
{ lastName: { contains: search, mode: QueryMode.Insensitive } },
],
},
limit: 3,
},
});
// const { data: peopleData } = useSearchPeopleQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [
// { firstName: { contains: search, mode: QueryMode.Insensitive } },
// { lastName: { contains: search, mode: QueryMode.Insensitive } },
// ],
// },
// limit: 3,
// },
// });
const people = peopleData?.searchResults ?? [];
// const people = peopleData?.searchResults ?? [];
const { data: companyData } = useSearchCompanyQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [{ name: { contains: search, mode: QueryMode.Insensitive } }],
},
limit: 3,
},
});
// const { data: companyData } = useSearchCompanyQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [{ name: { contains: search, mode: QueryMode.Insensitive } }],
// },
// limit: 3,
// },
// });
const companies = companyData?.searchResults ?? [];
// const companies = companyData?.searchResults ?? [];
const { data: activityData } = useSearchActivityQuery({
skip: !isCommandMenuOpened,
variables: {
where: {
OR: [
{ title: { contains: search, mode: QueryMode.Insensitive } },
{ body: { contains: search, mode: QueryMode.Insensitive } },
],
},
limit: 3,
},
});
// const { data: activityData } = useSearchActivityQuery({
// skip: !isCommandMenuOpened,
// variables: {
// where: {
// OR: [
// { title: { contains: search, mode: QueryMode.Insensitive } },
// { body: { contains: search, mode: QueryMode.Insensitive } },
// ],
// },
// limit: 3,
// },
// });
const activities = activityData?.searchResults ?? [];
// const activities = activityData?.searchResults ?? [];
const checkInShortcuts = (cmd: Command, search: string) => {
return (cmd.firstHotKey + (cmd.secondHotKey ?? ''))
@ -158,7 +149,7 @@ export const CommandMenu = () => {
/>
))}
</CommandGroup>
<CommandGroup heading="People">
{/* <CommandGroup heading="People">
{people.map((person) => (
<CommandMenuItem
key={person.id}
@ -200,7 +191,7 @@ export const CommandMenu = () => {
onClick={() => openActivityRightDrawer(activity.id)}
/>
))}
</CommandGroup>
</CommandGroup> */}
</StyledList>
</StyledDialog>
);

View File

@ -10,7 +10,7 @@ import { Command, CommandType } from '../types/Command';
export const commandMenuCommands: Command[] = [
{
to: '/people',
to: '/objects/people',
label: 'Go to People',
type: CommandType.Navigate,
firstHotKey: 'G',
@ -18,7 +18,7 @@ export const commandMenuCommands: Command[] = [
Icon: IconUser,
},
{
to: '/companies',
to: '/objects/companies',
label: 'Go to Companies',
type: CommandType.Navigate,
firstHotKey: 'G',
@ -26,7 +26,7 @@ export const commandMenuCommands: Command[] = [
Icon: IconBuildingSkyscraper,
},
{
to: '/opportunities',
to: '/objects/opportunities',
label: 'Go to Opportunities',
type: CommandType.Navigate,
firstHotKey: 'G',

View File

@ -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 = {};

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ export const CompanyChip = ({
}: CompanyChipProps) => (
<EntityChip
entityId={id}
linkToEntity={`/companies/${id}`}
linkToEntity={`/objects/companies/${id}`}
name={name}
avatarType="squared"
pictureUrl={pictureUrl}

View File

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

View File

@ -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}`}

View File

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

View File

@ -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 || ''),

View File

@ -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(() => {

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_MANY_COMPANY = gql`
mutation InsertManyCompany($data: [CompanyCreateManyInput!]!) {
createManyCompany(data: $data) {
count
}
}
`;

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_ONE_COMPANY = gql`
mutation InsertOneCompany($data: CompanyCreateInput!) {
createOneCompany(data: $data) {
...companyFieldsFragment
}
}
`;

View File

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

View File

@ -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];
},
};

View File

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

View File

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

View File

@ -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);
},
});
};

View File

@ -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(),
},
]);
}),
};
};

View File

@ -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],
);
};

View File

@ -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],
);
};

View File

@ -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,
});
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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',
},
];

View 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;
};

View File

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

View File

@ -18,7 +18,7 @@ const StyledContainer = styled.div`
export const Favorites = () => {
// This is only temporary and will be refactored once we have main identifiers
const { favorites, handleReorderFavorite } = useFavorites({
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
});
if (!favorites || favorites.length === 0) return <></>;

View File

@ -1,9 +0,0 @@
import { gql } from '@apollo/client';
export const DELETE_FAVORITE = gql`
mutation DeleteFavorite($where: FavoriteWhereInput!) {
deleteFavorite(where: $where) {
id
}
}
`;

View File

@ -1,14 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_COMPANY_FAVORITE = gql`
mutation InsertCompanyFavorite($data: FavoriteMutationForCompanyArgs!) {
createFavoriteForCompany(data: $data) {
id
company {
id
name
domainName
}
}
}
`;

View File

@ -1,15 +0,0 @@
import { gql } from '@apollo/client';
export const INSERT_PERSON_FAVORITE = gql`
mutation InsertPersonFavorite($data: FavoriteMutationForPersonArgs!) {
createFavoriteForPerson(data: $data) {
id
person {
id
firstName
lastName
displayName
}
}
}
`;

View File

@ -1,28 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_FAVORITE = gql`
mutation UpdateOneFavorite(
$data: FavoriteUpdateInput!
$where: FavoriteWhereUniqueInput!
) {
updateOneFavorites(data: $data, where: $where) {
id
person {
id
firstName
lastName
avatarUrl
}
company {
id
name
domainName
accountOwner {
id
displayName
avatarUrl
}
}
}
}
`;

View File

@ -1,26 +0,0 @@
import { gql } from '@apollo/client';
export const GET_FAVORITES = gql`
query GetFavorites {
findFavorites {
id
position
person {
id
firstName
lastName
avatarUrl
}
company {
id
name
domainName
accountOwner {
id
displayName
avatarUrl
}
}
}
}
`;

View File

@ -4,12 +4,12 @@ import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { Company } from '@/companies/types/Company';
import { Favorite } from '@/favorites/types/Favorite';
import { mapFavorites } from '@/favorites/utils/mapFavorites';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useFindManyObjectRecords } from '@/object-record/hooks/useFindManyObjectRecords';
import { PaginatedObjectTypeResults } from '@/object-record/types/PaginatedObjectTypeResults';
import { Company } from '~/generated/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { favoritesState } from '../states/favoritesState';
@ -25,7 +25,7 @@ export const useFavorites = ({
const { updateOneMutation, createOneMutation, deleteOneMutation } =
useFindOneObjectMetadataItem({
objectNamePlural: 'favoritesV2',
objectNamePlural: 'favorites',
});
const { foundObjectMetadataItem: favoriteTargetObjectMetadataItem } =
@ -44,7 +44,7 @@ export const useFavorites = ({
// This is only temporary and will be refactored once we have main identifiers
const { loading: companiesLoading } = useFindManyObjectRecords({
objectNamePlural: 'companiesV2',
objectNamePlural: 'companies',
onCompleted: async (
data: PaginatedObjectTypeResults<Required<Company>>,
) => {
@ -64,7 +64,7 @@ export const useFavorites = ({
});
const { loading: peopleLoading } = useFindManyObjectRecords({
objectNamePlural: 'peopleV2',
objectNamePlural: 'people',
onCompleted: async (data) => {
setAllPeople(
data.edges.reduce(
@ -84,7 +84,7 @@ export const useFavorites = ({
useFindManyObjectRecords({
skip: companiesLoading || peopleLoading,
objectNamePlural: 'favoritesV2',
objectNamePlural: 'favorites',
onCompleted: useRecoilCallback(
({ snapshot, set }) =>
async (data: PaginatedObjectTypeResults<Required<Favorite>>) => {
@ -112,8 +112,7 @@ export const useFavorites = ({
const favorites = snapshot.getLoadable(favoritesState).getValue();
const targetObjectName =
favoriteTargetObjectMetadataItem?.nameSingular.replace('V2', '') ??
'';
favoriteTargetObjectMetadataItem?.nameSingular ?? '';
const result = await apolloClient.mutate({
mutation: createOneMutation,

View File

@ -1,4 +1,5 @@
import { Company, Person } from '~/generated/graphql';
import { Company } from '@/companies/types/Company';
import { Person } from '@/people/types/Person';
import { getLogoUrlFromDomainName } from '~/utils';
import { assertNotNull } from '~/utils/assert';
import { isDefined } from '~/utils/isDefined';
@ -7,8 +8,8 @@ export const mapFavorites = (
favorites: any,
recordsDict: {
[key: string]: {
firstName?: Person['firstName'];
lastName?: Person['lastName'];
firstName?: Person['name']['firstName'];
lastName?: Person['name']['lastName'];
avatarUrl?: Person['avatarUrl'];
name?: Company['name'];
domainName?: Company['domainName'];

View File

@ -0,0 +1,9 @@
import { useFindManyObjectMetadataItems } from '@/object-metadata/hooks/useFindManyObjectMetadataItems';
export const ObjectMetadataItemsProvider = ({
children,
}: React.PropsWithChildren) => {
const { loading } = useFindManyObjectMetadataItems();
return loading ? <></> : <>{children}</>;
};

View File

@ -1,28 +1,19 @@
import { useNavigate } from 'react-router-dom';
import { useObjectMetadataItemForSettings } from '@/object-metadata/hooks/useObjectMetadataItemForSettings';
import { Icon123 } from '@/ui/input/constants/icons';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import NavItem from '@/ui/navigation/navbar/components/NavItem';
import { useFindManyObjectMetadataItems } from '../hooks/useFindManyObjectMetadataItems';
export const ObjectMetadataNavItems = () => {
const { objectMetadataItems } = useFindManyObjectMetadataItems({
objectFilter: {
isSystem: { is: false },
},
fieldFilter: {
isSystem: { is: false },
},
});
const { activeObjectMetadataItems } = useObjectMetadataItemForSettings();
const navigate = useNavigate();
const { icons } = useLazyLoadIcons();
return (
<>
{objectMetadataItems.map((objectMetadataItem) => {
if (objectMetadataItem.nameSingular === 'opportunityV2') return null;
{activeObjectMetadataItems.map((objectMetadataItem) => {
if (objectMetadataItem.nameSingular === 'opportunity') return null;
return (
<NavItem
key={objectMetadataItem.id}

View File

@ -0,0 +1,46 @@
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
import { formatFieldMetadataItemsAsFilterDefinitions } from '../utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldMetadataItemsAsSortDefinitions';
export const useComputeDefinitionsFromFieldMetadata = (
objectMetadataItem?: ObjectMetadataItem,
) => {
if (!objectMetadataItem) {
return {
columnDefinitions: [],
filterDefinitions: [],
sortDefinitions: [],
};
}
const activeFieldMetadataItems = objectMetadataItem.fields.filter(
({ isActive }) => isActive,
);
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
activeFieldMetadataItems.map((field, index) =>
formatFieldMetadataItemAsColumnDefinition({
position: index,
field,
objectMetadataItem: objectMetadataItem,
}),
);
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: activeFieldMetadataItems,
});
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
fields: activeFieldMetadataItems,
});
return {
columnDefinitions,
filterDefinitions,
sortDefinitions,
};
};

View File

@ -5,14 +5,8 @@ import { useGenerateDeleteOneObjectMutation } from '@/object-record/utils/useGen
import { useGenerateFindManyCustomObjectsQuery } from '@/object-record/utils/useGenerateFindManyCustomObjectsQuery';
import { useGenerateFindOneCustomObjectQuery } from '@/object-record/utils/useGenerateFindOneCustomObjectQuery';
import { useGenerateUpdateOneObjectMutation } from '@/object-record/utils/useGenerateUpdateOneObjectMutation';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { ObjectMetadataItemIdentifier } from '../types/ObjectMetadataItemIdentifier';
import { formatFieldMetadataItemAsColumnDefinition } from '../utils/formatFieldMetadataItemAsColumnDefinition';
import { formatFieldMetadataItemsAsFilterDefinitions } from '../utils/formatFieldMetadataItemsAsFilterDefinitions';
import { formatFieldMetadataItemsAsSortDefinitions } from '../utils/formatFieldMetadataItemsAsSortDefinitions';
import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems';
@ -43,37 +37,10 @@ export const useFindOneObjectMetadataItem = ({
object.nameSingular === objectNameSingular,
);
const { icons } = useLazyLoadIcons();
const objectNotFoundInMetadata =
objectMetadataItems.length === 0 ||
(objectMetadataItems.length > 0 && !foundObjectMetadataItem);
const activeFields =
foundObjectMetadataItem?.fields.filter(({ isActive }) => isActive) ?? [];
const columnDefinitions: ColumnDefinition<FieldMetadata>[] =
foundObjectMetadataItem
? activeFields.map((field, index) =>
formatFieldMetadataItemAsColumnDefinition({
position: index,
field,
objectMetadataItem: foundObjectMetadataItem,
icons,
}),
)
: [];
const filterDefinitions = formatFieldMetadataItemsAsFilterDefinitions({
fields: activeFields,
icons,
});
const sortDefinitions = formatFieldMetadataItemsAsSortDefinitions({
fields: activeFields,
icons,
});
const findManyQuery = useGenerateFindManyCustomObjectsQuery({
objectMetadataItem: foundObjectMetadataItem,
});
@ -97,9 +64,6 @@ export const useFindOneObjectMetadataItem = ({
return {
foundObjectMetadataItem,
objectNotFoundInMetadata,
columnDefinitions,
filterDefinitions,
sortDefinitions,
findManyQuery,
findOneQuery,
createOneMutation,

View File

@ -8,20 +8,13 @@ import { useFindManyObjectMetadataItems } from './useFindManyObjectMetadataItems
import { useUpdateOneObjectMetadataItem } from './useUpdateOneObjectMetadataItem';
export const useObjectMetadataItemForSettings = () => {
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems({
objectFilter: {
isSystem: { is: false },
},
fieldFilter: {
isSystem: { is: false },
},
});
const { objectMetadataItems, loading } = useFindManyObjectMetadataItems();
const activeObjectMetadataItems = objectMetadataItems.filter(
({ isActive }) => isActive,
({ isActive, isSystem }) => isActive && !isSystem,
);
const disabledObjectMetadataItems = objectMetadataItems.filter(
({ isActive }) => !isActive,
({ isActive, isSystem }) => !isActive && !isSystem,
);
const findActiveObjectMetadataItemBySlug = (slug: string) =>

View File

@ -1,11 +0,0 @@
import { atom } from 'recoil';
import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
/**
* @deprecated Use `useFindManyObjectMetadataItems` instead.
*/
export const objectMetadataItemsState = atom<ObjectMetadataItem[]>({
key: 'objectMetadataItemsState',
default: [],
});

View File

@ -1,12 +0,0 @@
import { selector } from 'recoil';
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
import { objectMetadataItemsState } from '../objectMetadataItemsState';
export const activeObjectMetadataItemsSelector = selector<ObjectMetadataItem[]>(
{
key: 'activeObjectMetadataItemsSelector',
get: ({ get }) =>
get(objectMetadataItemsState).filter(({ isActive }) => isActive),
},
);

View File

@ -1,12 +0,0 @@
import { selector } from 'recoil';
import { ObjectMetadataItem } from '../../types/ObjectMetadataItem';
import { objectMetadataItemsState } from '../objectMetadataItemsState';
export const disabledObjectMetadataItemsSelector = selector<
ObjectMetadataItem[]
>({
key: 'disabledObjectMetadataItemsSelector',
get: ({ get }) =>
get(objectMetadataItemsState).filter(({ isActive }) => !isActive),
});

View File

@ -1,5 +1,4 @@
import { parseFieldRelationType } from '@/object-metadata/utils/parseFieldRelationType';
import { IconComponent } from '@/ui/display/icon/types/IconComponent';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
@ -12,12 +11,10 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
position,
field,
objectMetadataItem,
icons,
}: {
position: number;
field: FieldMetadataItem;
objectMetadataItem: Omit<ObjectMetadataItem, 'fields'>;
icons: Record<string, IconComponent>;
}): ColumnDefinition<FieldMetadata> => ({
position,
fieldMetadataId: field.id,
@ -28,7 +25,7 @@ export const formatFieldMetadataItemAsColumnDefinition = ({
fieldName: field.name,
placeHolder: field.label,
},
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
isVisible: true,
basePathToShowPage: `/object/${objectMetadataItem.nameSingular}/`,
relationType: parseFieldRelationType(field),

View File

@ -5,10 +5,8 @@ import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
export const formatFieldMetadataItemsAsFilterDefinitions = ({
fields,
icons,
}: {
fields: Array<ObjectMetadataItem['fields'][0]>;
icons: Record<string, any>;
}): FilterDefinition[] =>
fields.reduce((acc, field) => {
if (
@ -20,22 +18,17 @@ export const formatFieldMetadataItemsAsFilterDefinitions = ({
) {
return acc;
}
return [
...acc,
formatFieldMetadataItemAsFilterDefinition({ field, icons }),
];
return [...acc, formatFieldMetadataItemAsFilterDefinition({ field })];
}, [] as FilterDefinition[]);
const formatFieldMetadataItemAsFilterDefinition = ({
field,
icons,
}: {
field: ObjectMetadataItem['fields'][0];
icons: Record<string, any>;
}): FilterDefinition => ({
fieldMetadataId: field.id,
label: field.label,
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
type:
field.type === FieldMetadataType.Date
? 'DATE'

View File

@ -5,10 +5,8 @@ import { ObjectMetadataItem } from '../types/ObjectMetadataItem';
export const formatFieldMetadataItemsAsSortDefinitions = ({
fields,
icons,
}: {
fields: Array<ObjectMetadataItem['fields'][0]>;
icons: Record<string, any>;
}): SortDefinition[] =>
fields.reduce((acc, field) => {
if (
@ -27,7 +25,7 @@ export const formatFieldMetadataItemsAsSortDefinitions = ({
{
fieldMetadataId: field.id,
label: field.label,
Icon: icons[field.icon ?? 'Icon123'],
iconName: field.icon ?? 'Icon123',
},
];
}, [] as SortDefinition[]);

View File

@ -7,7 +7,6 @@ import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOne
import { formatFieldMetadataItemAsColumnDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsColumnDefinition';
import { filterAvailableFieldMetadataItem } from '@/object-record/utils/filterAvailableFieldMetadataItem';
import { IconBuildingSkyscraper } from '@/ui/display/icon';
import { useLazyLoadIcons } from '@/ui/input/hooks/useLazyLoadIcons';
import { PageBody } from '@/ui/layout/page/PageBody';
import { PageContainer } from '@/ui/layout/page/PageContainer';
import { PageFavoriteButton } from '@/ui/layout/page/PageFavoriteButton';
@ -36,8 +35,6 @@ export const RecordShowPage = () => {
objectMetadataId: string;
}>();
const { icons } = useLazyLoadIcons();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNameSingular,
});
@ -91,7 +88,7 @@ export const RecordShowPage = () => {
if (isFavorite) deleteFavorite(object?.id);
else {
const additionalData =
objectNameSingular === 'personV2'
objectNameSingular === 'person'
? {
labelIdentifier:
object.name.firstName + ' ' + object.name.lastName,
@ -100,7 +97,7 @@ export const RecordShowPage = () => {
link: `/object/personV2/${object.id}`,
recordId: object.id,
}
: objectNameSingular === 'companyV2'
: objectNameSingular === 'company'
? {
labelIdentifier: object.name,
avatarUrl: getLogoUrlFromDomainName(object.domainName ?? ''),
@ -116,7 +113,7 @@ export const RecordShowPage = () => {
if (!object) return <></>;
const pageName =
objectNameSingular === 'personV2'
objectNameSingular === 'person'
? object.name.firstName + ' ' + object.name.lastName
: object.name;
@ -173,7 +170,6 @@ export const RecordShowPage = () => {
field: metadataField,
position: index,
objectMetadataItem: foundObjectMetadataItem,
icons,
}),
useUpdateEntityMutation: useUpdateOneObjectMutation,
hotkeyScope: InlineCellHotkeyScope.InlineCell,

View File

@ -1,6 +1,7 @@
import styled from '@emotion/styled';
import { useRecoilCallback } from 'recoil';
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { RecordTable } from '@/ui/object/record-table/components/RecordTable';
import { TableOptionsDropdownId } from '@/ui/object/record-table/constants/TableOptionsDropdownId';
@ -9,7 +10,6 @@ import { TableOptionsDropdown } from '@/ui/object/record-table/options/component
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';
@ -32,10 +32,12 @@ export const RecordTableContainer = ({
}: {
objectNamePlural: string;
}) => {
const { columnDefinitions, foundObjectMetadataItem } =
useFindOneObjectMetadataItem({
objectNamePlural,
});
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { columnDefinitions } = useComputeDefinitionsFromFieldMetadata(
foundObjectMetadataItem,
);
const { updateOneObject } = useUpdateOneObjectRecord({
objectNameSingular: foundObjectMetadataItem?.nameSingular,
@ -50,8 +52,6 @@ export const RecordTableContainer = ({
recordTableScopeId: tableScopeId,
});
const { setEntityCountInCurrentView } = useView({ viewScopeId });
const updateEntity = ({
variables,
}: {
@ -89,9 +89,6 @@ export const RecordTableContainer = ({
onColumnsChange={useRecoilCallback(() => (columns) => {
persistViewFields(mapColumnDefinitionsToViewFields(columns));
})}
onEntityCountChange={(entityCount) => {
setEntityCountInCurrentView(entityCount);
}}
>
<ViewBar
optionsDropdownButton={<TableOptionsDropdown />}

View File

@ -1,5 +1,6 @@
import { useEffect } from 'react';
import { useComputeDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/useComputeDefinitionsFromFieldMetadata';
import { useFindOneObjectMetadataItem } from '@/object-metadata/hooks/useFindOneObjectMetadataItem';
import { useRecordTableContextMenuEntries } from '@/object-record/hooks/useRecordTableContextMenuEntries';
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
@ -8,24 +9,26 @@ import { useView } from '@/views/hooks/useView';
import { ViewType } from '@/views/types/ViewType';
export const RecordTableEffect = () => {
const { scopeId: objectNamePlural, setAvailableTableColumns } =
useRecordTable();
const {
columnDefinitions,
filterDefinitions,
sortDefinitions,
foundObjectMetadataItem,
} = useFindOneObjectMetadataItem({
scopeId: objectNamePlural,
setAvailableTableColumns,
setOnEntityCountChange,
} = useRecordTable();
const { foundObjectMetadataItem } = useFindOneObjectMetadataItem({
objectNamePlural,
});
const { columnDefinitions, filterDefinitions, sortDefinitions } =
useComputeDefinitionsFromFieldMetadata(foundObjectMetadataItem);
const {
setAvailableSortDefinitions,
setAvailableFilterDefinitions,
setAvailableFieldDefinitions,
setViewType,
setViewObjectMetadataId,
setEntityCountInCurrentView,
} = useView();
useEffect(() => {
@ -65,5 +68,11 @@ export const RecordTableEffect = () => {
setContextMenuEntries?.();
}, [setActionBarEntries, setContextMenuEntries]);
useEffect(() => {
setOnEntityCountChange(
() => (entityCount: number) => setEntityCountInCurrentView(entityCount),
);
}, [setEntityCountInCurrentView, setOnEntityCountChange]);
return <></>;
};

View File

@ -19,6 +19,7 @@ import { RecordTableContainer } from './RecordTableContainer';
const StyledTableContainer = styled.div`
display: flex;
height: 100%;
width: 100%;
`;
@ -48,7 +49,8 @@ export const RecordTablePage = () => {
});
const handleAddButtonClick = async () => {
createOneObject?.({});
const createdObject = await createOneObject?.({});
console.log(createdObject);
};
return (

View File

@ -1,8 +1,8 @@
import { gql } from '@apollo/client';
export const CREATE_ONE_WORKSPACE_MEMBER_V2 = gql`
mutation CreateOneWorkspaceMemberV2($input: WorkspaceMemberV2CreateInput!) {
createWorkspaceMemberV2(data: $input) {
mutation CreateOneWorkspaceMember($input: WorkspaceMemberCreateInput!) {
createWorkspaceMember(data: $input) {
id
name {
firstName

View File

@ -23,6 +23,17 @@ export const getRecordOptimisticEffectDefinition = ({
const newRecordPaginatedCacheField = produce<
PaginatedObjectTypeResults<any>
>(currentData as PaginatedObjectTypeResults<any>, (draft) => {
if (!draft) {
return {
edges: [{ node: newData, cursor: '' }],
pageInfo: {
endCursor: '',
hasNextPage: false,
hasPreviousPage: false,
startCursor: '',
},
};
}
draft.edges.unshift({ node: newData, cursor: '' });
});

Some files were not shown because too many files have changed in this diff Show More