snapshot of https://github.com/twentyhq/twenty.git (main, 2026-09-17; blobs > 131072B omitted)
Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 3s

This commit is contained in:
upstream-mirror
2026-09-17 23:28:13 +05:00
commit e7da97194a
32688 changed files with 4208681 additions and 0 deletions

View File

@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn
# codegen
generated
# testing
/coverage
/e2e/.auth
/e2e/.results
/playwright-report
/test-results
# dev
/dist/
.twenty
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# typescript
*.tsbuildinfo
*.d.ts

View File

@@ -0,0 +1 @@
24.5.0

View File

@@ -0,0 +1,37 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": ["typescript"],
"categories": {
"correctness": "off"
},
"ignorePatterns": ["node_modules", "dist"],
"rules": {
"no-unused-vars": "off",
"typescript/no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"typescript/no-explicit-any": "off"
},
"overrides": [
{
"files": ["**/*.logic-function.ts", "**/logic-functions/**/*.ts"],
"rules": {
"no-restricted-imports": [
"error",
{
"patterns": [
{
"group": ["twenty-shared", "twenty-shared/*"],
"message": "Logic functions must not import from twenty-shared directly. Import runtime types and helpers from `twenty-sdk/logic-function` instead so the logic-function bundle stays minimal."
}
]
}
]
}
}
]
}

View File

@@ -0,0 +1 @@
nodeLinker: node-modules

View File

@@ -0,0 +1,30 @@
{
"name": "real-estate",
"version": "0.7.1",
"license": "MIT",
"engines": {
"node": "^24.5.0",
"npm": "please-use-yarn",
"yarn": ">=4.0.2",
"twenty": ">=2.19.0"
},
"keywords": [
"twenty-app"
],
"packageManager": "yarn@4.13.0",
"scripts": {
"twenty": "twenty",
"lint": "oxlint -c .oxlintrc.json .",
"lint:fix": "oxlint --fix -c .oxlintrc.json ."
},
"devDependencies": {
"@types/node": "^24.7.2",
"@types/react": "^19.0.0",
"oxlint": "^0.16.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"twenty-client-sdk": "2.31.0",
"twenty-sdk": "2.31.0",
"typescript": "^5.9.3"
}
}

View File

@@ -0,0 +1,11 @@
import { defineApplication } from 'twenty-sdk/define';
export const APPLICATION_UNIVERSAL_IDENTIFIER =
'0dc70098-ce83-430a-bb37-d5b5f2790b99';
export default defineApplication({
universalIdentifier: APPLICATION_UNIVERSAL_IDENTIFIER,
displayName: 'Real Estate',
description:
'Demo real estate CRM: properties, showings, and buyer/seller/agent roles',
});

View File

@@ -0,0 +1,29 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const AGENT_ON_SHOWING_ID = 'e25d8e44-2210-4765-8723-46a3228295ab';
export const AGENT_SHOWINGS_ON_PERSON_ID =
'aed60deb-9d96-41fd-821a-446175b3016d';
export default defineField({
universalIdentifier: AGENT_ON_SHOWING_ID,
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'agent',
label: 'Agent',
icon: 'IconUserStar',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: AGENT_SHOWINGS_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'agentId',
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
import {
AGENT_ON_SHOWING_ID,
AGENT_SHOWINGS_ON_PERSON_ID,
} from './agent-on-showing.field';
export default defineField({
universalIdentifier: AGENT_SHOWINGS_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'agentShowings',
label: 'Showings as agent',
icon: 'IconCalendarEvent',
relationTargetObjectMetadataUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: AGENT_ON_SHOWING_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,30 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const BUYER_ON_OPPORTUNITY_ID = 'b64e08c3-4263-4100-add5-8f700c85c741';
export const BUYER_OPPORTUNITIES_ON_PERSON_ID =
'6a882d40-1987-49b2-91f4-4ed80900be57';
export default defineField({
universalIdentifier: BUYER_ON_OPPORTUNITY_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: FieldType.RELATION,
name: 'buyer',
label: 'Buyer',
icon: 'IconUser',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
BUYER_OPPORTUNITIES_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'buyerId',
},
});

View File

@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const BUYER_ON_SHOWING_ID = '6be576de-50cb-4e3f-b12d-d2538b84d4cc';
export const SHOWINGS_ON_PERSON_ID = '69c1792d-fab7-4b9b-a606-a995782019cd';
export default defineField({
universalIdentifier: BUYER_ON_SHOWING_ID,
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'buyer',
label: 'Buyer',
icon: 'IconUser',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: SHOWINGS_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'buyerId',
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
BUYER_ON_OPPORTUNITY_ID,
BUYER_OPPORTUNITIES_ON_PERSON_ID,
} from './buyer-on-opportunity.field';
export default defineField({
universalIdentifier: BUYER_OPPORTUNITIES_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'buyerOpportunities',
label: 'Opportunities as buyer',
icon: 'IconTargetArrow',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: BUYER_ON_OPPORTUNITY_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,27 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import {
LISTED_PROPERTIES_ON_PERSON_ID,
LISTING_AGENT_ON_PROPERTY_ID,
} from './listing-agent-on-property.field';
export default defineField({
universalIdentifier: LISTED_PROPERTIES_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'listedProperties',
label: 'Listed properties',
icon: 'IconHome',
relationTargetObjectMetadataUniversalIdentifier:
PROPERTY_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: LISTING_AGENT_ON_PROPERTY_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,31 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
export const LISTING_AGENT_ON_PROPERTY_ID =
'e958682f-cdf6-4930-9758-743014a5d64a';
export const LISTED_PROPERTIES_ON_PERSON_ID =
'0d8e78f1-f27e-4164-baeb-7f80b77dd9bc';
export default defineField({
universalIdentifier: LISTING_AGENT_ON_PROPERTY_ID,
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'listingAgent',
label: 'Listing agent',
icon: 'IconUserStar',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
LISTED_PROPERTIES_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'listingAgentId',
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import {
OPPORTUNITIES_ON_PROPERTY_ID,
PROPERTY_ON_OPPORTUNITY_ID,
} from './property-on-opportunity.field';
export default defineField({
universalIdentifier: OPPORTUNITIES_ON_PROPERTY_ID,
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'opportunities',
label: 'Opportunities',
icon: 'IconTargetArrow',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: PROPERTY_ON_OPPORTUNITY_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,38 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const BUYER_STAGE_FIELD_ID = '268886e5-6083-42a1-a18b-3b183bd0af12';
export enum BuyerStage {
COMPLETING_PROFILE = 'COMPLETING_PROFILE',
SHOWING = 'SHOWING',
OFFER_MADE = 'OFFER_MADE',
CLOSING = 'CLOSING',
WON = 'WON',
LOST = 'LOST',
}
export const BUYER_STAGE_OPTIONS = [
{ id: 'e1bada75-8b25-4001-9c42-1ef0bcdc39c6', value: BuyerStage.COMPLETING_PROFILE, label: 'Completing profile', position: 0, color: 'gray' as const },
{ id: '668e4af7-f967-409e-a625-33ae4cab3e16', value: BuyerStage.SHOWING, label: 'Showing', position: 1, color: 'blue' as const },
{ id: '5556a6de-e158-4e67-8ba0-2cfe670f53db', value: BuyerStage.OFFER_MADE, label: 'Offer made', position: 2, color: 'orange' as const },
{ id: '2289bfc5-dcc7-4348-b001-dbf63eeaef8d', value: BuyerStage.CLOSING, label: 'Closing', position: 3, color: 'purple' as const },
{ id: '98bde945-071e-4207-a0dd-3c3445edf26f', value: BuyerStage.WON, label: 'Won', position: 4, color: 'green' as const },
{ id: 'a00947c7-47de-4fa5-946c-0a141c7cb33f', value: BuyerStage.LOST, label: 'Lost', position: 5, color: 'red' as const },
];
export default defineField({
universalIdentifier: BUYER_STAGE_FIELD_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: FieldType.SELECT,
name: 'buyerStage',
label: 'Buyer stage',
description: 'Where the buyer is in their journey',
icon: 'IconRoute',
isNullable: true,
options: BUYER_STAGE_OPTIONS,
});

View File

@@ -0,0 +1,30 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const OPPORTUNITY_ON_SHOWING_ID =
'69fa9f30-dc06-4c2c-b6e5-79351c7a52c8';
export const SHOWINGS_ON_OPPORTUNITY_ID =
'4f296937-b62c-4e43-9316-6278148909c4';
export default defineField({
universalIdentifier: OPPORTUNITY_ON_SHOWING_ID,
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'opportunity',
label: 'Opportunity',
icon: 'IconTargetArrow',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: SHOWINGS_ON_OPPORTUNITY_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'opportunityId',
},
});

View File

@@ -0,0 +1,20 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const BUDGET_MAX_FIELD_UNIVERSAL_IDENTIFIER =
'85717714-a03b-4f7a-a13e-504f4e60d588';
export default defineField({
universalIdentifier: BUDGET_MAX_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.CURRENCY,
name: 'budgetMax',
label: 'Budget max',
description: 'Buyer maximum budget',
icon: 'IconCurrencyEuro',
isNullable: true,
});

View File

@@ -0,0 +1,20 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const BUDGET_MIN_FIELD_UNIVERSAL_IDENTIFIER =
'46ebae6d-27ab-425f-b60e-1feac27cc45b';
export default defineField({
universalIdentifier: BUDGET_MIN_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.CURRENCY,
name: 'budgetMin',
label: 'Budget min',
description: 'Buyer minimum budget',
icon: 'IconCurrencyEuro',
isNullable: true,
});

View File

@@ -0,0 +1,19 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const DESIRED_AREA_FIELD_UNIVERSAL_IDENTIFIER =
'073ed8cf-7e08-4325-bd3a-c033155a4a4d';
export default defineField({
universalIdentifier: DESIRED_AREA_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.TEXT,
name: 'desiredArea',
label: 'Desired area',
description: 'Neighborhood or area the buyer is looking in',
icon: 'IconMapSearch',
});

View File

@@ -0,0 +1,20 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const PRE_APPROVED_FIELD_UNIVERSAL_IDENTIFIER =
'21e0c04d-845e-4639-b9f6-2d35177ae05a';
export default defineField({
universalIdentifier: PRE_APPROVED_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.BOOLEAN,
name: 'preApproved',
label: 'Pre-approved',
description: 'Whether the buyer has financing pre-approval',
icon: 'IconCheck',
defaultValue: false,
});

View File

@@ -0,0 +1,49 @@
import {
defineField,
FieldType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
enum PersonType {
BUYER = 'BUYER',
SELLER = 'SELLER',
AGENT = 'AGENT',
}
export const PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER =
'ea97d102-34e9-40bb-bbad-556a6456016a';
export default defineField({
universalIdentifier: PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.SELECT,
name: 'personType',
label: 'Person type',
description: 'Role of this person in the real estate flow',
icon: 'IconUserCog',
isNullable: true,
options: [
{
id: 'e6bdb2bf-c5d4-48ae-8781-30caf34e313f',
value: PersonType.BUYER,
label: 'Buyer',
position: 0,
color: 'blue',
},
{
id: '3312451e-bd22-4e01-bec2-ee30ecd60cf0',
value: PersonType.SELLER,
label: 'Seller',
position: 1,
color: 'purple',
},
{
id: 'e3975fa7-d15f-4e4c-88f5-6de99cd20f4e',
value: PersonType.AGENT,
label: 'Agent',
position: 2,
color: 'green',
},
],
});

View File

@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import {
PROPERTIES_FOR_SALE_ON_PERSON_ID,
SELLER_CONTACT_ON_PROPERTY_ID,
} from './seller-contact-on-property.field';
export default defineField({
universalIdentifier: PROPERTIES_FOR_SALE_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'propertiesForSale',
label: 'Properties for sale',
icon: 'IconHome',
relationTargetObjectMetadataUniversalIdentifier:
PROPERTY_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier:
SELLER_CONTACT_ON_PROPERTY_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,31 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
export const PROPERTY_ON_OPPORTUNITY_ID =
'899dff13-3962-468c-ac8d-cdb10eac7621';
export const OPPORTUNITIES_ON_PROPERTY_ID =
'f3b50c66-2096-486b-bedc-2261c537b5dd';
export default defineField({
universalIdentifier: PROPERTY_ON_OPPORTUNITY_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: FieldType.RELATION,
name: 'property',
label: 'Property',
icon: 'IconHome',
relationTargetObjectMetadataUniversalIdentifier:
PROPERTY_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: OPPORTUNITIES_ON_PROPERTY_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'propertyId',
},
});

View File

@@ -0,0 +1,28 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const PROPERTY_ON_SHOWING_ID = '833124c5-4c9d-4921-aae9-fc5a235cc04e';
export const SHOWINGS_ON_PROPERTY_ID = 'ff848464-396a-4feb-a2b7-3aa28c1e3857';
export default defineField({
universalIdentifier: PROPERTY_ON_SHOWING_ID,
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'property',
label: 'Property',
icon: 'IconHome',
relationTargetObjectMetadataUniversalIdentifier:
PROPERTY_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: SHOWINGS_ON_PROPERTY_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'propertyId',
},
});

View File

@@ -0,0 +1,31 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
export const SELLER_CONTACT_ON_PROPERTY_ID =
'5471ea86-b2bf-4b9e-925f-a4df1d8819b3';
export const PROPERTIES_FOR_SALE_ON_PERSON_ID =
'eccff600-5ab9-46cd-9243-fe8c76ddb144';
export default defineField({
universalIdentifier: SELLER_CONTACT_ON_PROPERTY_ID,
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'sellerContact',
label: 'Seller',
icon: 'IconUserDollar',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
PROPERTIES_FOR_SALE_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'sellerContactId',
},
});

View File

@@ -0,0 +1,30 @@
import {
defineField,
FieldType,
OnDeleteAction,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
export const SELLER_ON_OPPORTUNITY_ID = 'c3e755f5-2648-448f-8886-c81db4e3a97e';
export const SELLER_OPPORTUNITIES_ON_PERSON_ID =
'9e617a78-d853-4f46-9947-d0f28dd2cd48';
export default defineField({
universalIdentifier: SELLER_ON_OPPORTUNITY_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: FieldType.RELATION,
name: 'seller',
label: 'Seller',
icon: 'IconUserDollar',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier:
SELLER_OPPORTUNITIES_ON_PERSON_ID,
universalSettings: {
relationType: RelationType.MANY_TO_ONE,
onDelete: OnDeleteAction.SET_NULL,
joinColumnName: 'sellerId',
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
SELLER_ON_OPPORTUNITY_ID,
SELLER_OPPORTUNITIES_ON_PERSON_ID,
} from './seller-on-opportunity.field';
export default defineField({
universalIdentifier: SELLER_OPPORTUNITIES_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'sellerOpportunities',
label: 'Opportunities as seller',
icon: 'IconTargetArrow',
relationTargetObjectMetadataUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
relationTargetFieldMetadataUniversalIdentifier: SELLER_ON_OPPORTUNITY_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
import {
OPPORTUNITY_ON_SHOWING_ID,
SHOWINGS_ON_OPPORTUNITY_ID,
} from './opportunity-on-showing.field';
export default defineField({
universalIdentifier: SHOWINGS_ON_OPPORTUNITY_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: FieldType.RELATION,
name: 'showings',
label: 'Showings',
icon: 'IconCalendarEvent',
relationTargetObjectMetadataUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: OPPORTUNITY_ON_SHOWING_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,26 @@
import {
defineField,
FieldType,
RelationType,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
import {
BUYER_ON_SHOWING_ID,
SHOWINGS_ON_PERSON_ID,
} from './buyer-on-showing.field';
export default defineField({
universalIdentifier: SHOWINGS_ON_PERSON_ID,
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: FieldType.RELATION,
name: 'showings',
label: 'Showings',
icon: 'IconCalendarEvent',
relationTargetObjectMetadataUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: BUYER_ON_SHOWING_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,21 @@
import { defineField, FieldType, RelationType } from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
import {
PROPERTY_ON_SHOWING_ID,
SHOWINGS_ON_PROPERTY_ID,
} from './property-on-showing.field';
export default defineField({
universalIdentifier: SHOWINGS_ON_PROPERTY_ID,
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: FieldType.RELATION,
name: 'showings',
label: 'Showings',
icon: 'IconCalendarEvent',
relationTargetObjectMetadataUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
relationTargetFieldMetadataUniversalIdentifier: PROPERTY_ON_SHOWING_ID,
universalSettings: {
relationType: RelationType.ONE_TO_MANY,
},
});

View File

@@ -0,0 +1,224 @@
import { CoreApiClient } from 'twenty-client-sdk/core';
import { definePostInstallLogicFunction } from 'twenty-sdk/define';
import { BuyerStage } from '../fields/opportunity-buyer-stage.field';
const eur = (amount: number) => ({
amountMicros: amount * 1_000_000,
currencyCode: 'EUR',
});
const FIRST = [
'Emma', 'Lucas', 'Chloe', 'Louis', 'Jade', 'Hugo', 'Lea', 'Nathan',
'Manon', 'Enzo', 'Camille', 'Gabriel', 'Sarah', 'Raphael', 'Ines',
'Arthur', 'Zoe', 'Paul', 'Julie', 'Adam', 'Alice', 'Theo', 'Lina',
'Noah', 'Eva', 'Tom', 'Rose', 'Sacha', 'Nina', 'Leo',
];
const LAST = [
'Martin', 'Bernard', 'Dubois', 'Thomas', 'Robert', 'Richard', 'Petit',
'Durand', 'Leroy', 'Moreau', 'Simon', 'Laurent', 'Lefevre', 'Michel',
'Garcia',
];
const lastFor = (i: number) => LAST[i % LAST.length];
const AGENT_COUNT = 4;
const SELLER_COUNT = 12;
const BUYER_COUNT = 12;
const PROPERTY_COUNT = 30;
const agentFirst = FIRST.slice(0, AGENT_COUNT);
const sellerFirst = FIRST.slice(AGENT_COUNT, AGENT_COUNT + SELLER_COUNT);
const buyerFirst = FIRST.slice(
AGENT_COUNT + SELLER_COUNT,
AGENT_COUNT + SELLER_COUNT + BUYER_COUNT,
);
const AREAS = [
'Le Marais', 'Montmartre', 'Belleville', 'Bastille', 'Latin Quarter',
'Saint-Germain', 'Canal Saint-Martin', 'Batignolles', 'Butte-aux-Cailles',
'Pigalle', 'Oberkampf', 'Nation',
];
const STREETS = [
'Rue de Rivoli', 'Avenue Montaigne', 'Rue des Martyrs', 'Villa Leandre',
'Boulevard Haussmann', 'Rue Oberkampf', 'Rue du Bac', 'Avenue Foch',
'Rue de la Pompe', 'Quai de Valmy', 'Rue Lepic', 'Cours Mirabeau',
];
const CITIES: [string, string][] = [
['Paris', '75001'], ['Paris', '75008'], ['Lyon', '69002'],
['Bordeaux', '33000'], ['Marseille', '13001'], ['Nice', '06000'],
];
const TYPES = ['APARTMENT', 'HOUSE', 'CONDO', 'LAND'];
const STATUSES = ['ACTIVE', 'ACTIVE', 'ACTIVE', 'UNDER_OFFER', 'COMING_SOON', 'SOLD'];
const SHOWING_STATUSES = ['COMPLETED', 'SCHEDULED', 'COMPLETED', 'NO_SHOW', 'CANCELLED'];
const BUYER_STAGE_CYCLE = [
BuyerStage.COMPLETING_PROFILE,
BuyerStage.SHOWING,
BuyerStage.OFFER_MADE,
BuyerStage.CLOSING,
BuyerStage.WON,
BuyerStage.LOST,
];
const STANDARD_STAGE_BY_BUYER_STAGE: Record<BuyerStage, string> = {
[BuyerStage.COMPLETING_PROFILE]: 'NEW',
[BuyerStage.SHOWING]: 'SCREENING',
[BuyerStage.OFFER_MADE]: 'PROPOSAL',
[BuyerStage.CLOSING]: 'PROPOSAL',
[BuyerStage.WON]: 'CUSTOMER',
[BuyerStage.LOST]: 'NEW',
};
const handler = async () => {
const client = new CoreApiClient();
const peopleData = [
...agentFirst.map((firstName, i) => ({
name: { firstName, lastName: lastFor(i) },
personType: 'AGENT',
})),
...sellerFirst.map((firstName, i) => ({
name: { firstName, lastName: lastFor(AGENT_COUNT + i) },
personType: 'SELLER',
})),
...buyerFirst.map((firstName, b) => ({
name: { firstName, lastName: lastFor(AGENT_COUNT + SELLER_COUNT + b) },
personType: 'BUYER',
budgetMin: eur(200_000 + b * 40_000),
budgetMax: eur(200_000 + b * 40_000 + 150_000 + (b % 3) * 50_000),
preApproved: b % 2 === 0,
desiredArea: AREAS[b % AREAS.length],
})),
];
const { createPeople } = (await client.mutation({
createPeople: {
__args: { data: peopleData as any },
id: true,
name: { firstName: true },
},
} as any)) as any;
const personId = (firstName: string): string =>
createPeople.find((person: any) => person.name?.firstName === firstName).id;
const propertyMeta = Array.from({ length: PROPERTY_COUNT }, (_, i) => {
const [city, postcode] = CITIES[i % CITIES.length];
const street = STREETS[i % STREETS.length];
const number = 3 + i * 7;
return {
name: `${number} ${street}`,
number,
street,
city,
postcode,
priceEur: 180_000 + i * 38_000 + (i % 3) * 15_000,
status: STATUSES[i % STATUSES.length],
propertyType: TYPES[i % TYPES.length],
bedrooms: 1 + (i % 5),
bathrooms: 1 + (i % 3),
surfaceSqm: 42 + i * 4,
agentFirst: agentFirst[i % AGENT_COUNT],
sellerFirst: sellerFirst[i % SELLER_COUNT],
};
});
const propertiesData = propertyMeta.map((m) => ({
name: m.name,
propertyAddress: {
addressStreet1: `${m.number} ${m.street}`,
addressCity: m.city,
addressPostcode: m.postcode,
addressCountry: 'France',
},
price: eur(m.priceEur),
status: m.status,
propertyType: m.propertyType,
bedrooms: m.bedrooms,
bathrooms: m.bathrooms,
surfaceSqm: m.surfaceSqm,
listingAgentId: personId(m.agentFirst),
sellerContactId: personId(m.sellerFirst),
}));
const { createProperties } = (await client.mutation({
createProperties: {
__args: { data: propertiesData as any },
id: true,
name: true,
},
} as any)) as any;
const propertyId = (name: string): string =>
createProperties.find((property: any) => property.name === name).id;
// One opportunity per buyer: the buyer's pipeline card, staged by buyerStage.
const opportunitiesData = buyerFirst.map((firstName, b) => {
const buyerStage = BUYER_STAGE_CYCLE[b % BUYER_STAGE_CYCLE.length];
const meta = propertyMeta[(b * 2) % PROPERTY_COUNT];
return {
name: `${firstName} ${lastFor(AGENT_COUNT + SELLER_COUNT + b)}`,
buyerStage,
stage: STANDARD_STAGE_BY_BUYER_STAGE[buyerStage],
amount: eur(Math.round(meta.priceEur * 0.97)),
closeDate: `2026-${String(9 + (b % 3)).padStart(2, '0')}-15T00:00:00.000Z`,
buyerId: personId(firstName),
sellerId: personId(meta.sellerFirst),
propertyId: propertyId(meta.name),
};
});
const { createOpportunities } = (await client.mutation({
createOpportunities: {
__args: { data: opportunitiesData as any },
id: true,
name: true,
},
} as any)) as any;
const opportunityId = (name: string): string =>
createOpportunities.find((opportunity: any) => opportunity.name === name).id;
const showingsData = buyerFirst.flatMap((firstName, b) => {
const opportunityName = `${firstName} ${lastFor(AGENT_COUNT + SELLER_COUNT + b)}`;
return [0, 1].map((k) => {
const propertyIndex = (b * 2 + k * 11) % PROPERTY_COUNT;
const meta = propertyMeta[propertyIndex];
const status = SHOWING_STATUSES[(b + k) % SHOWING_STATUSES.length];
const month = 7 + ((b + k) % 2);
const day = String(3 + ((b * 3 + k) % 25)).padStart(2, '0');
return {
name: `${meta.name} / ${lastFor(AGENT_COUNT + SELLER_COUNT + b)}`,
status,
scheduledAt: `2026-0${month}-${day}T1${k + 2}:00:00.000Z`,
propertyId: propertyId(meta.name),
buyerId: personId(firstName),
agentId: personId(meta.agentFirst),
opportunityId: opportunityId(opportunityName),
...(status === 'COMPLETED'
? { interestLevel: `RATING_${2 + ((b + k) % 4)}` }
: {}),
};
});
});
await client.mutation({
createShowings: {
__args: { data: showingsData as any },
id: true,
},
} as any);
console.log(
`Seeded ${peopleData.length} people, ${propertiesData.length} properties, ${showingsData.length} showings, ${opportunitiesData.length} opportunities.`,
);
return {};
};
export default definePostInstallLogicFunction({
universalIdentifier: '8adff1aa-c3a5-41ba-8071-d86c1195ffd8',
name: 'post-install',
description: 'Seeds demo people, properties, showings and opportunities.',
timeoutSeconds: 120,
shouldRunSynchronously: true,
handler,
});

View File

@@ -0,0 +1,14 @@
import {
defineNavigationMenuItem,
NavigationMenuItemType,
} from 'twenty-sdk/define';
import { AGENCY_DASHBOARD_PAGE_LAYOUT_ID } from '../page-layouts/agency-dashboard.page-layout';
export default defineNavigationMenuItem({
universalIdentifier: 'ab130995-9090-49c7-9285-e9f1569802be',
name: 'Agency Overview',
icon: 'IconChartBar',
position: 2,
type: NavigationMenuItemType.PAGE_LAYOUT,
pageLayoutUniversalIdentifier: AGENCY_DASHBOARD_PAGE_LAYOUT_ID,
});

View File

@@ -0,0 +1,12 @@
import {
defineNavigationMenuItem,
NavigationMenuItemType,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
export default defineNavigationMenuItem({
universalIdentifier: 'f034a868-8acc-4aa9-8c1d-86eba3d89fbe',
position: 0,
type: NavigationMenuItemType.OBJECT,
targetObjectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
});

View File

@@ -0,0 +1,12 @@
import {
defineNavigationMenuItem,
NavigationMenuItemType,
} from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export default defineNavigationMenuItem({
universalIdentifier: '075cab08-9a87-401a-b3b2-47f8e0504113',
position: 1,
type: NavigationMenuItemType.OBJECT,
targetObjectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
});

View File

@@ -0,0 +1,171 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
enum PropertyStatus {
COMING_SOON = 'COMING_SOON',
ACTIVE = 'ACTIVE',
UNDER_OFFER = 'UNDER_OFFER',
SOLD = 'SOLD',
}
enum PropertyType {
APARTMENT = 'APARTMENT',
HOUSE = 'HOUSE',
CONDO = 'CONDO',
LAND = 'LAND',
}
export const PROPERTY_UNIVERSAL_IDENTIFIER =
'f1d4bc63-38e4-467f-ad87-ffc0feaf5f01';
export const PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER =
'4bb99253-6ae2-4aa8-8f44-708ce3d29c31';
export const PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER =
'97fb5f33-bdc3-4b04-9c32-86d5ff995b0e';
export const PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER =
'0fc19f9c-4bc5-41e6-8295-628e0258ba15';
export default defineObject({
universalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
nameSingular: 'property',
namePlural: 'properties',
labelSingular: 'Property',
labelPlural: 'Properties',
description: 'A property listing',
icon: 'IconHome',
labelIdentifierFieldMetadataUniversalIdentifier:
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
fields: [
{
universalIdentifier: PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.TEXT,
name: 'name',
label: 'Name',
description: 'Listing name, e.g. the street address',
icon: 'IconAbc',
},
{
universalIdentifier: 'b254a4f5-669f-429c-af92-f4df283b132a',
type: FieldType.ADDRESS,
name: 'propertyAddress',
label: 'Address',
icon: 'IconMapPin',
},
{
universalIdentifier: PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.CURRENCY,
name: 'price',
label: 'Price',
icon: 'IconCurrencyEuro',
isNullable: true,
},
{
universalIdentifier: PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.SELECT,
name: 'status',
label: 'Status',
icon: 'IconProgress',
defaultValue: `'${PropertyStatus.ACTIVE}'`,
options: [
{
id: '075cab08-9a87-401a-b3b2-47f8e0504112',
value: PropertyStatus.COMING_SOON,
label: 'Coming soon',
position: 0,
color: 'gray',
},
{
id: 'd637ac19-fcf4-4a92-921c-72fe505f2123',
value: PropertyStatus.ACTIVE,
label: 'Active',
position: 1,
color: 'green',
},
{
id: 'f0ab44f3-7939-4e00-92ba-9c29010e6d52',
value: PropertyStatus.UNDER_OFFER,
label: 'Under offer',
position: 2,
color: 'orange',
},
{
id: '67d245aa-6ec9-42c0-a657-83dd842e4107',
value: PropertyStatus.SOLD,
label: 'Sold',
position: 3,
color: 'blue',
},
],
},
{
universalIdentifier: 'f6bf3f42-6c4f-4dba-af51-7db74979c888',
type: FieldType.SELECT,
name: 'propertyType',
label: 'Property type',
icon: 'IconBuildingCommunity',
isNullable: true,
options: [
{
id: '7efcd575-d7d0-46d9-bf26-54a3c1d5ba1e',
value: PropertyType.APARTMENT,
label: 'Apartment',
position: 0,
color: 'blue',
},
{
id: 'b6f3ec35-3cac-4ad3-8f4a-7144edda75e9',
value: PropertyType.HOUSE,
label: 'House',
position: 1,
color: 'green',
},
{
id: '486a439d-af96-4884-8125-5f5ca01999fe',
value: PropertyType.CONDO,
label: 'Condo',
position: 2,
color: 'purple',
},
{
id: '53db1ebd-c816-40c4-85d5-3c17a39669cd',
value: PropertyType.LAND,
label: 'Land',
position: 3,
color: 'brown',
},
],
},
{
universalIdentifier: 'f3254ad1-ac74-459b-bfdc-b443dad61001',
type: FieldType.NUMBER,
name: 'bedrooms',
label: 'Bedrooms',
icon: 'IconBed',
isNullable: true,
},
{
universalIdentifier: 'f1289297-f0d3-4001-bb45-100135d71e88',
type: FieldType.NUMBER,
name: 'bathrooms',
label: 'Bathrooms',
icon: 'IconBath',
isNullable: true,
},
{
universalIdentifier: '6de9a40e-6260-4dbf-8096-7296bc87911e',
type: FieldType.NUMBER,
name: 'surfaceSqm',
label: 'Surface (sqm)',
icon: 'IconRuler2',
isNullable: true,
},
{
universalIdentifier: 'eb610e48-e621-43c8-8f1b-934b1af42a03',
type: FieldType.FILES,
name: 'photos',
label: 'Photos',
icon: 'IconPhoto',
isNullable: true,
universalSettings: { maxNumberOfValues: 10 },
},
],
});

View File

@@ -0,0 +1,112 @@
import { defineObject, FieldType } from 'twenty-sdk/define';
enum ShowingStatus {
SCHEDULED = 'SCHEDULED',
COMPLETED = 'COMPLETED',
CANCELLED = 'CANCELLED',
NO_SHOW = 'NO_SHOW',
}
export const SHOWING_UNIVERSAL_IDENTIFIER =
'548f2632-741b-4ff2-987c-b1c906b9ba1b';
export const SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER =
'5fda67f0-0552-4056-86db-6cc531840d00';
export const SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER =
'f83082b5-a65f-4dde-8836-0e32ecef8653';
export const SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER =
'9a7efb7e-1c82-49ec-b58b-d1bda0859fd5';
export const SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER =
'e27872da-9a4b-4973-a894-7bcf3b7d7773';
export default defineObject({
universalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
nameSingular: 'showing',
namePlural: 'showings',
labelSingular: 'Showing',
labelPlural: 'Showings',
description: 'A property visit booked for a buyer',
icon: 'IconCalendarEvent',
labelIdentifierFieldMetadataUniversalIdentifier:
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
fields: [
{
universalIdentifier: SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.TEXT,
name: 'name',
label: 'Name',
description: 'Showing label, e.g. property / buyer',
icon: 'IconAbc',
},
{
universalIdentifier: SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.DATE_TIME,
name: 'scheduledAt',
label: 'Scheduled at',
icon: 'IconClock',
isNullable: true,
},
{
universalIdentifier: SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.SELECT,
name: 'status',
label: 'Status',
icon: 'IconProgress',
defaultValue: `'${ShowingStatus.SCHEDULED}'`,
options: [
{
id: 'f9337370-6b1d-44c2-9f7e-5dfeae5dc43f',
value: ShowingStatus.SCHEDULED,
label: 'Scheduled',
position: 0,
color: 'blue',
},
{
id: 'f4dcdaa5-e587-4afb-b264-1b3e1d34a987',
value: ShowingStatus.COMPLETED,
label: 'Completed',
position: 1,
color: 'green',
},
{
id: 'a0be44a0-64bb-4937-9b09-7d367274a658',
value: ShowingStatus.CANCELLED,
label: 'Cancelled',
position: 2,
color: 'gray',
},
{
id: 'b0769e7d-4f71-41e7-aa2c-e04780efe6e0',
value: ShowingStatus.NO_SHOW,
label: 'No show',
position: 3,
color: 'red',
},
],
},
{
universalIdentifier: '377e4a00-8398-413a-8662-526236ff2d65',
type: FieldType.RICH_TEXT,
name: 'feedback',
label: 'Feedback',
description: 'Buyer reaction after the visit',
icon: 'IconMessage',
isNullable: true,
},
{
universalIdentifier: SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER,
type: FieldType.RATING,
name: 'interestLevel',
label: 'Interest level',
icon: 'IconStar',
isNullable: true,
options: [
{ id: '885a6c52-512c-47b1-a160-a609dcbd82bb', value: 'RATING_1', label: '1', position: 0 },
{ id: '0dc70098-ce83-430a-bb37-d5b5f2790b90', value: 'RATING_2', label: '2', position: 1 },
{ id: '0dc70098-ce83-430a-bb37-d5b5f2790b91', value: 'RATING_3', label: '3', position: 2 },
{ id: '0dc70098-ce83-430a-bb37-d5b5f2790b92', value: 'RATING_4', label: '4', position: 3 },
{ id: '0dc70098-ce83-430a-bb37-d5b5f2790b93', value: 'RATING_5', label: '5', position: 4 },
],
},
],
});

View File

@@ -0,0 +1,244 @@
import {
AggregateOperations,
definePageLayout,
PageLayoutTabLayoutMode,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import {
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_UNIVERSAL_IDENTIFIER,
} from '../objects/property.object';
import {
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_UNIVERSAL_IDENTIFIER,
} from '../objects/showing.object';
import { LISTING_AGENT_ON_PROPERTY_ID } from '../fields/listing-agent-on-property.field';
import { AGENT_ON_SHOWING_ID } from '../fields/agent-on-showing.field';
import { BUYER_STAGE_FIELD_ID } from '../fields/opportunity-buyer-stage.field';
export const AGENCY_DASHBOARD_PAGE_LAYOUT_ID =
'a5de312d-d97f-4dad-bce8-e8110f6b86c7';
const opportunityFields =
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.fields;
const opportunityObjectId =
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier;
// Chart-config values the SDK doesn't export as enums (passed as string literals).
const BAR = {
layout: 'VERTICAL',
primaryAxisOrderBy: 'FIELD_ASC',
axisNameDisplay: 'NONE',
color: 'auto',
timezone: 'UTC',
firstDayOfTheWeek: 1,
} as const;
export default definePageLayout({
universalIdentifier: AGENCY_DASHBOARD_PAGE_LAYOUT_ID,
name: 'Agency Overview',
type: 'STANDALONE_PAGE',
tabs: [
{
universalIdentifier: '62f7f9e3-75c9-4a60-9ae9-151b417a2c4c',
title: 'Overview',
position: 0,
icon: 'IconChartBar',
layoutMode: PageLayoutTabLayoutMode.GRID,
widgets: [
{
universalIdentifier: '7934653e-a3d4-47d6-a624-a7216a7e18ca',
title: 'Portfolio value',
type: 'GRAPH',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 0,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.SUM,
displayDataLabel: true,
timezone: 'UTC',
firstDayOfTheWeek: 1,
},
},
{
universalIdentifier: '30e59360-b245-480b-b74f-382ef7c19976',
title: 'Pipeline value',
type: 'GRAPH',
objectUniversalIdentifier: opportunityObjectId,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 3,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
opportunityFields.amount.universalIdentifier,
aggregateOperation: AggregateOperations.SUM,
displayDataLabel: true,
timezone: 'UTC',
firstDayOfTheWeek: 1,
},
},
{
universalIdentifier: 'b2a949ff-36ba-47d2-a3af-a0ac986745ca',
title: 'Properties sold',
type: 'GRAPH',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 6,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.COUNT,
displayDataLabel: true,
timezone: 'UTC',
firstDayOfTheWeek: 1,
filter: {
recordFilters: [
{
fieldMetadataUniversalIdentifier:
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
operand: 'IS',
value: '["SOLD"]',
},
],
},
},
},
{
universalIdentifier: '80f0d9e5-07d8-461c-9188-e6cd7beed9f5',
title: 'Showings',
type: 'GRAPH',
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 0,
column: 9,
rowSpan: 2,
columnSpan: 3,
},
configuration: {
configurationType: 'AGGREGATE_CHART',
aggregateFieldMetadataUniversalIdentifier:
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.COUNT,
displayDataLabel: true,
timezone: 'UTC',
firstDayOfTheWeek: 1,
},
},
{
universalIdentifier: '81baf0f8-1db9-4e60-be4f-a2304e56a714',
title: 'Properties by status',
type: 'GRAPH',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 0,
rowSpan: 5,
columnSpan: 6,
},
configuration: {
configurationType: 'PIE_CHART',
aggregateFieldMetadataUniversalIdentifier:
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.COUNT,
groupByFieldMetadataUniversalIdentifier:
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
displayLegend: true,
timezone: 'UTC',
firstDayOfTheWeek: 1,
},
},
{
universalIdentifier: '2c8597fb-ef1d-41cf-9788-3ff4c933b2b5',
title: 'Pipeline by stage',
type: 'GRAPH',
objectUniversalIdentifier: opportunityObjectId,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 2,
column: 6,
rowSpan: 5,
columnSpan: 6,
},
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
opportunityFields.name.universalIdentifier,
aggregateOperation: AggregateOperations.COUNT,
primaryAxisGroupByFieldMetadataUniversalIdentifier:
BUYER_STAGE_FIELD_ID,
...BAR,
},
},
{
universalIdentifier: '4f9c41e3-3871-43fc-bdb8-1f42fdfcaecb',
title: 'Listings by agent',
type: 'GRAPH',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 7,
column: 0,
rowSpan: 5,
columnSpan: 6,
},
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.COUNT,
primaryAxisGroupByFieldMetadataUniversalIdentifier:
LISTING_AGENT_ON_PROPERTY_ID,
...BAR,
primaryAxisOrderBy: 'VALUE_DESC',
},
},
{
universalIdentifier: '0018b8ef-abd0-4937-8c80-2b86805f4dc5',
title: 'Showings by agent',
type: 'GRAPH',
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
position: {
layoutMode: PageLayoutTabLayoutMode.GRID,
row: 7,
column: 6,
rowSpan: 5,
columnSpan: 6,
},
configuration: {
configurationType: 'BAR_CHART',
aggregateFieldMetadataUniversalIdentifier:
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
aggregateOperation: AggregateOperations.COUNT,
primaryAxisGroupByFieldMetadataUniversalIdentifier:
AGENT_ON_SHOWING_ID,
...BAR,
primaryAxisOrderBy: 'VALUE_DESC',
},
},
],
},
],
});

View File

@@ -0,0 +1,54 @@
import {
definePageLayout,
PageLayoutTabLayoutMode,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
const OPPORTUNITY_RECORD_PAGE_FIELDS_VIEW_ID =
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.views
.opportunityRecordPageFields.universalIdentifier;
// Override the Opportunity record page so its Fields widget surfaces the app's
// buyer / seller / property / showings relations (added as view-fields).
export default definePageLayout({
universalIdentifier: '170e0171-b2f3-4c30-a376-6c92fec7ee9d',
name: 'Opportunity Record Page',
type: 'RECORD_PAGE',
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
tabs: [
{
universalIdentifier: 'a4691ec1-4499-4dd3-a7e1-1cea5faf116f',
title: 'Home',
position: 10,
icon: 'IconHome',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: '29af4140-c86a-4159-9bcc-674ff364f675',
title: 'Fields',
type: 'FIELDS',
configuration: {
configurationType: 'FIELDS',
viewUniversalIdentifier: OPPORTUNITY_RECORD_PAGE_FIELDS_VIEW_ID,
},
},
],
},
{
universalIdentifier: '61db2471-e338-436d-934f-136dc1d8174b',
title: 'Timeline',
position: 20,
icon: 'IconTimelineEvent',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: 'b0a3c0b2-600c-418e-8b3f-49f8588bdcbd',
title: 'Timeline',
type: 'TIMELINE',
configuration: { configurationType: 'TIMELINE' },
},
],
},
],
});

View File

@@ -0,0 +1,45 @@
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
import { SHOWING_RECORD_PAGE_FIELDS_VIEW_ID } from '../views/showing-record-page-fields.view';
export default definePageLayout({
universalIdentifier: 'dc95166e-85f3-427d-91ef-dbdcdb1cee13',
name: 'Showing Record Page',
type: 'RECORD_PAGE',
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
tabs: [
{
universalIdentifier: 'bd4bcb1f-202a-4bd5-acce-ec1f4788b47e',
title: 'Home',
position: 10,
icon: 'IconHome',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: '56140f7f-9b6c-4573-b701-e8552cf8a1c2',
title: 'Fields',
type: 'FIELDS',
configuration: {
configurationType: 'FIELDS',
viewUniversalIdentifier: SHOWING_RECORD_PAGE_FIELDS_VIEW_ID,
},
},
],
},
{
universalIdentifier: '202334f7-7e4a-47ff-9fbd-83e013c9e322',
title: 'Timeline',
position: 20,
icon: 'IconTimelineEvent',
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
widgets: [
{
universalIdentifier: '75a5ebcf-0962-4eb5-810a-49a9da3b4e39',
title: 'Timeline',
type: 'TIMELINE',
configuration: { configurationType: 'TIMELINE' },
},
],
},
],
});

View File

@@ -0,0 +1,51 @@
import {
defineRole,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
} from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const AGENT_ROLE_UNIVERSAL_IDENTIFIER =
'253f1693-39e2-423f-93ef-87811d0b2298';
const readWrite = {
canReadObjectRecords: true,
canUpdateObjectRecords: true,
canSoftDeleteObjectRecords: false,
canDestroyObjectRecords: false,
};
export default defineRole({
universalIdentifier: AGENT_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Agent',
description:
'Works listings, showings and contacts. No access to opportunities.',
icon: 'IconUserStar',
canReadAllObjectRecords: false,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canUpdateAllSettings: false,
canBeAssignedToUsers: true,
canBeAssignedToAgents: false,
canBeAssignedToApiKeys: false,
objectPermissions: [
{ objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER, ...readWrite },
{ objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER, ...readWrite },
{
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
...readWrite,
},
{
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.note.universalIdentifier,
...readWrite,
},
{
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.task.universalIdentifier,
...readWrite,
},
],
});

View File

@@ -0,0 +1,19 @@
import { defineRole } from 'twenty-sdk/define';
export const BROKER_ROLE_UNIVERSAL_IDENTIFIER =
'f5bf4e0c-a74f-4509-8d9b-802fc246ccc1';
export default defineRole({
universalIdentifier: BROKER_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Broker',
description: 'Full access to the real estate workspace',
icon: 'IconBuildingSkyscraper',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
canUpdateAllSettings: false,
canBeAssignedToUsers: true,
canBeAssignedToAgents: true,
canBeAssignedToApiKeys: true,
});

View File

@@ -0,0 +1,14 @@
import { defineApplicationRole } from 'twenty-sdk/define';
export const DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER =
'23eb2a01-f1a9-4364-b2f0-0136c0acb9b2';
export default defineApplicationRole({
universalIdentifier: DEFAULT_FUNCTION_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Real Estate default function role',
description: 'Role the Real Estate app operations run as',
canReadAllObjectRecords: true,
canUpdateAllObjectRecords: true,
canSoftDeleteAllObjectRecords: true,
canDestroyAllObjectRecords: false,
});

View File

@@ -0,0 +1,32 @@
import { defineRole } from 'twenty-sdk/define';
import { PROPERTY_UNIVERSAL_IDENTIFIER } from '../objects/property.object';
import { SHOWING_UNIVERSAL_IDENTIFIER } from '../objects/showing.object';
export const SELLER_ROLE_UNIVERSAL_IDENTIFIER =
'f034a868-8acc-4aa9-8c1d-86eba3d89fbf';
const readOnly = {
canReadObjectRecords: true,
canUpdateObjectRecords: false,
canSoftDeleteObjectRecords: false,
canDestroyObjectRecords: false,
};
export default defineRole({
universalIdentifier: SELLER_ROLE_UNIVERSAL_IDENTIFIER,
label: 'Seller',
description: 'Read-only access to their listing and its showings',
icon: 'IconUserDollar',
canReadAllObjectRecords: false,
canUpdateAllObjectRecords: false,
canSoftDeleteAllObjectRecords: false,
canDestroyAllObjectRecords: false,
canUpdateAllSettings: false,
canBeAssignedToUsers: true,
canBeAssignedToAgents: false,
canBeAssignedToApiKeys: false,
objectPermissions: [
{ objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER, ...readOnly },
{ objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER, ...readOnly },
],
});

View File

@@ -0,0 +1,35 @@
import {
defineView,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
ViewFilterOperand,
ViewType,
} from 'twenty-sdk/define';
import { PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-type.field';
import { LISTED_PROPERTIES_ON_PERSON_ID } from '../fields/listing-agent-on-property.field';
export const AGENTS_VIEW_ID = '799dbc65-52e7-4533-874c-aeac05f599fa';
const personFields = STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields;
export default defineView({
universalIdentifier: AGENTS_VIEW_ID,
name: 'Agents',
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: ViewType.TABLE,
icon: 'IconUserStar',
position: 10,
fields: [
{ universalIdentifier: 'be978ab0-6e04-4c63-a233-982310142296', fieldMetadataUniversalIdentifier: personFields.name.universalIdentifier, position: 0, isVisible: true, size: 220 },
{ universalIdentifier: '7543b11d-9058-420e-912b-558a9623d806', fieldMetadataUniversalIdentifier: PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER, position: 1, isVisible: true, size: 130 },
{ universalIdentifier: 'dde38d0d-2e36-4cc5-9205-3fb2b6a19612', fieldMetadataUniversalIdentifier: LISTED_PROPERTIES_ON_PERSON_ID, position: 2, isVisible: true, size: 260 },
],
filters: [
{
universalIdentifier: '121391c6-c578-4b67-bab0-6e8ce8ead74b',
fieldMetadataUniversalIdentifier: PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER,
operand: ViewFilterOperand.IS,
value: ['AGENT'],
},
],
});

View File

@@ -0,0 +1,52 @@
import { defineView, ViewType } from 'twenty-sdk/define';
import {
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_UNIVERSAL_IDENTIFIER,
} from '../objects/property.object';
import { LISTING_AGENT_ON_PROPERTY_ID } from '../fields/listing-agent-on-property.field';
export const ALL_PROPERTIES_VIEW_ID = '3cae86f2-c7ea-45ee-8292-507dc5aa7dcf';
export default defineView({
universalIdentifier: ALL_PROPERTIES_VIEW_ID,
name: 'All Properties',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: ViewType.TABLE,
icon: 'IconHome',
position: 0,
fields: [
{
universalIdentifier: '56b7b9fa-950c-4923-b017-51d6ccef35ed',
fieldMetadataUniversalIdentifier:
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
position: 0,
isVisible: true,
size: 240,
},
{
universalIdentifier: 'b730aca0-0511-4d91-aad8-ea69b90bca8f',
fieldMetadataUniversalIdentifier:
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
position: 1,
isVisible: true,
size: 150,
},
{
universalIdentifier: '8241c0d5-eebd-4d83-b509-7d69acaf7892',
fieldMetadataUniversalIdentifier:
PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
position: 2,
isVisible: true,
size: 150,
},
{
universalIdentifier: 'bbb411f6-51c0-4716-90d8-b1b4b9b1a23a',
fieldMetadataUniversalIdentifier: LISTING_AGENT_ON_PROPERTY_ID,
position: 3,
isVisible: true,
size: 200,
},
],
});

View File

@@ -0,0 +1,68 @@
import { defineView, ViewType } from 'twenty-sdk/define';
import {
SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_UNIVERSAL_IDENTIFIER,
} from '../objects/showing.object';
import { PROPERTY_ON_SHOWING_ID } from '../fields/property-on-showing.field';
import { BUYER_ON_SHOWING_ID } from '../fields/buyer-on-showing.field';
export const ALL_SHOWINGS_VIEW_ID = '618f5217-ad9b-4922-8caa-38413159d872';
export default defineView({
universalIdentifier: ALL_SHOWINGS_VIEW_ID,
name: 'All Showings',
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: ViewType.TABLE,
icon: 'IconCalendarEvent',
position: 0,
fields: [
{
universalIdentifier: '332eaa7b-eb59-428c-98ba-6b7297dd3491',
fieldMetadataUniversalIdentifier: SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
position: 0,
isVisible: true,
size: 240,
},
{
universalIdentifier: 'd64a810a-6749-49b4-a606-ac6b06a346fd',
fieldMetadataUniversalIdentifier: PROPERTY_ON_SHOWING_ID,
position: 1,
isVisible: true,
size: 200,
},
{
universalIdentifier: '47165894-5959-4368-a187-701182f7c963',
fieldMetadataUniversalIdentifier: BUYER_ON_SHOWING_ID,
position: 2,
isVisible: true,
size: 200,
},
{
universalIdentifier: '1be249ea-285c-4e94-b6bb-0673b69e7638',
fieldMetadataUniversalIdentifier:
SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER,
position: 3,
isVisible: true,
size: 160,
},
{
universalIdentifier: '6dd7ec1f-5d5f-473d-bafd-7c40bf644c80',
fieldMetadataUniversalIdentifier:
SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
position: 4,
isVisible: true,
size: 140,
},
{
universalIdentifier: '253f1693-39e2-423f-93ef-87811d0b2297',
fieldMetadataUniversalIdentifier:
SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER,
position: 5,
isVisible: true,
size: 120,
},
],
});

View File

@@ -0,0 +1,46 @@
import {
defineView,
ViewFilterOperand,
ViewSortDirection,
ViewType,
} from 'twenty-sdk/define';
import {
PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
PROPERTY_UNIVERSAL_IDENTIFIER,
} from '../objects/property.object';
import { LISTING_AGENT_ON_PROPERTY_ID } from '../fields/listing-agent-on-property.field';
export const AVAILABLE_PROPERTIES_VIEW_ID =
'6c41e295-bc6f-430f-b526-d1708c5c6153';
export default defineView({
universalIdentifier: AVAILABLE_PROPERTIES_VIEW_ID,
name: 'Available (by price)',
objectUniversalIdentifier: PROPERTY_UNIVERSAL_IDENTIFIER,
type: ViewType.TABLE,
icon: 'IconCoin',
position: 1,
fields: [
{ universalIdentifier: '0a80e4b4-f52b-420a-9372-8143ea9d726a', fieldMetadataUniversalIdentifier: PROPERTY_NAME_FIELD_UNIVERSAL_IDENTIFIER, position: 0, isVisible: true, size: 240 },
{ universalIdentifier: 'f53d191c-c9e2-47c1-8e89-b600f77e667e', fieldMetadataUniversalIdentifier: PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER, position: 1, isVisible: true, size: 150 },
{ universalIdentifier: '3d5d31a7-016a-4d39-8b39-8d850e4c8d90', fieldMetadataUniversalIdentifier: PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 2, isVisible: true, size: 150 },
{ universalIdentifier: '0987e9ff-6210-468b-8639-7af8c64f23f6', fieldMetadataUniversalIdentifier: LISTING_AGENT_ON_PROPERTY_ID, position: 3, isVisible: true, size: 200 },
],
filters: [
{
universalIdentifier: '332e0166-af2b-459c-9de0-045adcf9a275',
fieldMetadataUniversalIdentifier: PROPERTY_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
operand: ViewFilterOperand.IS_NOT,
value: ['SOLD'],
},
],
sorts: [
{
universalIdentifier: 'e0c1ee67-a244-462d-aded-6e73fa1030df',
fieldMetadataUniversalIdentifier: PROPERTY_PRICE_FIELD_UNIVERSAL_IDENTIFIER,
direction: ViewSortDirection.DESC,
},
],
});

View File

@@ -0,0 +1,60 @@
import {
defineView,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
ViewFilterOperand,
ViewType,
} from 'twenty-sdk/define';
import {
BUYER_STAGE_FIELD_ID,
BUYER_STAGE_OPTIONS,
} from '../fields/opportunity-buyer-stage.field';
import { PROPERTY_ON_OPPORTUNITY_ID } from '../fields/property-on-opportunity.field';
import { BUYER_ON_OPPORTUNITY_ID } from '../fields/buyer-on-opportunity.field';
export const BUYER_PIPELINE_VIEW_ID = '4efec37d-ab61-4270-b8d0-f9ff6ff435c9';
const opportunityFields =
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.fields;
const GROUP_IDS = [
'ea85b2b4-4339-4a8d-9f00-b72d436ff315',
'daee17b1-0b3f-4897-905d-7f9f9ffe09ea',
'2c710417-40bc-4e64-902c-04962cec780a',
'2729a148-600e-4bad-b47a-e992ece189ce',
'6dbc865f-d0fc-4659-ae3a-86fc44db6c7a',
'64332ad1-f824-4085-b269-19c9c3f7a97d',
];
export default defineView({
universalIdentifier: BUYER_PIPELINE_VIEW_ID,
name: 'Buyer Pipeline',
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.opportunity.universalIdentifier,
type: ViewType.KANBAN,
icon: 'IconLayoutKanban',
position: 0,
mainGroupByFieldMetadataUniversalIdentifier: BUYER_STAGE_FIELD_ID,
fields: [
{ universalIdentifier: 'aa354542-400a-4d55-bdbb-a7f014d51242', fieldMetadataUniversalIdentifier: opportunityFields.name.universalIdentifier, position: 0, isVisible: true, size: 200 },
{ universalIdentifier: '4eaeae10-80b8-490b-9f24-9a71beec9ac1', fieldMetadataUniversalIdentifier: BUYER_STAGE_FIELD_ID, position: 1, isVisible: true, size: 150 },
{ universalIdentifier: 'e0e095be-1f73-424c-95d6-e0e01e2fc608', fieldMetadataUniversalIdentifier: BUYER_ON_OPPORTUNITY_ID, position: 2, isVisible: true, size: 180 },
{ universalIdentifier: 'f62c744e-2859-4772-b1c5-1584a2e82c95', fieldMetadataUniversalIdentifier: PROPERTY_ON_OPPORTUNITY_ID, position: 3, isVisible: true, size: 180 },
{ universalIdentifier: '1661234f-bd05-4d8e-8716-ab2b17b0e5df', fieldMetadataUniversalIdentifier: opportunityFields.amount.universalIdentifier, position: 4, isVisible: true, size: 150 },
],
groups: BUYER_STAGE_OPTIONS.map((option, index) => ({
universalIdentifier: GROUP_IDS[index],
fieldValue: option.value,
position: index,
isVisible: true,
})),
// Scope the pipeline to real-estate deals (linked to a property),
// excluding any other opportunities in the workspace.
filters: [
{
universalIdentifier: '57cde77d-5f59-42ca-8722-44fe06689817',
fieldMetadataUniversalIdentifier: PROPERTY_ON_OPPORTUNITY_ID,
operand: ViewFilterOperand.IS_NOT_EMPTY,
value: '',
},
],
});

View File

@@ -0,0 +1,40 @@
import {
defineView,
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS,
ViewFilterOperand,
ViewType,
} from 'twenty-sdk/define';
import { PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-type.field';
import { BUDGET_MIN_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-budget-min.field';
import { BUDGET_MAX_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-budget-max.field';
import { PRE_APPROVED_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-pre-approved.field';
import { DESIRED_AREA_FIELD_UNIVERSAL_IDENTIFIER } from '../fields/person-desired-area.field';
export const BUYERS_VIEW_ID = 'd34441a5-51bc-4826-b5cc-828d536de602';
const personFields = STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.fields;
export default defineView({
universalIdentifier: BUYERS_VIEW_ID,
name: 'Buyers',
objectUniversalIdentifier:
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.person.universalIdentifier,
type: ViewType.TABLE,
icon: 'IconUserSearch',
position: 11,
fields: [
{ universalIdentifier: '70f73b71-afc5-44e3-b9d7-8bb89670cd91', fieldMetadataUniversalIdentifier: personFields.name.universalIdentifier, position: 0, isVisible: true, size: 200 },
{ universalIdentifier: '5ab16f37-55f6-4c45-98c4-096ef3087c43', fieldMetadataUniversalIdentifier: DESIRED_AREA_FIELD_UNIVERSAL_IDENTIFIER, position: 1, isVisible: true, size: 160 },
{ universalIdentifier: '2b1766af-894e-4b9a-a974-07953ceea577', fieldMetadataUniversalIdentifier: BUDGET_MIN_FIELD_UNIVERSAL_IDENTIFIER, position: 2, isVisible: true, size: 130 },
{ universalIdentifier: '6dd553b3-1c4b-4cea-9ae4-c600a8215bb4', fieldMetadataUniversalIdentifier: BUDGET_MAX_FIELD_UNIVERSAL_IDENTIFIER, position: 3, isVisible: true, size: 130 },
{ universalIdentifier: 'df024778-8780-4d9b-b78b-316fe5ce519d', fieldMetadataUniversalIdentifier: PRE_APPROVED_FIELD_UNIVERSAL_IDENTIFIER, position: 4, isVisible: true, size: 120 },
],
filters: [
{
universalIdentifier: 'f0550294-9a36-4ffc-a4d8-76ab048a7e6e',
fieldMetadataUniversalIdentifier: PERSON_TYPE_FIELD_UNIVERSAL_IDENTIFIER,
operand: ViewFilterOperand.IS,
value: ['BUYER'],
},
],
});

View File

@@ -0,0 +1,35 @@
import { defineView, ViewType } from 'twenty-sdk/define';
import {
SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER,
SHOWING_UNIVERSAL_IDENTIFIER,
} from '../objects/showing.object';
import { PROPERTY_ON_SHOWING_ID } from '../fields/property-on-showing.field';
import { BUYER_ON_SHOWING_ID } from '../fields/buyer-on-showing.field';
import { AGENT_ON_SHOWING_ID } from '../fields/agent-on-showing.field';
import { OPPORTUNITY_ON_SHOWING_ID } from '../fields/opportunity-on-showing.field';
const SHOWING_FEEDBACK_FIELD_ID = '377e4a00-8398-413a-8662-526236ff2d65';
export const SHOWING_RECORD_PAGE_FIELDS_VIEW_ID =
'aab8b6fa-ba20-4a4c-bd51-e3135e2e5b17';
export default defineView({
universalIdentifier: SHOWING_RECORD_PAGE_FIELDS_VIEW_ID,
name: 'Showing Record Page Fields',
objectUniversalIdentifier: SHOWING_UNIVERSAL_IDENTIFIER,
type: ViewType.FIELDS_WIDGET,
fields: [
{ universalIdentifier: '005556e9-620f-4dd2-8c80-ff3c2ebf34be', fieldMetadataUniversalIdentifier: SHOWING_NAME_FIELD_UNIVERSAL_IDENTIFIER, position: 0, isVisible: true },
{ universalIdentifier: '18573139-b71f-4939-8a6b-0534dcadca4e', fieldMetadataUniversalIdentifier: SHOWING_SCHEDULED_AT_FIELD_UNIVERSAL_IDENTIFIER, position: 1, isVisible: true },
{ universalIdentifier: 'ba643965-84e5-4298-84fa-e743335d1e65', fieldMetadataUniversalIdentifier: SHOWING_STATUS_FIELD_UNIVERSAL_IDENTIFIER, position: 2, isVisible: true },
{ universalIdentifier: '65a7b779-073a-467d-848b-b93bebb22431', fieldMetadataUniversalIdentifier: SHOWING_INTEREST_LEVEL_FIELD_UNIVERSAL_IDENTIFIER, position: 3, isVisible: true },
{ universalIdentifier: 'a6347283-e422-4fa8-aab7-f6ebbdeb462d', fieldMetadataUniversalIdentifier: SHOWING_FEEDBACK_FIELD_ID, position: 4, isVisible: true },
{ universalIdentifier: 'ef43efbf-7660-4a85-bb38-696e9702e2df', fieldMetadataUniversalIdentifier: PROPERTY_ON_SHOWING_ID, position: 5, isVisible: true },
{ universalIdentifier: '7b49387d-494a-4b19-89bb-19cdc31a2bbd', fieldMetadataUniversalIdentifier: BUYER_ON_SHOWING_ID, position: 6, isVisible: true },
{ universalIdentifier: 'dd260533-802a-439d-bc9e-653b47152acc', fieldMetadataUniversalIdentifier: AGENT_ON_SHOWING_ID, position: 7, isVisible: true },
{ universalIdentifier: 'fc77c926-5301-406e-9785-0c55138ea339', fieldMetadataUniversalIdentifier: OPPORTUNITY_ON_SHOWING_ID, position: 8, isVisible: true },
],
});

View File

@@ -0,0 +1,31 @@
{
"compileOnSave": false,
"compilerOptions": {
"sourceMap": true,
"declaration": true,
"outDir": "./dist",
"rootDir": ".",
"jsx": "react-jsx",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"allowUnreachableCode": false,
"strict": true,
"alwaysStrict": true,
"noImplicitAny": true,
"strictBindCallApply": false,
"target": "es2018",
"module": "esnext",
"lib": ["es2020", "dom"],
"skipLibCheck": true,
"skipDefaultLibCheck": true,
"resolveJsonModule": true,
"paths": {
"src/*": ["./src/*"],
"~/*": ["./*"]
}
},
"exclude": ["node_modules", "dist"]
}

File diff suppressed because it is too large Load Diff