mirror of
https://github.com/twentyhq/twenty
synced 2026-04-21 13:37:22 +00:00
Optimize merge queue to only run E2E and integrate prettier into lint (#18459)
## Summary - **Merge queue optimization**: Created a dedicated `ci-merge-queue.yaml` workflow that only runs Playwright E2E tests on `ubuntu-latest-8-cores`. Removed `merge_group` trigger from all 7 existing CI workflows (front, server, shared, website, sdk, zapier, docker-compose). The merge queue goes from ~30+ parallel jobs to a single focused E2E job. - **Label-based merge queue simulation**: Added `run-merge-queue` label support so developers can trigger the exact merge queue E2E pipeline on any open PR before it enters the queue. - **Prettier in lint**: Chained `prettier --check` into `lint` and `prettier --write` into `lint --configuration=fix` across `nx.json` defaults, `twenty-front`, and `twenty-server`. Prettier formatting errors are now caught by `lint` and fixed by `lint:fix` / `lint:diff-with-main --configuration=fix`. ## After merge (manual repo settings) Update GitHub branch protection required status checks: 1. Remove old per-workflow merge queue checks (`ci-front-status-check`, `ci-e2e-status-check`, `ci-server-status-check`, etc.) 2. Add `ci-merge-queue-status-check` as the required check for the merge queue
This commit is contained in:
parent
d825ac06dd
commit
d37ed7e07c
82 changed files with 673 additions and 616 deletions
133
.github/workflows/ci-front.yaml
vendored
133
.github/workflows/ci-front.yaml
vendored
|
|
@ -1,10 +1,8 @@
|
|||
name: CI Front and E2E
|
||||
name: CI Front
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
|
@ -29,15 +27,6 @@ jobs:
|
|||
packages/twenty-shared/**
|
||||
packages/twenty-sdk/**
|
||||
!packages/twenty-sdk/package.json
|
||||
changed-files-check-e2e:
|
||||
uses: ./.github/workflows/changed-files.yaml
|
||||
with:
|
||||
files: |
|
||||
packages/**
|
||||
!packages/create-twenty-app/package.json
|
||||
!packages/twenty-sdk/package.json
|
||||
playwright.config.ts
|
||||
.github/workflows/ci-front.yaml
|
||||
front-sb-build:
|
||||
needs: changed-files-check
|
||||
if: needs.changed-files-check.outputs.any_changed == 'true'
|
||||
|
|
@ -257,117 +246,6 @@ jobs:
|
|||
# name: frontend-build
|
||||
# path: packages/twenty-front/build
|
||||
# retention-days: 1
|
||||
e2e-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changed-files-check-e2e, front-build]
|
||||
if: |
|
||||
always() &&
|
||||
needs.changed-files-check-e2e.outputs.any_changed == 'true' &&
|
||||
(needs.front-build.result == 'success' || needs.front-build.result == 'skipped') &&
|
||||
(github.event_name == 'push' || github.event_name == 'merge_group' || (github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'run-e2e')))
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=10240"
|
||||
services:
|
||||
postgres:
|
||||
image: twentycrm/twenty-postgres-spilo
|
||||
env:
|
||||
PGUSER_SUPERUSER: postgres
|
||||
PGPASSWORD_SUPERUSER: postgres
|
||||
ALLOW_NOSSL: "true"
|
||||
SPILO_PROVIDER: "local"
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 10
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Check system resources
|
||||
run: |
|
||||
echo "Available memory:"
|
||||
free -h
|
||||
echo "Available disk space:"
|
||||
df -h
|
||||
echo "CPU info:"
|
||||
lscpu
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
|
||||
- name: Build twenty-shared
|
||||
run: npx nx build twenty-shared
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx nx setup twenty-e2e-testing
|
||||
|
||||
- name: Setup environment files
|
||||
run: |
|
||||
cp packages/twenty-front/.env.example packages/twenty-front/.env
|
||||
npx nx reset:env:e2e-testing-server twenty-server
|
||||
|
||||
# - name: Download frontend build artifact
|
||||
# if: needs.front-build.result == 'success'
|
||||
# uses: actions/download-artifact@v4
|
||||
# with:
|
||||
# name: frontend-build
|
||||
# path: packages/twenty-front/build
|
||||
|
||||
# - name: Build frontend (if not available from front-build)
|
||||
# if: needs.front-build.result == 'skipped'
|
||||
# run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
|
||||
|
||||
- name: Build frontend
|
||||
run: NODE_ENV=production NODE_OPTIONS="--max-old-space-size=10240" npx nx build twenty-front
|
||||
|
||||
- name: Build server
|
||||
run: npx nx build twenty-server
|
||||
|
||||
- name: Create and setup database
|
||||
run: |
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "default";'
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
|
||||
npx nx run twenty-server:database:reset
|
||||
|
||||
- name: Start server
|
||||
run: |
|
||||
npx nx start twenty-server &
|
||||
echo "Waiting for server to be ready..."
|
||||
timeout 60 bash -c 'until curl -s http://localhost:3000/health; do sleep 2; done'
|
||||
|
||||
- name: Start frontend
|
||||
run: |
|
||||
npm_config_yes=true npx serve -s packages/twenty-front/build -l 3001 &
|
||||
echo "Waiting for frontend to be ready..."
|
||||
timeout 60 bash -c 'until curl -s http://localhost:3001; do sleep 2; done'
|
||||
|
||||
- name: Start worker
|
||||
run: |
|
||||
npx nx run twenty-server:worker &
|
||||
echo "Worker started"
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: npx nx test twenty-e2e-testing
|
||||
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# if: always()
|
||||
# with:
|
||||
# name: playwright-report
|
||||
# path: packages/twenty-e2e-testing/run_results/
|
||||
# retention-days: 30
|
||||
|
||||
ci-front-status-check:
|
||||
if: always() && !cancelled()
|
||||
timeout-minutes: 5
|
||||
|
|
@ -385,12 +263,3 @@ jobs:
|
|||
- name: Fail job if any needs failed
|
||||
if: contains(needs.*.result, 'failure')
|
||||
run: exit 1
|
||||
ci-e2e-status-check:
|
||||
if: always() && !cancelled()
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
needs: [changed-files-check-e2e, e2e-test]
|
||||
steps:
|
||||
- name: Fail job if any needs failed
|
||||
if: contains(needs.*.result, 'failure')
|
||||
run: exit 1
|
||||
|
|
|
|||
118
.github/workflows/ci-merge-queue.yaml
vendored
Normal file
118
.github/workflows/ci-merge-queue.yaml
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
name: CI Merge Queue
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
pull_request:
|
||||
types: [labeled, synchronize, opened, reopened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
e2e-test:
|
||||
if: >
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
contains(github.event.pull_request.labels.*.name, 'run-merge-queue'))
|
||||
runs-on: ubuntu-latest-8-cores
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=10240"
|
||||
services:
|
||||
postgres:
|
||||
image: twentycrm/twenty-postgres-spilo
|
||||
env:
|
||||
PGUSER_SUPERUSER: postgres
|
||||
PGPASSWORD_SUPERUSER: postgres
|
||||
ALLOW_NOSSL: "true"
|
||||
SPILO_PROVIDER: "local"
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: lts/*
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./.github/actions/yarn-install
|
||||
|
||||
- name: Build twenty-shared
|
||||
run: npx nx build twenty-shared
|
||||
|
||||
- name: Install Playwright Browsers
|
||||
run: npx nx setup twenty-e2e-testing
|
||||
|
||||
- name: Setup environment files
|
||||
run: |
|
||||
cp packages/twenty-front/.env.example packages/twenty-front/.env
|
||||
npx nx reset:env:e2e-testing-server twenty-server
|
||||
|
||||
- name: Build frontend
|
||||
run: NODE_ENV=production npx nx build twenty-front
|
||||
|
||||
- name: Build server
|
||||
run: npx nx build twenty-server
|
||||
|
||||
- name: Create and setup database
|
||||
run: |
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "default";'
|
||||
PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";'
|
||||
npx nx run twenty-server:database:reset
|
||||
|
||||
- name: Start server
|
||||
run: |
|
||||
npx nx start twenty-server &
|
||||
echo "Waiting for server to be ready..."
|
||||
timeout 60 bash -c 'until curl -s http://localhost:3000/health; do sleep 2; done'
|
||||
|
||||
- name: Start frontend
|
||||
run: |
|
||||
npm_config_yes=true npx serve -s packages/twenty-front/build -l 3001 &
|
||||
echo "Waiting for frontend to be ready..."
|
||||
timeout 60 bash -c 'until curl -s http://localhost:3001; do sleep 2; done'
|
||||
|
||||
- name: Start worker
|
||||
run: |
|
||||
npx nx run twenty-server:worker &
|
||||
echo "Worker started"
|
||||
|
||||
- name: Run Playwright tests
|
||||
run: npx nx test twenty-e2e-testing
|
||||
|
||||
- name: Upload Playwright results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: playwright-results
|
||||
path: |
|
||||
packages/twenty-e2e-testing/run_results/
|
||||
packages/twenty-e2e-testing/test-results/
|
||||
retention-days: 7
|
||||
|
||||
ci-merge-queue-status-check:
|
||||
if: always() && !cancelled()
|
||||
timeout-minutes: 5
|
||||
runs-on: ubuntu-latest
|
||||
needs: [e2e-test]
|
||||
steps:
|
||||
- name: Fail job if any needs failed
|
||||
if: contains(needs.*.result, 'failure')
|
||||
run: exit 1
|
||||
2
.github/workflows/ci-sdk.yaml
vendored
2
.github/workflows/ci-sdk.yaml
vendored
|
|
@ -1,8 +1,6 @@
|
|||
name: CI SDK
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
|
|
|
|||
2
.github/workflows/ci-server.yaml
vendored
2
.github/workflows/ci-server.yaml
vendored
|
|
@ -3,8 +3,6 @@ name: CI Server
|
|||
on:
|
||||
pull_request:
|
||||
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
|
|
|||
2
.github/workflows/ci-shared.yaml
vendored
2
.github/workflows/ci-shared.yaml
vendored
|
|
@ -1,8 +1,6 @@
|
|||
name: CI Shared
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ permissions:
|
|||
contents: read
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
|
|
|
|||
2
.github/workflows/ci-website.yaml
vendored
2
.github/workflows/ci-website.yaml
vendored
|
|
@ -4,8 +4,6 @@ permissions:
|
|||
contents: read
|
||||
|
||||
on:
|
||||
merge_group:
|
||||
|
||||
pull_request:
|
||||
|
||||
concurrency:
|
||||
|
|
|
|||
2
.github/workflows/ci-zapier.yaml
vendored
2
.github/workflows/ci-zapier.yaml
vendored
|
|
@ -3,8 +3,6 @@ name: CI Zapier
|
|||
on:
|
||||
pull_request:
|
||||
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
|
|
|
|||
8
nx.json
8
nx.json
|
|
@ -44,12 +44,12 @@
|
|||
"cache": true,
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx oxlint -c .oxlintrc.json ."
|
||||
"command": "npx oxlint -c .oxlintrc.json . && (prettier . --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint --configuration=fix' && false))"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {},
|
||||
"fix": {
|
||||
"command": "npx oxlint --fix -c .oxlintrc.json ."
|
||||
"command": "npx oxlint --fix -c .oxlintrc.json . && prettier . --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata"
|
||||
}
|
||||
},
|
||||
"dependsOn": ["^build", "twenty-oxlint-rules:build"]
|
||||
|
|
@ -58,12 +58,12 @@
|
|||
"executor": "nx:run-commands",
|
||||
"cache": false,
|
||||
"options": {
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || npx oxlint -c {projectRoot}/.oxlintrc.json $FILES",
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint -c {projectRoot}/.oxlintrc.json $FILES && (prettier --check $FILES || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint:diff-with-main --configuration=fix' && false)))",
|
||||
"pattern": "\\.(ts|tsx|js|jsx)$"
|
||||
},
|
||||
"configurations": {
|
||||
"fix": {
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || npx oxlint --fix -c {projectRoot}/.oxlintrc.json $FILES"
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main -- {projectRoot}/ | grep -E '{args.pattern}'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --fix -c {projectRoot}/.oxlintrc.json $FILES && prettier --write $FILES)"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -17,22 +17,31 @@
|
|||
"import/no-duplicates": "error",
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}]
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
packages/create-twenty-app/.prettierignore
Normal file
1
packages/create-twenty-app/.prettierignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
dist
|
||||
|
|
@ -19,9 +19,11 @@ Create Twenty App is the official scaffolding CLI for building apps on top of [T
|
|||
- Strong TypeScript support and typed client generation
|
||||
|
||||
## Documentation
|
||||
|
||||
See Twenty application documentation https://docs.twenty.com/developers/extend/capabilities/apps
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 24+ (recommended) and Yarn 4
|
||||
- A Twenty workspace and an API key (create one at https://app.twenty.com/settings/api-webhooks)
|
||||
|
||||
|
|
@ -64,11 +66,11 @@ yarn twenty app:uninstall
|
|||
|
||||
Control which example files are included when creating a new app:
|
||||
|
||||
| Flag | Behavior |
|
||||
|------|----------|
|
||||
| `-e, --exhaustive` | **(default)** Creates all example files without prompting |
|
||||
| `-m, --minimal` | Creates only core files (`application-config.ts` and `default-role.ts`) |
|
||||
| `-i, --interactive` | Prompts you to select which examples to include |
|
||||
| Flag | Behavior |
|
||||
| ------------------- | ----------------------------------------------------------------------- |
|
||||
| `-e, --exhaustive` | **(default)** Creates all example files without prompting |
|
||||
| `-m, --minimal` | Creates only core files (`application-config.ts` and `default-role.ts`) |
|
||||
| `-i, --interactive` | Prompts you to select which examples to include |
|
||||
|
||||
```bash
|
||||
# Default: all examples included
|
||||
|
|
@ -82,6 +84,7 @@ npx create-twenty-app@latest my-app -i
|
|||
```
|
||||
|
||||
In interactive mode, you can pick from:
|
||||
|
||||
- **Example object** — a custom CRM object definition (`objects/example-object.ts`)
|
||||
- **Example field** — a custom field on the example object (`fields/example-field.ts`)
|
||||
- **Example logic function** — a server-side handler with HTTP trigger (`logic-functions/hello-world.ts`)
|
||||
|
|
@ -94,6 +97,7 @@ In interactive mode, you can pick from:
|
|||
## What gets scaffolded
|
||||
|
||||
**Core files (always created):**
|
||||
|
||||
- `application-config.ts` — Application metadata configuration
|
||||
- `roles/default-role.ts` — Default role for logic functions
|
||||
- `logic-functions/pre-install.ts` — Pre-install logic function (runs before app installation)
|
||||
|
|
@ -102,6 +106,7 @@ In interactive mode, you can pick from:
|
|||
- A prewired `twenty` script that delegates to the `twenty` CLI from twenty-sdk
|
||||
|
||||
**Example files (controlled by scaffolding mode):**
|
||||
|
||||
- `objects/example-object.ts` — Example custom object with a text field
|
||||
- `fields/example-field.ts` — Example standalone field extending the example object
|
||||
- `logic-functions/hello-world.ts` — Example logic function with HTTP trigger
|
||||
|
|
@ -112,14 +117,15 @@ In interactive mode, you can pick from:
|
|||
- `__tests__/app-install.integration-test.ts` — Integration test that builds, installs, and verifies the app (includes `vitest.config.ts`, `tsconfig.spec.json`, and a setup file)
|
||||
|
||||
## Next steps
|
||||
|
||||
- Run `yarn twenty help` to see all available commands.
|
||||
- Use `yarn twenty auth:login` to authenticate with your Twenty workspace.
|
||||
- Explore the generated project and add your first entity with `yarn twenty entity:add` (logic functions, front components, objects, roles, views, navigation menu items, skills).
|
||||
- Use `yarn twenty app:dev` while you iterate — it watches, builds, and syncs changes to your workspace in real time.
|
||||
- Two typed API clients are auto‑generated by `yarn twenty app:dev` and stored in `node_modules/twenty-sdk/generated`: `CoreApiClient` (for workspace data via `/graphql`) and `MetadataApiClient` (for workspace configuration and file uploads via `/metadata`).
|
||||
|
||||
|
||||
## Publish your application
|
||||
|
||||
Applications are currently stored in `twenty/packages/twenty-apps`.
|
||||
|
||||
You can share your application with all Twenty users:
|
||||
|
|
@ -144,9 +150,11 @@ git push
|
|||
Our team reviews contributions for quality, security, and reusability before merging.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Auth prompts not appearing: run `yarn twenty auth:login` again and verify the API key permissions.
|
||||
- Types not generated: ensure `yarn twenty app:dev` is running — it auto‑generates the typed client.
|
||||
|
||||
## Contributing
|
||||
|
||||
- See our [GitHub](https://github.com/twentyhq/twenty)
|
||||
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,12 @@
|
|||
"rules": {
|
||||
"no-unused-vars": "off",
|
||||
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"typescript/no-explicit-any": "off"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
- Rich app example: https://github.com/twentyhq/twenty/tree/main/packages/twenty-sdk/src/cli/__tests__/apps/rich-app
|
||||
|
||||
## UUID requirement
|
||||
|
||||
- All generated UUIDs must be valid UUID v4.
|
||||
|
||||
## Common Pitfalls
|
||||
|
|
|
|||
|
|
@ -27,5 +27,11 @@
|
|||
"~/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts", "**/*.integration-test.ts"]
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"dist",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.integration-test.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,5 @@
|
|||
"vite.config.ts",
|
||||
"jest.config.mjs"
|
||||
],
|
||||
"exclude": [
|
||||
"src/constants/base-application/vitest.config.ts"
|
||||
]
|
||||
"exclude": ["src/constants/base-application/vitest.config.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ export class LoginPage {
|
|||
|
||||
async clickLoginWithEmailIfVisible() {
|
||||
try {
|
||||
await this.loginWithEmailButton.click();
|
||||
await this.loginWithEmailButton.click({ timeout: 3000 });
|
||||
} catch {
|
||||
// Button not found - email field might already be visible (SSO-only or different auth flow)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ test('Sign up with invite link via email', async ({
|
|||
});
|
||||
|
||||
await test.step('Create new account', async () => {
|
||||
await loginPage.clickLoginWithEmail();
|
||||
await loginPage.clickLoginWithEmailIfVisible();
|
||||
await loginPage.typeEmail(email);
|
||||
await loginPage.clickContinueButton();
|
||||
await loginPage.typePassword(process.env.DEFAULT_PASSWORD);
|
||||
|
|
|
|||
|
|
@ -34,10 +34,7 @@ test('Create workflow', async ({ page }) => {
|
|||
|
||||
const recordName = page.getByTestId('top-bar-title').getByPlaceholder('Name');
|
||||
await expect(recordName).toBeVisible();
|
||||
await recordName.click();
|
||||
|
||||
const nameInput = page.getByTestId('top-bar-title').getByRole('textbox');
|
||||
await nameInput.fill(NEW_WORKFLOW_NAME);
|
||||
await recordName.fill(NEW_WORKFLOW_NAME);
|
||||
|
||||
const workflowDiagramContainer = page.locator('.react-flow__renderer');
|
||||
await workflowDiagramContainer.click();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
"categories": {
|
||||
"correctness": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"node_modules"
|
||||
],
|
||||
"ignorePatterns": ["node_modules"],
|
||||
"rules": {
|
||||
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
|
||||
"no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }],
|
||||
"no-console": [
|
||||
"warn",
|
||||
{ "allow": ["group", "groupCollapsed", "groupEnd"] }
|
||||
],
|
||||
"no-control-regex": "off",
|
||||
"no-debugger": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
|
|
@ -34,31 +35,43 @@
|
|||
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
|
||||
"twenty/enforce-module-boundaries": ["error", {
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "scope:backend",
|
||||
"onlyDependOnLibsWithTags": ["scope:shared", "scope:backend"]
|
||||
}
|
||||
]
|
||||
}]
|
||||
"twenty/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "scope:backend",
|
||||
"onlyDependOnLibsWithTags": ["scope:shared", "scope:backend"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
1
packages/twenty-emails/.prettierignore
Normal file
1
packages/twenty-emails/.prettierignore
Normal file
|
|
@ -0,0 +1 @@
|
|||
src/locales/generated
|
||||
|
|
@ -5,12 +5,12 @@
|
|||
"plugins": [
|
||||
[
|
||||
"@lingui/swc-plugin",
|
||||
{
|
||||
"runtimeModules": {
|
||||
"i18n": ["@lingui/core", "i18n"],
|
||||
"trans": ["@lingui/react", "Trans"]
|
||||
},
|
||||
"stripNonEssentialFields": false
|
||||
{
|
||||
"runtimeModules": {
|
||||
"i18n": ["@lingui/core", "i18n"],
|
||||
"trans": ["@lingui/react", "Trans"]
|
||||
},
|
||||
"stripNonEssentialFields": false
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,9 +13,5 @@
|
|||
"src/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"vite.config.ts"
|
||||
]
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,10 +7,5 @@
|
|||
"types": ["node", "@nx/react/typings/image.d.ts", "vite/client"]
|
||||
},
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"],
|
||||
"exclude": [
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx"
|
||||
]
|
||||
"exclude": ["**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
src/generated
|
||||
src/generated-metadata
|
||||
src/locales/generated
|
||||
src/testing/mock-data/generated
|
||||
|
|
@ -77,11 +77,11 @@
|
|||
],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx oxlint --type-aware -c .oxlintrc.json src/"
|
||||
"command": "npx oxlint --type-aware -c .oxlintrc.json src/ && (prettier src/ --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint twenty-front --configuration=fix' && false))"
|
||||
},
|
||||
"configurations": {
|
||||
"fix": {
|
||||
"command": "npx oxlint --type-aware --fix -c .oxlintrc.json src/"
|
||||
"command": "npx oxlint --type-aware --fix -c .oxlintrc.json src/ && prettier src/ --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -89,11 +89,11 @@
|
|||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main...HEAD -- src/ | grep -E '\\.(ts|tsx)$'); [ -z \"$FILES\" ] && echo 'No changed files.' || npx oxlint --type-aware -c .oxlintrc.json $FILES"
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main...HEAD -- src/ | grep -E '\\.(ts|tsx)$'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --type-aware -c .oxlintrc.json $FILES && (prettier --check $FILES || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint:diff-with-main twenty-front --configuration=fix' && false)))"
|
||||
},
|
||||
"configurations": {
|
||||
"fix": {
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main...HEAD -- src/ | grep -E '\\.(ts|tsx)$'); [ -z \"$FILES\" ] && echo 'No changed files.' || npx oxlint --type-aware --fix -c .oxlintrc.json $FILES"
|
||||
"command": "FILES=$(git diff --name-only --diff-filter=d main...HEAD -- src/ | grep -E '\\.(ts|tsx)$'); [ -z \"$FILES\" ] && echo 'No changed files.' || (npx oxlint --type-aware --fix -c .oxlintrc.json $FILES && prettier --write $FILES)"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ export const TimelineCard = () => {
|
|||
const { timelineActivities, loading, fetchMoreRecords } =
|
||||
useTimelineActivities(targetRecord);
|
||||
|
||||
const isTimelineActivitiesEmpty =
|
||||
timelineActivities.length === 0;
|
||||
const isTimelineActivitiesEmpty = timelineActivities.length === 0;
|
||||
|
||||
if (loading === true) {
|
||||
return <SkeletonLoader withSubSections />;
|
||||
|
|
|
|||
|
|
@ -31,11 +31,9 @@ export const getActivityTargetObjectRecords = ({
|
|||
|
||||
const targets = activityTargets
|
||||
? activityTargets
|
||||
: 'noteTargets' in activityRecord &&
|
||||
isDefined(activityRecord.noteTargets)
|
||||
: 'noteTargets' in activityRecord && isDefined(activityRecord.noteTargets)
|
||||
? activityRecord.noteTargets
|
||||
: 'taskTargets' in activityRecord &&
|
||||
isDefined(activityRecord.taskTargets)
|
||||
: 'taskTargets' in activityRecord && isDefined(activityRecord.taskTargets)
|
||||
? activityRecord.taskTargets
|
||||
: [];
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ export const RoutingStatusDisplay = ({
|
|||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const isLoading = data.state === 'loading';
|
||||
const isDebugMode = process.env.IS_DEBUG_MODE === 'true';
|
||||
const isExpandable = isDebugMode && data.state === 'routed' && isDefined(data.debug);
|
||||
const isExpandable =
|
||||
isDebugMode && data.state === 'routed' && isDefined(data.debug);
|
||||
|
||||
if (data.state === 'error') {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -117,56 +117,108 @@ export const useCreateAppRouter = (
|
|||
<Route path={AppPath.VerifyEmail} element={<VerifyEmailEffect />} />
|
||||
<Route
|
||||
path={AppPath.SignInUp}
|
||||
element={<LazyRoute><SignInUp /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<SignInUp />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.Invite}
|
||||
element={<LazyRoute><SignInUp /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<SignInUp />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.ResetPassword}
|
||||
element={<LazyRoute><PasswordReset /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<PasswordReset />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.CreateWorkspace}
|
||||
element={<LazyRoute><CreateWorkspace /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<CreateWorkspace />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.CreateProfile}
|
||||
element={<LazyRoute><CreateProfile /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<CreateProfile />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.SyncEmails}
|
||||
element={<LazyRoute><SyncEmails /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<SyncEmails />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.InviteTeam}
|
||||
element={<LazyRoute><InviteTeam /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<InviteTeam />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.PlanRequired}
|
||||
element={<LazyRoute><ChooseYourPlan /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<ChooseYourPlan />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.PlanRequiredSuccess}
|
||||
element={<LazyRoute><PaymentSuccess /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<PaymentSuccess />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.BookCallDecision}
|
||||
element={<LazyRoute><BookCallDecision /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<BookCallDecision />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.BookCall}
|
||||
element={<LazyRoute><BookCall /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<BookCall />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route path={indexAppPath.getIndexAppPath()} element={<></>} />
|
||||
<Route
|
||||
path={AppPath.RecordIndexPage}
|
||||
element={<LazyRoute><RecordIndexPage /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<RecordIndexPage />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.RecordShowPage}
|
||||
element={<LazyRoute><RecordShowPage /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<RecordShowPage />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={AppPath.SettingsCatchAll}
|
||||
|
|
@ -179,13 +231,21 @@ export const useCreateAppRouter = (
|
|||
/>
|
||||
<Route
|
||||
path={AppPath.NotFoundWildcard}
|
||||
element={<LazyRoute><NotFound /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<NotFound />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
<Route element={<BlankLayout />}>
|
||||
<Route
|
||||
path={AppPath.Authorize}
|
||||
element={<LazyRoute><Authorize /></LazyRoute>}
|
||||
element={
|
||||
<LazyRoute>
|
||||
<Authorize />
|
||||
</LazyRoute>
|
||||
}
|
||||
/>
|
||||
</Route>
|
||||
</Route>,
|
||||
|
|
|
|||
|
|
@ -97,7 +97,12 @@ export const MeteredPriceSelector = ({
|
|||
selectedPriceId !== currentMeteredPrice?.stripePriceId;
|
||||
|
||||
const isUpgrade = () => {
|
||||
if (!isChanged || !isDefined(selectedPrice) || !isDefined(currentMeteredPrice)) return false;
|
||||
if (
|
||||
!isChanged ||
|
||||
!isDefined(selectedPrice) ||
|
||||
!isDefined(currentMeteredPrice)
|
||||
)
|
||||
return false;
|
||||
return (
|
||||
(selectedPrice.tiers as BillingPriceTiers)[0].flatAmount >
|
||||
(currentMeteredPrice.tiers as BillingPriceTiers)[0].flatAmount
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ export const FavoriteFolderPickerEffect = ({
|
|||
.filter(
|
||||
(favorite) =>
|
||||
favorite.recordId === targetId &&
|
||||
isDefined(favorite.forWorkspaceMemberId),
|
||||
isDefined(favorite.forWorkspaceMemberId),
|
||||
)
|
||||
.map((favorite) => favorite.favoriteFolderId ?? 'no-folder');
|
||||
setFavoriteFolderPickerChecked(checkedFolderIds);
|
||||
|
|
|
|||
|
|
@ -50,8 +50,7 @@ export const useMentionSearch = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const searchRecords =
|
||||
data?.search.edges.map((edge) => edge.node) ?? [];
|
||||
const searchRecords = data?.search.edges.map((edge) => edge.node) ?? [];
|
||||
|
||||
return searchRecords.map((searchRecord) => ({
|
||||
recordId: searchRecord.recordId,
|
||||
|
|
|
|||
|
|
@ -56,7 +56,11 @@ export const useFindOneRecord = <T extends ObjectRecord = ObjectRecord>({
|
|||
const { data, loading, error, refetch } = useQuery<{
|
||||
[nameSingular: string]: RecordGqlNode;
|
||||
}>(findOneRecordQuery, {
|
||||
skip: !isDefined(objectMetadataItem) || !objectRecordId || skip || !hasReadPermission,
|
||||
skip:
|
||||
!isDefined(objectMetadataItem) ||
|
||||
!objectRecordId ||
|
||||
skip ||
|
||||
!hasReadPermission,
|
||||
variables: { objectRecordId },
|
||||
client: apolloCoreClient,
|
||||
onCompleted: (data) => {
|
||||
|
|
|
|||
|
|
@ -93,8 +93,7 @@ export const RecordTableVirtualizedInitialDataLoadEffect = () => {
|
|||
JSON.stringify(lastContextStoreVirtualizedVisibleRecordFields) !==
|
||||
JSON.stringify(visibleRecordFields)
|
||||
) {
|
||||
const lastFields =
|
||||
lastContextStoreVirtualizedVisibleRecordFields ?? [];
|
||||
const lastFields = lastContextStoreVirtualizedVisibleRecordFields ?? [];
|
||||
const currentFields = visibleRecordFields ?? [];
|
||||
|
||||
setLastContextStoreVirtualizedVisibleRecordFields(visibleRecordFields);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ export const SettingsOptionCardContentButton = ({
|
|||
</StyledSettingsCardDescription>
|
||||
)}
|
||||
</StyledSettingsCardTextContainer>
|
||||
{isDefined(Button) && <StyledButtonContainer>{Button}</StyledButtonContainer>}
|
||||
{isDefined(Button) && (
|
||||
<StyledButtonContainer>{Button}</StyledButtonContainer>
|
||||
)}
|
||||
</StyledSettingsCardContent>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -110,7 +110,9 @@ export const SettingsRolePermissionsObjectLevelRecordLevelPermissionMeValueSelec
|
|||
}
|
||||
}
|
||||
|
||||
const compatibleWorkspaceMemberFields = !isDefined(workspaceMemberMetadataItem)
|
||||
const compatibleWorkspaceMemberFields = !isDefined(
|
||||
workspaceMemberMetadataItem,
|
||||
)
|
||||
? []
|
||||
: workspaceMemberMetadataItem.fields.filter((field) => {
|
||||
if (
|
||||
|
|
|
|||
|
|
@ -28,14 +28,12 @@ export const SubMatchingSelectInput = ({
|
|||
|
||||
const optionsToSelect = useMemo(() => {
|
||||
const searchTerm = normalizeSearchText(searchFilter);
|
||||
return (
|
||||
options.filter((option) => {
|
||||
return (
|
||||
option.value !== selectedOption?.value &&
|
||||
normalizeSearchText(option.label).includes(searchTerm)
|
||||
);
|
||||
})
|
||||
);
|
||||
return options.filter((option) => {
|
||||
return (
|
||||
option.value !== selectedOption?.value &&
|
||||
normalizeSearchText(option.label).includes(searchTerm)
|
||||
);
|
||||
});
|
||||
}, [options, searchFilter, selectedOption?.value]);
|
||||
|
||||
const optionsInDropDown = useMemo(
|
||||
|
|
|
|||
|
|
@ -206,11 +206,11 @@ export const ValidationStep = ({
|
|||
if (filterByErrors) {
|
||||
return data.filter((value) => {
|
||||
if (isDefined(value?.__errors)) {
|
||||
return (
|
||||
(Object.values(value.__errors)?.filter(
|
||||
(err) => err.level === 'error',
|
||||
).length ?? 0) > 0
|
||||
);
|
||||
return (
|
||||
(Object.values(value.__errors)?.filter(
|
||||
(err) => err.level === 'error',
|
||||
).length ?? 0) > 0
|
||||
);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,14 +58,12 @@ export const SelectInput = ({
|
|||
|
||||
const optionsToSelect = useMemo(() => {
|
||||
const searchTerm = normalizeSearchText(searchFilter);
|
||||
return (
|
||||
options.filter((option) => {
|
||||
return (
|
||||
option.value !== selectedOption?.value &&
|
||||
normalizeSearchText(option.label).includes(searchTerm)
|
||||
);
|
||||
})
|
||||
);
|
||||
return options.filter((option) => {
|
||||
return (
|
||||
option.value !== selectedOption?.value &&
|
||||
normalizeSearchText(option.label).includes(searchTerm)
|
||||
);
|
||||
});
|
||||
}, [options, searchFilter, selectedOption?.value]);
|
||||
|
||||
const optionsInDropDown = useMemo(
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ export const useSystemColorScheme = (): ColorScheme => {
|
|||
);
|
||||
|
||||
const [preferredColorScheme, setPreferredColorScheme] = useState<ColorScheme>(
|
||||
isUndefinedOrNull(window.matchMedia) || !mediaQuery.matches ? 'Light' : 'Dark',
|
||||
isUndefinedOrNull(window.matchMedia) || !mediaQuery.matches
|
||||
? 'Light'
|
||||
: 'Dark',
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export const ViewPickerOptionDropdown = ({
|
|||
: favorites.some(
|
||||
(favorite) =>
|
||||
favorite.recordId === view.id &&
|
||||
isDefined(favorite.forWorkspaceMemberId),
|
||||
isDefined(favorite.forWorkspaceMemberId),
|
||||
);
|
||||
|
||||
const handleDelete = () => {
|
||||
|
|
|
|||
|
|
@ -116,7 +116,8 @@ export const WorkflowStepFilterValueInput = ({
|
|||
const isDisabled = !stepFilter.operand;
|
||||
|
||||
const operandHasNoInput =
|
||||
(isDefined(stepFilter) && !configurableViewFilterOperands.has(stepFilter.operand)) ??
|
||||
(isDefined(stepFilter) &&
|
||||
!configurableViewFilterOperands.has(stepFilter.operand)) ??
|
||||
true;
|
||||
|
||||
const {
|
||||
|
|
|
|||
|
|
@ -144,7 +144,11 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
|
|||
...trigger,
|
||||
settings: {
|
||||
...trigger.settings,
|
||||
fields: isDefined(fields) ? (Array.isArray(fields) ? fields : [fields]) : null,
|
||||
fields: isDefined(fields)
|
||||
? Array.isArray(fields)
|
||||
? fields
|
||||
: [fields]
|
||||
: null,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -121,7 +121,10 @@ export const WorkflowEditTriggerManual = ({
|
|||
options={availableMetadata}
|
||||
disabled={triggerOptions.readonly}
|
||||
onChange={(objectNameSingular) => {
|
||||
if (triggerOptions.readonly === true || !isDefined(availability)) {
|
||||
if (
|
||||
triggerOptions.readonly === true ||
|
||||
!isDefined(availability)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ export const useMapFieldMetadataItemToSettingsObjectDetailTableItem = (
|
|||
fieldMetadataItem,
|
||||
fieldType: fieldType ?? '',
|
||||
dataType:
|
||||
(isDefined(relationObjectMetadataItem?.labelPlural) ||
|
||||
isFieldTypeSupportedInSettings(fieldMetadataType))
|
||||
isDefined(relationObjectMetadataItem?.labelPlural) ||
|
||||
isFieldTypeSupportedInSettings(fieldMetadataType)
|
||||
? getSettingsFieldTypeConfig(fieldMetadataType as SettingsFieldType)
|
||||
?.label
|
||||
: '',
|
||||
|
|
|
|||
5
packages/twenty-oxlint-rules/package.json
Normal file
5
packages/twenty-oxlint-rules/package.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "twenty-oxlint-rules",
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
"compilerOptions": {
|
||||
"outDir": "../../.cache/tsc",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "node16",
|
||||
"module": "node16",
|
||||
"moduleResolution": "bundler",
|
||||
"module": "esnext",
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["**/*.ts"]
|
||||
|
|
|
|||
|
|
@ -17,23 +17,32 @@
|
|||
"import/no-duplicates": "error",
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"react/no-unescaped-entities": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-key": "off",
|
||||
|
|
|
|||
3
packages/twenty-sdk/.prettierignore
Normal file
3
packages/twenty-sdk/.prettierignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
dist
|
||||
storybook-static
|
||||
coverage
|
||||
|
|
@ -11,9 +11,7 @@ const dirname =
|
|||
const sdkRoot = path.resolve(dirname, '..');
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
'../src/front-component-renderer/**/*.stories.@(js|jsx|ts|tsx)',
|
||||
],
|
||||
stories: ['../src/front-component-renderer/**/*.stories.@(js|jsx|ts|tsx)'],
|
||||
|
||||
addons: ['@storybook/addon-vitest'],
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ A CLI and SDK to develop, build, and publish applications that extend [Twenty CR
|
|||
- Works great with the scaffolder: [create-twenty-app](https://www.npmjs.com/package/create-twenty-app)
|
||||
|
||||
## Documentation
|
||||
|
||||
See Twenty application documentation https://docs.twenty.com/developers/extend/capabilities/apps
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Node.js 24+ (recommended) and Yarn 4
|
||||
- A Twenty workspace and an API key. Generate one at https://app.twenty.com/settings/api-webhooks
|
||||
|
||||
|
|
@ -73,6 +75,7 @@ In a scaffolded project (via `create-twenty-app`), use `yarn twenty <command>` i
|
|||
Authenticate the CLI against your Twenty workspace.
|
||||
|
||||
- `twenty auth:login` — Authenticate with Twenty.
|
||||
|
||||
- Options:
|
||||
- `--api-key <key>`: API key for authentication.
|
||||
- `--api-url <url>`: Twenty API URL (defaults to your current profile's value or `http://localhost:3000`).
|
||||
|
|
@ -83,6 +86,7 @@ Authenticate the CLI against your Twenty workspace.
|
|||
- `twenty auth:status` — Print the current authentication status (API URL, masked API key, validity).
|
||||
|
||||
- `twenty auth:list` — List all configured workspaces.
|
||||
|
||||
- Behavior: Displays all available workspaces with their authentication status and API URLs. Shows which workspace is the current default.
|
||||
|
||||
- `twenty auth:switch [workspace]` — Switch the default workspace for authentication.
|
||||
|
|
@ -123,6 +127,7 @@ twenty auth:switch production
|
|||
Application development commands.
|
||||
|
||||
- `twenty app:dev [appPath]` — Start development mode: watch and sync local application changes.
|
||||
|
||||
- Behavior: Builds your application (functions and front components), computes the manifest, syncs everything to your workspace, then watches the directory for changes and re-syncs automatically. Displays an interactive UI showing build and sync status in real time. Press Ctrl+C to stop.
|
||||
|
||||
- `twenty app:typecheck [appPath]` — Run TypeScript type checking on the application (runs `tsc --noEmit`). Exits with code 1 if type errors are found.
|
||||
|
|
@ -149,6 +154,7 @@ Application development commands.
|
|||
### Function
|
||||
|
||||
- `twenty function:logs [appPath]` — Stream application function logs.
|
||||
|
||||
- Options:
|
||||
- `-u, --functionUniversalIdentifier <id>`: Only show logs for a specific function universal ID.
|
||||
- `-n, --functionName <name>`: Only show logs for a specific function name.
|
||||
|
|
@ -249,8 +255,8 @@ Notes:
|
|||
- `twenty auth:switch` sets the `defaultWorkspace` field, which is used when `--workspace` is not specified.
|
||||
- `twenty auth:list` shows all configured workspaces and their authentication status.
|
||||
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Auth errors: run `twenty auth:login` again and ensure the API key has the required permissions.
|
||||
- Typings out of date: restart `twenty app:dev` to refresh the client and types.
|
||||
- Not seeing changes in dev: make sure dev mode is running (`twenty app:dev`).
|
||||
|
|
@ -301,5 +307,6 @@ node packages/twenty-sdk/dist/cli.cjs <command>
|
|||
```
|
||||
|
||||
### Resources
|
||||
|
||||
- See our [GitHub](https://github.com/twentyhq/twenty)
|
||||
- Join our [Discord](https://discord.gg/cx5n4Jzs57)
|
||||
|
|
|
|||
|
|
@ -3,24 +3,14 @@
|
|||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/twenty-sdk/src",
|
||||
"projectType": "library",
|
||||
"tags": [
|
||||
"scope:sdk",
|
||||
"scope:shared"
|
||||
],
|
||||
"tags": ["scope:sdk"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
],
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist"
|
||||
],
|
||||
"inputs": ["production", "^production"],
|
||||
"dependsOn": ["^build"],
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
|
|
@ -32,9 +22,7 @@
|
|||
},
|
||||
"dev": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts && npx vite build -c vite.config.sdk.ts && tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist && npx vite build -c vite.config.node.ts --watch & npx vite build -c vite.config.browser.ts --watch & npx vite build -c vite.config.sdk.ts --watch"
|
||||
|
|
@ -42,9 +30,7 @@
|
|||
},
|
||||
"start": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"dependsOn": ["build"],
|
||||
"options": {
|
||||
"cwd": "packages/twenty-sdk",
|
||||
"command": "node dist/cli.cjs"
|
||||
|
|
@ -54,9 +40,7 @@
|
|||
"lint": {},
|
||||
"test": {
|
||||
"executor": "@nx/vitest:test",
|
||||
"outputs": [
|
||||
"{workspaceRoot}/coverage/{projectRoot}"
|
||||
],
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"config": "{projectRoot}/vitest.config.ts"
|
||||
},
|
||||
|
|
@ -103,15 +87,9 @@
|
|||
"build:sdk": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"inputs": [
|
||||
"{projectRoot}/src/sdk/**/*"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist/sdk"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": ["{projectRoot}/src/sdk/**/*"],
|
||||
"outputs": ["{projectRoot}/dist/sdk"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx vite build -c vite.config.sdk.ts"
|
||||
|
|
@ -120,9 +98,7 @@
|
|||
"generate-remote-dom-elements": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"^build"
|
||||
],
|
||||
"dependsOn": ["^build"],
|
||||
"inputs": [
|
||||
"{projectRoot}/scripts/remote-dom/**/*",
|
||||
"{projectRoot}/src/sdk/front-component-api/**/*",
|
||||
|
|
@ -171,9 +147,7 @@
|
|||
}
|
||||
},
|
||||
"storybook:build": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"configurations": {
|
||||
"test": {}
|
||||
}
|
||||
|
|
@ -194,17 +168,13 @@
|
|||
}
|
||||
},
|
||||
"storybook:test": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"options": {
|
||||
"command": "vitest run --coverage --config vitest.storybook.config.ts --shard={args.shard}"
|
||||
}
|
||||
},
|
||||
"storybook:test:no-coverage": {
|
||||
"dependsOn": [
|
||||
"storybook:prebuild"
|
||||
],
|
||||
"dependsOn": ["storybook:prebuild"],
|
||||
"options": {
|
||||
"command": "vitest run --config vitest.storybook.config.ts --shard={args.shard}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ export const CardDisplay = ({
|
|||
content: string;
|
||||
}) => {
|
||||
return (
|
||||
<div style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}>
|
||||
<div
|
||||
style={{ border: '1px solid #ccc', padding: '16px', borderRadius: '8px' }}
|
||||
>
|
||||
<h3>{title}</h3>
|
||||
<p>{content}</p>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -114,11 +114,7 @@ export const DevUiApplicationPanel = ({
|
|||
const entities = groupedEntities.get(type) ?? [];
|
||||
|
||||
return (
|
||||
<DevUiEntitySection
|
||||
key={type}
|
||||
type={type}
|
||||
entities={entities}
|
||||
/>
|
||||
<DevUiEntitySection key={type} type={type} entities={entities} />
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import {
|
||||
type OrchestratorStateEntityInfo,
|
||||
} from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
|
||||
import { type OrchestratorStateEntityInfo } from '@/cli/utilities/dev/orchestrator/dev-mode-orchestrator-state';
|
||||
import {
|
||||
type DevUiStatus,
|
||||
DEV_UI_STATUS_CONFIG,
|
||||
|
|
@ -37,9 +35,7 @@ export const DevUiEntityRow = ({
|
|||
|
||||
return (
|
||||
<Box>
|
||||
<DevUiStatusIcon
|
||||
uiStatus={mapFileStatusToDevUiStatus(entity.status)}
|
||||
/>
|
||||
<DevUiStatusIcon uiStatus={mapFileStatusToDevUiStatus(entity.status)} />
|
||||
<Text>{entity.name}</Text>
|
||||
{entity.path !== entity.name && (
|
||||
<Text dimColor> ({shortenPath(entity.path)})</Text>
|
||||
|
|
|
|||
|
|
@ -74,10 +74,8 @@ const BundleSizesTable = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{entries.map((entry) => {
|
||||
const reactWidth =
|
||||
(entry.reactBytes / maxBytes) * BAR_MAX_WIDTH;
|
||||
const preactWidth =
|
||||
(entry.preactBytes / maxBytes) * BAR_MAX_WIDTH;
|
||||
const reactWidth = (entry.reactBytes / maxBytes) * BAR_MAX_WIDTH;
|
||||
const preactWidth = (entry.preactBytes / maxBytes) * BAR_MAX_WIDTH;
|
||||
const saving =
|
||||
entry.reactBytes > 0
|
||||
? (
|
||||
|
|
@ -102,7 +100,9 @@ const BundleSizesTable = () => {
|
|||
{formatLabel(entry.name)}
|
||||
</td>
|
||||
<td style={{ padding: '10px 12px' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'column', gap: 3 }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: 14,
|
||||
|
|
|
|||
|
|
@ -62,11 +62,7 @@ const ChakraComponent = () => {
|
|||
>
|
||||
Increment
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setCount(0)}
|
||||
>
|
||||
<Button variant="outline" size="sm" onClick={() => setCount(0)}>
|
||||
Reset
|
||||
</Button>
|
||||
</HStack>
|
||||
|
|
|
|||
|
|
@ -37,9 +37,7 @@ const SdkContextComponent = () => {
|
|||
|
||||
const userId = useUserId();
|
||||
|
||||
const fullContext = useFrontComponentExecutionContext(
|
||||
(context) => context,
|
||||
);
|
||||
const fullContext = useFrontComponentExecutionContext((context) => context);
|
||||
|
||||
return (
|
||||
<div data-testid="sdk-context-component" style={CARD_STYLE}>
|
||||
|
|
@ -78,9 +76,7 @@ const SdkContextComponent = () => {
|
|||
</pre>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{ display: 'flex', alignItems: 'center', gap: 12 }}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<button
|
||||
data-testid="sdk-context-button"
|
||||
onClick={() => setRenderCount((previous) => previous + 1)}
|
||||
|
|
|
|||
|
|
@ -13,14 +13,18 @@ export default defineConfig({
|
|||
environment: 'node',
|
||||
include: [
|
||||
'src/**/__tests__/**/*.{test,spec}.{ts,tsx}',
|
||||
'src/**/__integration__/**/*.{test,spec}.{ts,tsx}',
|
||||
'src/**/*.{test,spec}.{ts,tsx}',
|
||||
],
|
||||
exclude: ['**/node_modules/**', '**/.git/**', '**/__e2e__/**'],
|
||||
exclude: [
|
||||
'**/node_modules/**',
|
||||
'**/.git/**',
|
||||
'**/__e2e__/**',
|
||||
'**/__integration__/**',
|
||||
],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
include: ['src/**/*.{ts,tsx}'],
|
||||
exclude: ['src/**/*.d.ts', 'src/cli/cli.ts'],
|
||||
exclude: ['src/**/*.d.ts', 'src/cli/cli.ts', 'src/**/__stories__/**'],
|
||||
thresholds: {
|
||||
statements: 1,
|
||||
lines: 1,
|
||||
|
|
|
|||
|
|
@ -162,12 +162,12 @@
|
|||
],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx oxlint --type-aware -c .oxlintrc.json src/"
|
||||
"command": "npx oxlint --type-aware -c .oxlintrc.json src/ && (prettier src/ --check --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata || (echo 'ERROR: Prettier formatting check failed! Fix with: npx nx lint twenty-server --configuration=fix' && false))"
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {},
|
||||
"fix": {
|
||||
"command": "npx oxlint --type-aware --fix -c .oxlintrc.json src/"
|
||||
"command": "npx oxlint --type-aware --fix -c .oxlintrc.json src/ && prettier src/ --write --cache --cache-location ../../.cache/prettier/{projectRoot} --cache-strategy metadata"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -30,14 +30,14 @@ export class MyService {
|
|||
|
||||
// Track a workspace event
|
||||
auditService.insertWorkspaceEvent(CUSTOM_DOMAIN_ACTIVATED_EVENT, {});
|
||||
|
||||
|
||||
// Track an object event
|
||||
auditService.createObjectEvent(OBJECT_RECORD_CREATED_EVENT, {
|
||||
recordId: 'record-id',
|
||||
objectMetadataId: 'object-metadata-id',
|
||||
// other properties
|
||||
});
|
||||
|
||||
|
||||
// Track a pageview
|
||||
auditService.createPageviewEvent('page-name', {
|
||||
href: '/path',
|
||||
|
|
@ -82,12 +82,14 @@ Then update the `events.type.ts` file:
|
|||
|
||||
```typescript
|
||||
// src/engine/core-modules/analytics/types/events.type.ts
|
||||
import { MY_EVENT, MyEventTrackEvent } from '../utils/events/track/my-feature/my-event';
|
||||
import {
|
||||
MY_EVENT,
|
||||
MyEventTrackEvent,
|
||||
} from '../utils/events/track/my-feature/my-event';
|
||||
|
||||
// Add to the union type
|
||||
export type TrackEventName =
|
||||
| typeof MY_EVENT
|
||||
// ... other event names;
|
||||
export type TrackEventName = typeof MY_EVENT;
|
||||
// ... other event names;
|
||||
|
||||
// Add to the TrackEvents interface
|
||||
export interface TrackEvents {
|
||||
|
|
@ -96,9 +98,8 @@ export interface TrackEvents {
|
|||
}
|
||||
|
||||
// The TrackEventProperties type will automatically use the new event
|
||||
export type TrackEventProperties<T extends TrackEventName> = T extends keyof TrackEvents
|
||||
? TrackEvents[T]['properties']
|
||||
: object;
|
||||
export type TrackEventProperties<T extends TrackEventName> =
|
||||
T extends keyof TrackEvents ? TrackEvents[T]['properties'] : object;
|
||||
```
|
||||
|
||||
## API
|
||||
|
|
@ -136,9 +137,8 @@ export interface TrackEvents {
|
|||
}
|
||||
|
||||
// Use the mapping to extract properties for each event type
|
||||
export type TrackEventProperties<T extends TrackEventName> = T extends keyof TrackEvents
|
||||
? TrackEvents[T]['properties']
|
||||
: object;
|
||||
export type TrackEventProperties<T extends TrackEventName> =
|
||||
T extends keyof TrackEvents ? TrackEvents[T]['properties'] : object;
|
||||
```
|
||||
|
||||
This approach makes it easier to add new events without having to modify a complex nested conditional type.
|
||||
|
|
|
|||
|
|
@ -3,6 +3,4 @@ var main = async (params) => {
|
|||
const message = `Hello, input: ${a} and ${b}`;
|
||||
return { message };
|
||||
};
|
||||
export {
|
||||
main
|
||||
};
|
||||
export { main };
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@
|
|||
"ignorePatterns": ["node_modules", "dist"],
|
||||
"rules": {
|
||||
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
|
||||
"no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }],
|
||||
"no-console": [
|
||||
"warn",
|
||||
{ "allow": ["group", "groupCollapsed", "groupEnd"] }
|
||||
],
|
||||
"no-control-regex": "off",
|
||||
"no-debugger": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
|
|
@ -18,28 +21,43 @@
|
|||
"import/no-duplicates": "error",
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"twenty/enforce-module-boundaries": ["error", {
|
||||
"depConstraints": [
|
||||
{ "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] }
|
||||
]
|
||||
}]
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"twenty/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "scope:shared",
|
||||
"onlyDependOnLibsWithTags": ["scope:shared"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,21 +3,13 @@
|
|||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/twenty-shared/src",
|
||||
"projectType": "library",
|
||||
"tags": [
|
||||
"scope:shared"
|
||||
],
|
||||
"tags": ["scope:shared"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
],
|
||||
"dependsOn": [
|
||||
"generateBarrels",
|
||||
"^build"
|
||||
],
|
||||
"inputs": ["production", "^production"],
|
||||
"dependsOn": ["generateBarrels", "^build"],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist",
|
||||
"{projectRoot}/ai/package.json",
|
||||
|
|
@ -59,16 +51,9 @@
|
|||
"build:individual": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist/individual"
|
||||
],
|
||||
"dependsOn": ["build"],
|
||||
"inputs": ["production", "^production"],
|
||||
"outputs": ["{projectRoot}/dist/individual"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx vite build -c vite.config.individual.ts"
|
||||
|
|
@ -77,34 +62,17 @@
|
|||
"generateBarrels": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"production",
|
||||
"{projectRoot}/scripts/generateBarrels.ts"
|
||||
],
|
||||
"inputs": ["production", "{projectRoot}/scripts/generateBarrels.ts"],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/index.ts",
|
||||
"{projectRoot}/src/*/index.ts",
|
||||
"{projectRoot}/package.json"
|
||||
],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/scripts/generateBarrels.ts"
|
||||
}
|
||||
"options": { "command": "tsx {projectRoot}/scripts/generateBarrels.ts" }
|
||||
},
|
||||
"typecheck": {},
|
||||
"test": {},
|
||||
"lint": {
|
||||
"options": {},
|
||||
"configurations": {
|
||||
"fix": {}
|
||||
}
|
||||
},
|
||||
"fmt": {
|
||||
"options": {
|
||||
"files": "src"
|
||||
},
|
||||
"configurations": {
|
||||
"fix": {}
|
||||
}
|
||||
}
|
||||
"lint": { "options": {}, "configurations": { "fix": {} } },
|
||||
"fmt": { "options": { "files": "src" }, "configurations": { "fix": {} } }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -151,7 +151,10 @@ type WriteInJsonFileArgs = {
|
|||
};
|
||||
const updateJsonFile = ({ content, file }: WriteInJsonFileArgs) => {
|
||||
const updatedJsonFile = JSON.stringify(content);
|
||||
const formattedContent = prettierFormat(updatedJsonFile, 'json-stringify');
|
||||
const formattedContent = prettier.format(updatedJsonFile, {
|
||||
...prettierConfiguration,
|
||||
filepath: file,
|
||||
});
|
||||
fs.writeFileSync(file, formattedContent, 'utf-8');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,4 +9,3 @@ export * from './types';
|
|||
export * from './utils';
|
||||
export * from './workflow';
|
||||
export * from './workspace';
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,5 @@
|
|||
"jest.config.mjs",
|
||||
"vite.config.*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"src/individual-entry.ts"
|
||||
]
|
||||
"exclude": ["src/individual-entry.ts"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@
|
|||
"categories": {
|
||||
"correctness": "off"
|
||||
},
|
||||
"ignorePatterns": [
|
||||
"node_modules",
|
||||
"generated"
|
||||
],
|
||||
"ignorePatterns": ["node_modules", "generated"],
|
||||
"rules": {
|
||||
"func-style": ["error", "declaration", { "allowArrowFunctions": true }],
|
||||
"no-console": ["warn", { "allow": ["group", "groupCollapsed", "groupEnd"] }],
|
||||
"no-console": [
|
||||
"warn",
|
||||
{ "allow": ["group", "groupCollapsed", "groupEnd"] }
|
||||
],
|
||||
"no-control-regex": "off",
|
||||
"no-debugger": "error",
|
||||
"no-duplicate-imports": "error",
|
||||
|
|
@ -35,31 +35,43 @@
|
|||
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
|
||||
"twenty/enforce-module-boundaries": ["error", {
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "scope:shared",
|
||||
"onlyDependOnLibsWithTags": ["scope:shared"]
|
||||
}
|
||||
]
|
||||
}]
|
||||
"twenty/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "scope:shared",
|
||||
"onlyDependOnLibsWithTags": ["scope:shared"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
3
packages/twenty-ui/.prettierignore
Normal file
3
packages/twenty-ui/.prettierignore
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
dist
|
||||
storybook-static
|
||||
coverage
|
||||
|
|
@ -43,8 +43,7 @@ const config: StorybookConfig = {
|
|||
...viteConfig.resolve,
|
||||
alias: {
|
||||
...(viteConfig.resolve?.alias ?? {}),
|
||||
'@tabler/icons-react':
|
||||
'@tabler/icons-react/dist/esm/icons/index.mjs',
|
||||
'@tabler/icons-react': '@tabler/icons-react/dist/esm/icons/index.mjs',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<style>
|
||||
[data-is-storybook="true"] {
|
||||
background-color: transparent!important;
|
||||
}
|
||||
</style>
|
||||
[data-is-storybook='true'] {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -6,29 +6,29 @@
|
|||
<script src="https://cdnjs.cloudflare.com/ajax/libs/iframe-resizer/4.3.7/iframeResizer.contentWindow.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
html {
|
||||
font-size: 13px;
|
||||
}
|
||||
.sbdocs-wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
width: 4px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: 'Inter', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
html {
|
||||
font-size: 13px;
|
||||
}
|
||||
.sbdocs-wrapper {
|
||||
padding: 0 !important;
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
*::-webkit-scrollbar-corner {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,15 +3,10 @@
|
|||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "packages/twenty-ui/src",
|
||||
"projectType": "library",
|
||||
"tags": [
|
||||
"scope:shared"
|
||||
],
|
||||
"tags": ["scope:shared"],
|
||||
"targets": {
|
||||
"build": {
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
"generateBarrels"
|
||||
],
|
||||
"dependsOn": ["^build", "generateBarrels"],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist",
|
||||
"{projectRoot}/accessibility/package.json",
|
||||
|
|
@ -44,16 +39,10 @@
|
|||
},
|
||||
"generateThemeConstants": {
|
||||
"executor": "nx:run-commands",
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"dependsOn": ["build"],
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"{projectRoot}/scripts/generateThemeConstants.ts"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/theme-constants/generated"
|
||||
],
|
||||
"inputs": ["{projectRoot}/scripts/generateThemeConstants.ts"],
|
||||
"outputs": ["{projectRoot}/src/theme-constants/generated"],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/scripts/generateThemeConstants.ts"
|
||||
}
|
||||
|
|
@ -61,31 +50,19 @@
|
|||
"generateBarrels": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"inputs": [
|
||||
"production",
|
||||
"{projectRoot}/scripts/generateBarrels.ts"
|
||||
],
|
||||
"inputs": ["production", "{projectRoot}/scripts/generateBarrels.ts"],
|
||||
"outputs": [
|
||||
"{projectRoot}/src/**/*/index.ts",
|
||||
"{projectRoot}/package.json"
|
||||
],
|
||||
"options": {
|
||||
"command": "tsx {projectRoot}/scripts/generateBarrels.ts"
|
||||
}
|
||||
"options": { "command": "tsx {projectRoot}/scripts/generateBarrels.ts" }
|
||||
},
|
||||
"build:individual": {
|
||||
"executor": "nx:run-commands",
|
||||
"cache": true,
|
||||
"dependsOn": [
|
||||
"build"
|
||||
],
|
||||
"inputs": [
|
||||
"production",
|
||||
"^production"
|
||||
],
|
||||
"outputs": [
|
||||
"{projectRoot}/dist/individual"
|
||||
],
|
||||
"dependsOn": ["build"],
|
||||
"inputs": ["production", "^production"],
|
||||
"outputs": ["{projectRoot}/dist/individual"],
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"command": "npx vite build -c vite.config.individual.ts"
|
||||
|
|
@ -93,39 +70,17 @@
|
|||
},
|
||||
"clean": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "rimraf {projectRoot}/dist"
|
||||
}
|
||||
"options": { "command": "rimraf {projectRoot}/dist" }
|
||||
},
|
||||
"lint": {},
|
||||
"fmt": {
|
||||
"options": {
|
||||
"files": "src"
|
||||
},
|
||||
"configurations": {
|
||||
"fix": {}
|
||||
}
|
||||
},
|
||||
"fmt": { "options": { "files": "src" }, "configurations": { "fix": {} } },
|
||||
"test": {},
|
||||
"typecheck": {},
|
||||
"storybook:build": {
|
||||
"configurations": {
|
||||
"test": {}
|
||||
}
|
||||
},
|
||||
"storybook:serve:dev": {
|
||||
"options": {
|
||||
"port": 6007
|
||||
}
|
||||
},
|
||||
"storybook:build": { "configurations": { "test": {} } },
|
||||
"storybook:serve:dev": { "options": { "port": 6007 } },
|
||||
"storybook:serve:static": {
|
||||
"options": {
|
||||
"buildTarget": "twenty-ui:storybook:build",
|
||||
"port": 6007
|
||||
},
|
||||
"configurations": {
|
||||
"test": {}
|
||||
}
|
||||
"options": { "buildTarget": "twenty-ui:storybook:build", "port": 6007 },
|
||||
"configurations": { "test": {} }
|
||||
},
|
||||
"storybook:test": {},
|
||||
"storybook:test:no-coverage": {},
|
||||
|
|
|
|||
|
|
@ -151,7 +151,10 @@ type WriteInJsonFileArgs = {
|
|||
};
|
||||
const updateJsonFile = ({ content, file }: WriteInJsonFileArgs) => {
|
||||
const updatedJsonFile = JSON.stringify(content);
|
||||
const formattedContent = prettierFormat(updatedJsonFile, 'json-stringify');
|
||||
const formattedContent = prettier.format(updatedJsonFile, {
|
||||
...prettierConfiguration,
|
||||
filepath: file,
|
||||
});
|
||||
fs.writeFileSync(file, formattedContent, 'utf-8');
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -110,8 +110,16 @@
|
|||
--t-background-overlay-primary: #000000b8;
|
||||
--t-background-overlay-secondary: #0000005c;
|
||||
--t-background-overlay-tertiary: #0000005c;
|
||||
--t-background-radial-gradient: radial-gradient(50% 62.62% at 50% 0%, color(display-p3 0.506 0.506 0.506) 0%, color(display-p3 0.482 0.482 0.482) 100%);
|
||||
--t-background-radial-gradient-hover: radial-gradient(76.32% 95.59% at 50% 0%, color(display-p3 0.482 0.482 0.482) 0%, color(display-p3 0.702 0.702 0.702) 100%);
|
||||
--t-background-radial-gradient: radial-gradient(
|
||||
50% 62.62% at 50% 0%,
|
||||
color(display-p3 0.506 0.506 0.506) 0%,
|
||||
color(display-p3 0.482 0.482 0.482) 100%
|
||||
);
|
||||
--t-background-radial-gradient-hover: radial-gradient(
|
||||
76.32% 95.59% at 50% 0%,
|
||||
color(display-p3 0.482 0.482 0.482) 0%,
|
||||
color(display-p3 0.702 0.702 0.702) 100%
|
||||
);
|
||||
--t-background-primary-inverted: color(display-p3 0.922 0.922 0.922);
|
||||
--t-background-primary-inverted-hover: color(display-p3 0.702 0.702 0.702);
|
||||
--t-blur-light: blur(6px) saturate(200%) contrast(100%) brightness(130%);
|
||||
|
|
@ -132,11 +140,14 @@
|
|||
--t-border-radius-xxl: 40px;
|
||||
--t-border-radius-pill: 999px;
|
||||
--t-border-radius-rounded: 100%;
|
||||
--t-box-shadow-color: rgba(0,0,0,0.6);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px rgba(0,0,0,0.04), 0px 0px 4px 0px rgba(0,0,0,0.08);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px rgba(0,0,0,0.16), 0px 2px 4px 0px rgba(0,0,0,0.08);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px rgba(0,0,0,0.32);
|
||||
--t-box-shadow-super-heavy: 2px 4px 16px 0px rgba(0,0,0,0.12), 0px 2px 4px 0px rgba(0,0,0,0.04);
|
||||
--t-box-shadow-color: rgba(0, 0, 0, 0.6);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px rgba(0, 0, 0, 0.04),
|
||||
0px 0px 4px 0px rgba(0, 0, 0, 0.08);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px rgba(0, 0, 0, 0.16),
|
||||
0px 2px 4px 0px rgba(0, 0, 0, 0.08);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px rgba(0, 0, 0, 0.32);
|
||||
--t-box-shadow-super-heavy: 2px 4px 16px 0px rgba(0, 0, 0, 0.12),
|
||||
0px 2px 4px 0px rgba(0, 0, 0, 0.04);
|
||||
--t-font-color-primary: color(display-p3 0.922 0.922 0.922);
|
||||
--t-font-color-secondary: color(display-p3 0.702 0.702 0.702);
|
||||
--t-font-color-tertiary: color(display-p3 0.506 0.506 0.506);
|
||||
|
|
|
|||
|
|
@ -110,8 +110,16 @@
|
|||
--t-background-overlay-primary: color(display-p3 0 0 0 / 0.722);
|
||||
--t-background-overlay-secondary: color(display-p3 0 0 0 / 0.361);
|
||||
--t-background-overlay-tertiary: color(display-p3 0 0 0 / 0.071);
|
||||
--t-background-radial-gradient: radial-gradient(50% 62.62% at 50% 0%, color(display-p3 0.6 0.6 0.6) 0%, color(display-p3 0.514 0.514 0.514) 100%);
|
||||
--t-background-radial-gradient-hover: radial-gradient(76.32% 95.59% at 50% 0%, color(display-p3 0.514 0.514 0.514) 0%, color(display-p3 0.4 0.4 0.4) 100%);
|
||||
--t-background-radial-gradient: radial-gradient(
|
||||
50% 62.62% at 50% 0%,
|
||||
color(display-p3 0.6 0.6 0.6) 0%,
|
||||
color(display-p3 0.514 0.514 0.514) 100%
|
||||
);
|
||||
--t-background-radial-gradient-hover: radial-gradient(
|
||||
76.32% 95.59% at 50% 0%,
|
||||
color(display-p3 0.514 0.514 0.514) 0%,
|
||||
color(display-p3 0.4 0.4 0.4) 100%
|
||||
);
|
||||
--t-background-primary-inverted: color(display-p3 0.2 0.2 0.2);
|
||||
--t-background-primary-inverted-hover: color(display-p3 0.4 0.4 0.4);
|
||||
--t-blur-light: blur(6px) saturate(200%) contrast(50%) brightness(130%);
|
||||
|
|
@ -133,10 +141,14 @@
|
|||
--t-border-radius-pill: 999px;
|
||||
--t-border-radius-rounded: 100%;
|
||||
--t-box-shadow-color: color(display-p3 0 0 0 / 0.039);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px color(display-p3 0 0 0 / 0.039), 0px 0px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px color(display-p3 0 0 0 / 0.161), 0px 2px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-light: 0px 2px 4px 0px color(display-p3 0 0 0 / 0.039),
|
||||
0px 0px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-strong: 2px 4px 16px 0px color(display-p3 0 0 0 / 0.161),
|
||||
0px 2px 4px 0px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-underline: 0px 1px 0px 0px color(display-p3 0 0 0 / 0.361);
|
||||
--t-box-shadow-super-heavy: 0px 0px 8px 0px color(display-p3 0 0 0 / 0.161), 0px 8px 64px -16px color(display-p3 0 0 0 / 0.478), 0px 24px 56px -16px color(display-p3 0 0 0 / 0.078);
|
||||
--t-box-shadow-super-heavy: 0px 0px 8px 0px color(display-p3 0 0 0 / 0.161),
|
||||
0px 8px 64px -16px color(display-p3 0 0 0 / 0.478),
|
||||
0px 24px 56px -16px color(display-p3 0 0 0 / 0.078);
|
||||
--t-font-color-primary: color(display-p3 0.2 0.2 0.2);
|
||||
--t-font-color-secondary: color(display-p3 0.4 0.4 0.4);
|
||||
--t-font-color-tertiary: color(display-p3 0.6 0.6 0.6);
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ export default defineConfig(({ command }) => {
|
|||
alias: {
|
||||
'@ui/': path.resolve(__dirname, 'src') + '/',
|
||||
'@assets/': path.resolve(__dirname, 'src/assets') + '/',
|
||||
'@tabler/icons-react':
|
||||
'@tabler/icons-react/dist/esm/icons/index.mjs',
|
||||
'@tabler/icons-react': '@tabler/icons-react/dist/esm/icons/index.mjs',
|
||||
},
|
||||
},
|
||||
css: {
|
||||
|
|
|
|||
|
|
@ -17,23 +17,32 @@
|
|||
"import/no-duplicates": "error",
|
||||
"typescript/no-redeclare": "error",
|
||||
"typescript/ban-ts-comment": "error",
|
||||
"typescript/consistent-type-imports": ["error", {
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}],
|
||||
"typescript/consistent-type-imports": [
|
||||
"error",
|
||||
{
|
||||
"prefer": "type-imports",
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"typescript/explicit-function-return-type": "off",
|
||||
"typescript/explicit-module-boundary-types": "off",
|
||||
"typescript/no-empty-object-type": ["error", {
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}],
|
||||
"typescript/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
"allowInterfaces": "with-single-extends"
|
||||
}
|
||||
],
|
||||
"typescript/no-empty-function": "off",
|
||||
"typescript/no-explicit-any": "off",
|
||||
"typescript/no-unused-vars": ["warn", {
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}],
|
||||
"typescript/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"react/no-unescaped-entities": "off",
|
||||
"react/prop-types": "off",
|
||||
"react/jsx-key": "off",
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
## 2.2.0
|
||||
* Fix authentication issue
|
||||
|
||||
- Fix authentication issue
|
||||
|
||||
## 2.1.1
|
||||
* Add changelog
|
||||
|
||||
- Add changelog
|
||||
|
||||
## 2.1.0
|
||||
* Fix some authentication issues
|
||||
* Remove position from input fields
|
||||
* Fix required boolean fields that should not be
|
||||
|
||||
- Fix some authentication issues
|
||||
- Remove position from input fields
|
||||
- Fix required boolean fields that should not be
|
||||
|
||||
## 2.0.0
|
||||
* First release
|
||||
|
||||
- First release
|
||||
|
||||
## 1.0.0
|
||||
|
||||
Initial release to public.
|
||||
|
|
|
|||
|
|
@ -23,9 +23,7 @@
|
|||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
"commands": [
|
||||
"NODE_ENV=test jest --testTimeout 10000"
|
||||
]
|
||||
"commands": ["NODE_ENV=test jest --testTimeout 10000"]
|
||||
}
|
||||
},
|
||||
"lint": {},
|
||||
|
|
|
|||
|
|
@ -55779,6 +55779,12 @@ __metadata:
|
|||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"twenty-oxlint-rules@workspace:packages/twenty-oxlint-rules":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "twenty-oxlint-rules@workspace:packages/twenty-oxlint-rules"
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"twenty-sdk@workspace:*, twenty-sdk@workspace:packages/twenty-sdk":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "twenty-sdk@workspace:packages/twenty-sdk"
|
||||
|
|
|
|||
Loading…
Reference in a new issue