diff --git a/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx b/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx
index 7bc7e9f444..20ec286b76 100644
--- a/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx
+++ b/front/src/modules/ui/table/options/components/TableViewsDropdownButton.tsx
@@ -96,7 +96,7 @@ export const TableViewsDropdownButton = ({
const handleViewSelect = useRecoilCallback(
({ set, snapshot }) =>
- async (viewId?: string) => {
+ async (viewId: string) => {
const savedColumns = await snapshot.getPromise(
savedTableColumnsScopedState(viewId),
);
@@ -164,7 +164,7 @@ export const TableViewsDropdownButton = ({
{currentView?.name || defaultViewName}{' '}
- · {views.length + 1}
+ · {views.length}
>
}
@@ -175,10 +175,6 @@ export const TableViewsDropdownButton = ({
HotkeyScope={HotkeyScope}
>
- handleViewSelect(undefined)}>
-
- {defaultViewName}
-
{views.map((view) => (
[],
viewId = currentViewId,
) => {
- if (!columns.length) return;
+ if (!viewId || !columns.length) return;
return createViewFieldsMutation({
variables: {
@@ -84,7 +84,7 @@ export const useTableViewFields = ({
const updateViewFields = useCallback(
(columns: ViewFieldDefinition[]) => {
- if (!columns.length) return;
+ if (!currentViewId || !columns.length) return;
return Promise.all(
columns.map((column) =>
@@ -100,10 +100,11 @@ export const useTableViewFields = ({
),
);
},
- [updateViewFieldMutation],
+ [currentViewId, updateViewFieldMutation],
);
const { refetch } = useGetViewFieldsQuery({
+ skip: !currentViewId,
variables: {
orderBy: { index: SortOrder.Asc },
where: {
@@ -139,6 +140,8 @@ export const useTableViewFields = ({
});
const persistColumns = useCallback(async () => {
+ if (!currentViewId) return;
+
const viewFieldsToUpdate = columns.filter(
(column) =>
savedColumnsById[column.id] &&
@@ -148,7 +151,7 @@ export const useTableViewFields = ({
await updateViewFields(viewFieldsToUpdate);
return refetch();
- }, [columns, refetch, savedColumnsById, updateViewFields]);
+ }, [columns, currentViewId, refetch, savedColumnsById, updateViewFields]);
return { createViewFields, persistColumns };
};
diff --git a/front/src/modules/views/hooks/useViews.ts b/front/src/modules/views/hooks/useViews.ts
index 8b4a62e8fd..358312e7cd 100644
--- a/front/src/modules/views/hooks/useViews.ts
+++ b/front/src/modules/views/hooks/useViews.ts
@@ -2,6 +2,7 @@ import { useCallback } from 'react';
import { TableRecoilScopeContext } from '@/ui/table/states/recoil-scope-contexts/TableRecoilScopeContext';
import {
+ currentTableViewIdState,
type TableView,
tableViewsByIdState,
tableViewsState,
@@ -24,6 +25,10 @@ export const useViews = ({
objectId: 'company' | 'person';
onViewCreate: (viewId: string) => Promise;
}) => {
+ const [currentViewId, setCurrentViewId] = useRecoilScopedState(
+ currentTableViewIdState,
+ TableRecoilScopeContext,
+ );
const [views, setViews] = useRecoilScopedState(
tableViewsState,
TableRecoilScopeContext,
@@ -102,6 +107,8 @@ export const useViews = ({
}));
if (!isDeeplyEqual(views, nextViews)) setViews(nextViews);
+
+ if (nextViews.length && !currentViewId) setCurrentViewId(nextViews[0].id);
},
});
diff --git a/server/src/core/view/seed-data/views.json b/server/src/core/view/seed-data/views.json
new file mode 100644
index 0000000000..8f61b2498a
--- /dev/null
+++ b/server/src/core/view/seed-data/views.json
@@ -0,0 +1,111 @@
+[
+ {
+ "name": "All Companies",
+ "objectId": "company",
+ "type": "Table",
+ "fields": [
+ {
+ "fieldName": "Name",
+ "sizeInPx": 180,
+ "isVisible": true
+ },
+ {
+ "fieldName": "URL",
+ "sizeInPx": 100,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Account Owner",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Creation",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Employees",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "LinkedIn",
+ "sizeInPx": 170,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Address",
+ "sizeInPx": 170,
+ "isVisible": true
+ },
+ {
+ "fieldName": "ICP",
+ "sizeInPx": 150,
+ "isVisible": false
+ },
+ {
+ "fieldName": "ARR",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Twitter",
+ "sizeInPx": 150,
+ "isVisible": false
+ }
+ ]
+ },
+ {
+ "name": "All People",
+ "objectId": "person",
+ "type": "Table",
+ "fields": [
+ {
+ "fieldName": "People",
+ "sizeInPx": 210,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Email",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Company",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Phone",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Creation",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "City",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Job title",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "LinkedIn",
+ "sizeInPx": 150,
+ "isVisible": true
+ },
+ {
+ "fieldName": "Twitter",
+ "sizeInPx": 150,
+ "isVisible": true
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/server/src/core/view/services/view.service.ts b/server/src/core/view/services/view.service.ts
index cdd0f3d44f..bf4b2c0a92 100644
--- a/server/src/core/view/services/view.service.ts
+++ b/server/src/core/view/services/view.service.ts
@@ -1,6 +1,9 @@
import { Injectable } from '@nestjs/common';
+import type { ViewType } from '@prisma/client';
+
import { PrismaService } from 'src/database/prisma.service';
+import seedViews from 'src/core/view/seed-data/views.json';
@Injectable()
export class ViewService {
@@ -36,4 +39,29 @@ export class ViewService {
// GroupBy
groupBy = this.prismaService.client.view.groupBy;
+
+ // Custom
+ createDefaultViews({ workspaceId }: { workspaceId: string }) {
+ return Promise.all(
+ seedViews.map(async ({ fields, ...viewInput }) => {
+ const view = await this.create({
+ data: {
+ ...viewInput,
+ type: viewInput.type as ViewType,
+ workspace: { connect: { id: workspaceId } },
+ },
+ });
+
+ await this.prismaService.client.viewField.createMany({
+ data: fields.map((viewField, index) => ({
+ ...viewField,
+ objectName: view.objectId,
+ index: index + 1,
+ viewId: view.id,
+ workspaceId,
+ })),
+ });
+ }),
+ );
+ }
}
diff --git a/server/src/core/view/view.module.ts b/server/src/core/view/view.module.ts
index 681d34fa1a..9cd8d16c3d 100644
--- a/server/src/core/view/view.module.ts
+++ b/server/src/core/view/view.module.ts
@@ -20,5 +20,6 @@ import { ViewFilterResolver } from './resolvers/view-filter.resolver';
ViewFilterResolver,
ViewSortResolver,
],
+ exports: [ViewService],
})
export class ViewModule {}
diff --git a/server/src/core/workspace/services/workspace.service.spec.ts b/server/src/core/workspace/services/workspace.service.spec.ts
index 9bb373cc59..1d6a760dac 100644
--- a/server/src/core/workspace/services/workspace.service.spec.ts
+++ b/server/src/core/workspace/services/workspace.service.spec.ts
@@ -7,6 +7,7 @@ import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage.
import { PersonService } from 'src/core/person/person.service';
import { CompanyService } from 'src/core/company/company.service';
import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
+import { ViewService } from 'src/core/view/services/view.service';
import { WorkspaceService } from './workspace.service';
@@ -41,6 +42,10 @@ describe('WorkspaceService', () => {
provide: PipelineProgressService,
useValue: {},
},
+ {
+ provide: ViewService,
+ useValue: {},
+ },
],
}).compile();
diff --git a/server/src/core/workspace/services/workspace.service.ts b/server/src/core/workspace/services/workspace.service.ts
index d706bcffd6..127fabb0e1 100644
--- a/server/src/core/workspace/services/workspace.service.ts
+++ b/server/src/core/workspace/services/workspace.service.ts
@@ -1,14 +1,15 @@
import { Injectable } from '@nestjs/common';
-import { v4 } from 'uuid';
import { Prisma } from '@prisma/client';
+import { v4 } from 'uuid';
-import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage.service';
-import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
-import { PipelineService } from 'src/core/pipeline/services/pipeline.service';
-import { PrismaService } from 'src/database/prisma.service';
import { CompanyService } from 'src/core/company/company.service';
import { PersonService } from 'src/core/person/person.service';
+import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
+import { PipelineStageService } from 'src/core/pipeline/services/pipeline-stage.service';
+import { PipelineService } from 'src/core/pipeline/services/pipeline.service';
+import { ViewService } from 'src/core/view/services/view.service';
+import { PrismaService } from 'src/database/prisma.service';
import { assert } from 'src/utils/assert';
@Injectable()
@@ -20,6 +21,7 @@ export class WorkspaceService {
private readonly personService: PersonService,
private readonly pipelineStageService: PipelineStageService,
private readonly pipelineProgressService: PipelineProgressService,
+ private readonly viewService: ViewService,
) {}
// Find
@@ -83,6 +85,11 @@ export class WorkspaceService {
workspaceId: workspace.id,
});
+ // Create default views
+ await this.viewService.createDefaultViews({
+ workspaceId: workspace.id,
+ });
+
return workspace;
}
diff --git a/server/src/core/workspace/workspace.module.ts b/server/src/core/workspace/workspace.module.ts
index 35b7bf957b..62a53d775e 100644
--- a/server/src/core/workspace/workspace.module.ts
+++ b/server/src/core/workspace/workspace.module.ts
@@ -4,6 +4,7 @@ import { FileUploadService } from 'src/core/file/services/file-upload.service';
import { PipelineModule } from 'src/core/pipeline/pipeline.module';
import { CompanyModule } from 'src/core/company/company.module';
import { PersonModule } from 'src/core/person/person.module';
+import { ViewModule } from 'src/core/view/view.module';
import { WorkspaceService } from './services/workspace.service';
import { WorkspaceMemberService } from './services/workspace-member.service';
@@ -11,7 +12,7 @@ import { WorkspaceMemberResolver } from './resolvers/workspace-member.resolver';
import { WorkspaceResolver } from './resolvers/workspace.resolver';
@Module({
- imports: [PipelineModule, CompanyModule, PersonModule],
+ imports: [PipelineModule, CompanyModule, PersonModule, ViewModule],
providers: [
WorkspaceService,
FileUploadService,
diff --git a/server/src/database/seeds/index.ts b/server/src/database/seeds/index.ts
index d2a42cae31..1a905dc57b 100644
--- a/server/src/database/seeds/index.ts
+++ b/server/src/database/seeds/index.ts
@@ -6,6 +6,7 @@ import { seedPeople } from './people';
import { seedComments } from './comments';
import { seedUsers } from './users';
import { seedPipelines } from './pipelines';
+import { seedViews } from './views';
const seed = async () => {
const prisma = new PrismaClient();
@@ -15,6 +16,7 @@ const seed = async () => {
await seedPeople(prisma);
await seedComments(prisma);
await seedPipelines(prisma);
+ await seedViews(prisma);
await prisma.$disconnect();
};
diff --git a/server/src/database/seeds/views.ts b/server/src/database/seeds/views.ts
new file mode 100644
index 0000000000..f2ed1d28a5
--- /dev/null
+++ b/server/src/database/seeds/views.ts
@@ -0,0 +1,179 @@
+import { PrismaClient } from '@prisma/client';
+
+export const seedViews = async (prisma: PrismaClient) => {
+ const workspaceId = 'twenty-7ed9d212-1c25-4d02-bf25-6aeccf7ea419';
+ const companyViewId = 'twenty-5e924b69-a619-41bf-bd31-a9e8551fc9d1';
+ const personViewId = 'twenty-db9e6c85-c091-4fd6-88b1-c1830f5e90d1';
+
+ await prisma.view.upsert({
+ where: { id: companyViewId },
+ update: {},
+ create: {
+ id: companyViewId,
+ name: 'All Companies',
+ objectId: 'company',
+ type: 'Table',
+ workspaceId,
+ },
+ });
+
+ await Promise.all(
+ [
+ {
+ id: 'twenty-388833ba-1343-49d7-9092-065f92e0e5fa',
+ fieldName: 'Name',
+ sizeInPx: 180,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-fdbb7a60-18ac-4d52-83b8-399eb6055ec6',
+ fieldName: 'URL',
+ sizeInPx: 100,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-cc77beef-af99-4cd2-86dd-0230f8565ed5',
+ fieldName: 'Account Owner',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-28537b67-8b78-4885-903d-f749f34883b1',
+ fieldName: 'Creation',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-59e6624d-9a4d-492d-a0f2-52f51f69d004',
+ fieldName: 'Employees',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-2c4ee8b9-aacd-42dd-b422-22eca03aab5a',
+ fieldName: 'LinkedIn',
+ sizeInPx: 170,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-b83e299f-7098-4748-a39e-431cca2907ab',
+ fieldName: 'Address',
+ sizeInPx: 170,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-acef1246-8461-4e34-96b9-f326d598d655',
+ fieldName: 'ICP',
+ sizeInPx: 150,
+ isVisible: false,
+ },
+ {
+ id: 'twenty-971828c5-8167-4997-ae13-3b7895faa6f2',
+ fieldName: 'ARR',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-90977d8a-328d-4f69-98e8-8c69723c5a18',
+ fieldName: 'Twitter',
+ sizeInPx: 150,
+ isVisible: false,
+ },
+ ].map((viewField, index) =>
+ prisma.viewField.upsert({
+ where: { id: viewField.id },
+ update: {},
+ create: {
+ ...viewField,
+ index: index + 1,
+ objectName: 'company',
+ viewId: companyViewId,
+ workspaceId,
+ },
+ }),
+ ),
+ );
+
+ await prisma.view.upsert({
+ where: { id: personViewId },
+ update: {},
+ create: {
+ id: personViewId,
+ name: 'All People',
+ objectId: 'person',
+ type: 'Table',
+ workspaceId,
+ },
+ });
+
+ await Promise.all(
+ [
+ {
+ id: 'twenty-fc3461b4-661d-492e-8907-61004a41cca6',
+ fieldName: 'People',
+ sizeInPx: 210,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-4724d413-4343-4528-b8c4-4431910722f8',
+ fieldName: 'Email',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-fbb16b08-5a58-4a69-8bd0-a6d267994042',
+ fieldName: 'Company',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-1bad57bb-6627-40f8-8c75-bb5902892273',
+ fieldName: 'Phone',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-3544d797-740b-4e0b-8226-134bf38da256',
+ fieldName: 'Creation',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-4b6d48fb-17e2-4071-8565-d512f84656d5',
+ fieldName: 'City',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-418849cc-aa5c-4835-822b-c0dfb076106b',
+ fieldName: 'Job title',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-7591af5d-e081-4afa-94bb-09bd0e517850',
+ fieldName: 'LinkedIn',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ {
+ id: 'twenty-e7baeb3d-8ef3-4e61-89d6-60f64b1d52c5',
+ fieldName: 'Twitter',
+ sizeInPx: 150,
+ isVisible: true,
+ },
+ ].map((viewField, index) =>
+ prisma.viewField.upsert({
+ where: { id: viewField.id },
+ update: {},
+ create: {
+ ...viewField,
+ index: index + 1,
+ objectName: 'person',
+ viewId: personViewId,
+ workspaceId,
+ },
+ }),
+ ),
+ );
+};