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