mirror of
https://github.com/twentyhq/twenty.git
synced 2025-01-02 01:04:12 +03:00
feat: create default views on workspace creation + add views seed (#1313)
Closes #1311
This commit is contained in:
parent
8a3a176571
commit
209e8b64d9
@ -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 = ({
|
||||
<StyledViewIcon size={theme.icon.size.md} />
|
||||
{currentView?.name || defaultViewName}{' '}
|
||||
<StyledDropdownLabelAdornments>
|
||||
· {views.length + 1} <IconChevronDown size={theme.icon.size.sm} />
|
||||
· {views.length} <IconChevronDown size={theme.icon.size.sm} />
|
||||
</StyledDropdownLabelAdornments>
|
||||
</>
|
||||
}
|
||||
@ -175,10 +175,6 @@ export const TableViewsDropdownButton = ({
|
||||
HotkeyScope={HotkeyScope}
|
||||
>
|
||||
<StyledDropdownMenuItemsContainer>
|
||||
<DropdownMenuItem onClick={() => handleViewSelect(undefined)}>
|
||||
<IconList size={theme.icon.size.md} />
|
||||
{defaultViewName}
|
||||
</DropdownMenuItem>
|
||||
{views.map((view) => (
|
||||
<DropdownMenuItem
|
||||
key={view.id}
|
||||
|
@ -68,7 +68,7 @@ export const useTableViewFields = ({
|
||||
columns: ViewFieldDefinition<ViewFieldMetadata>[],
|
||||
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<ViewFieldMetadata>[]) => {
|
||||
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 };
|
||||
};
|
||||
|
@ -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<void>;
|
||||
}) => {
|
||||
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);
|
||||
},
|
||||
});
|
||||
|
||||
|
111
server/src/core/view/seed-data/views.json
Normal file
111
server/src/core/view/seed-data/views.json
Normal file
@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@ -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,
|
||||
})),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -20,5 +20,6 @@ import { ViewFilterResolver } from './resolvers/view-filter.resolver';
|
||||
ViewFilterResolver,
|
||||
ViewSortResolver,
|
||||
],
|
||||
exports: [ViewService],
|
||||
})
|
||||
export class ViewModule {}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
|
179
server/src/database/seeds/views.ts
Normal file
179
server/src/database/seeds/views.ts
Normal file
@ -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,
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user