diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2e4df4f069..753708009e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -968,6 +968,8 @@ jobs: run: node common/scripts/install-run-rush.js install - name: Building... run: node common/scripts/install-run-rush.js build + - name: Emit TypeScript declarations + run: node common/scripts/install-run-rush.js validate - name: Publish to npm env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.vscode/settings.json b/.vscode/settings.json index bb197af43f..2b631168c1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,14 +1,25 @@ { "eslint.format.enable": true, "eslint.enable": true, - "eslint.probe": ["javascript", "typescript", "html", "markdown", "svelte"], - "eslint.validate": ["javascript", "typescript", "markdown", "svelte"], + "eslint.probe": [ + "javascript", + "typescript", + "html", + "markdown", + "svelte" + ], + "eslint.validate": [ + "javascript", + "typescript", + "markdown", + "svelte" + ], "eslint.lintTask.enable": true, "[typescript]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" }, "[json]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" + "editor.defaultFormatter": "vscode.json-language-features" }, "[markdown]": { "editor.defaultFormatter": "esbenp.prettier-vscode" @@ -16,4 +27,4 @@ "[svelte]": { "editor.defaultFormatter": "dbaeumer.vscode-eslint" } -} +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..ac76eb73df --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +# Project Instructions + +- Do not run build commands automatically for verification. +- When finishing work, state that the task is complete and ask the user to verify it manually. diff --git a/README.md b/README.md index 82a8ce8cc7..1d2b32ba43 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,17 @@ ⭐️ Your star shines on us. Star us on GitHub! +> [!IMPORTANT] +> **Hosted Huly is shutting down — please migrate your data.** +> +> The hosted Huly service is being discontinued because its hosting is no longer being funded. If you keep important data on the hosted platform, export and back it up, and migrate as soon as possible — we can help you move to either a [self-hosted setup](https://github.com/hcengineering/huly-selfhost) or a hosted option. +> +> Not sure how? Follow the [backup & restore guide](docs/guides/backup-restore.en.md) for step-by-step instructions on downloading your backup and restoring it elsewhere. +> +> The service shutdown is expected on **July 20**. Please make sure to export and migrate your data before then rather than wait until the last day. +> +> Have questions or want updates? Join the [Huly community](https://link.huly.io/slack) to discuss migration and stay informed, or email us at [artem@hardcoreeng.com](mailto:artem@hardcoreeng.com) with any questions. This affects only the hosted **Huly** service — self-hosted deployments are not affected. + ## About The Huly Platform is a robust framework designed to accelerate the development of business applications, such as CRM systems. diff --git a/changelog.md b/changelog.md index cf8cdceae7..1a17cdf6f0 100644 --- a/changelog.md +++ b/changelog.md @@ -2,12 +2,15 @@ Changelog. +<<<<<<< HEAD ## [unreleased] * 🚀 FEATURES: · Add StringFromEnum and EnumFromString process functions ([#10806](https://github.com/hcengineering/platform/issues/10806)) · Add left menu for styled text ([#10807](https://github.com/hcengineering/platform/issues/10807)) · Add show all versions toggle to card view options and implement version query filtering ([#10809](https://github.com/hcengineering/platform/issues/10809)) · To sort attributes for parent and sub class together ([#10816](https://github.com/hcengineering/platform/issues/10816)) · To copy controlled document content ([#10819](https://github.com/hcengineering/platform/issues/10819)) · Korean (ko) translation ([#10820](https://github.com/hcengineering/platform/issues/10820)) · Hide inactive employees ([#10821](https://github.com/hcengineering/platform/issues/10821)) · Array for relation ([#10827](https://github.com/hcengineering/platform/issues/10827)) * 🐛 BUG FIXES: · Link action ([#10793](https://github.com/hcengineering/platform/issues/10793)) · Tables ([#10813](https://github.com/hcengineering/platform/issues/10813)) · Label overflow ([#10829](https://github.com/hcengineering/platform/issues/10829)) · Docker build ([#10831](https://github.com/hcengineering/platform/issues/10831)) · Set migration state in case of mode mismatch and add duration ([#10801](https://github.com/hcengineering/platform/issues/10801)) * ⚙️ MISCELLANEOUS TASKS: · Changelog +======= +>>>>>>> cb3a965b46852453b63606c58572ca9b4c0d1594 ## [0.7.423] - 2026-05-10 * 🚀 FEATURES: · Print documents by class and id ([#10753](https://github.com/hcengineering/platform/issues/10753)) · Add tool to create missing SocialIdentity ([#10758](https://github.com/hcengineering/platform/issues/10758)) · User statuses ([#10774](https://github.com/hcengineering/platform/issues/10774)) · Add ability to configure guest spaces in settings ([#10770](https://github.com/hcengineering/platform/issues/10770)) · Add min and max transformation functions ([#10782](https://github.com/hcengineering/platform/issues/10782)) · Add trigger to remove associated execution data when a card is deleted ([#10795](https://github.com/hcengineering/platform/issues/10795)) · Add found elements count in desktop ([#10799](https://github.com/hcengineering/platform/issues/10799)) diff --git a/common/config/rush/command-line.json b/common/config/rush/command-line.json index 39d7869cf7..8a7842b499 100644 --- a/common/config/rush/command-line.json +++ b/common/config/rush/command-line.json @@ -295,6 +295,14 @@ "safeForSimultaneousRushProcesses": true, "shellCommand": "cd ./dev && docker compose -f docker-compose.yaml -f docker-compose.min.yaml up -d --force-recreate" }, + { + "commandKind": "global", + "name": "docker:up:ext", + "summary": "Up extended development stack with billing + payment", + "description": "Start extended docker compose (docker-compose.yaml + docker-compose.ext.yaml) with the optional billing and payment services enabled. Required env: STRIPE_API_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_SUBSCRIPTION_PLANS (or POLAR_* equivalents).", + "safeForSimultaneousRushProcesses": true, + "shellCommand": "cd ./dev && docker compose -f docker-compose.yaml -f docker-compose.ext.yaml up -d --force-recreate" + }, { "commandKind": "global", "name": "docker:up:pg", diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 5073566554..505b7d34c0 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -683,8 +683,8 @@ importers: specifier: ^10.4.14 version: 10.4.21(postcss@8.5.6) browserslist: - specifier: ^4.28.1 - version: 4.28.1 + specifier: ^4.28.2 + version: 4.28.2 compression-webpack-plugin: specifier: ^10.0.0 version: 10.0.0(webpack@5.102.1) @@ -791,8 +791,8 @@ importers: specifier: ^5.9.3 version: 5.9.3 update-browserslist-db: - specifier: ^1.1.3 - version: 1.1.4(browserslist@4.28.1) + specifier: ^1.2.3 + version: 1.2.3(browserslist@4.28.2) webpack: specifier: ^5.97.1 version: 5.102.1(esbuild@0.25.12)(webpack-cli@5.1.4) @@ -1803,8 +1803,8 @@ importers: specifier: ^10.4.14 version: 10.4.21(postcss@8.5.6) browserslist: - specifier: ^4.28.1 - version: 4.28.1 + specifier: ^4.28.2 + version: 4.28.2 compression-webpack-plugin: specifier: ^10.0.0 version: 10.0.0(webpack@5.102.1) @@ -1869,8 +1869,8 @@ importers: specifier: ^5.9.3 version: 5.9.3 update-browserslist-db: - specifier: ^1.1.3 - version: 1.1.4(browserslist@4.28.1) + specifier: ^1.2.3 + version: 1.2.3(browserslist@4.28.2) webpack: specifier: ^5.97.1 version: 5.102.1(esbuild@0.25.12)(webpack-cli@5.1.4) @@ -16427,6 +16427,9 @@ importers: '@hcengineering/account-client': specifier: workspace:^0.7.423 version: link:../../foundations/core/packages/account-client + '@hcengineering/attachment': + specifier: workspace:^0.7.0 + version: link:../attachment '@hcengineering/billing': specifier: workspace:^0.7.423 version: link:../billing @@ -37730,6 +37733,9 @@ importers: '@hcengineering/analytics-service': specifier: workspace:^0.7.423 version: link:../../../foundations/core/packages/analytics-service + '@hcengineering/billing': + specifier: workspace:^0.7.0 + version: link:../../../plugins/billing '@hcengineering/core': specifier: workspace:^0.7.423 version: link:../../../foundations/core/packages/core @@ -38292,6 +38298,9 @@ importers: '@hcengineering/postgres': specifier: workspace:^0.7.423 version: link:../../../foundations/server/packages/postgres + '@hcengineering/products': + specifier: workspace:^0.7.0 + version: link:../../../plugins/products '@hcengineering/server-client': specifier: workspace:^0.7.423 version: link:../../../foundations/server/packages/client @@ -45703,8 +45712,9 @@ packages: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} - baseline-browser-mapping@2.9.11: - resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + baseline-browser-mapping@2.10.33: + resolution: {integrity: sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==} + engines: {node: '>=6.0.0'} hasBin: true basic-auth@2.0.1: @@ -45798,8 +45808,8 @@ packages: browser-or-node@2.1.1: resolution: {integrity: sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -45944,8 +45954,8 @@ packages: caniuse-lite@1.0.30001754: resolution: {integrity: sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==} - caniuse-lite@1.0.30001762: - resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} + caniuse-lite@1.0.30001793: + resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} centra@2.7.0: resolution: {integrity: sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==} @@ -46963,8 +46973,8 @@ packages: electron-store@8.2.0: resolution: {integrity: sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==} - electron-to-chromium@1.5.267: - resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + electron-to-chromium@1.5.366: + resolution: {integrity: sha512-OlRuhb688YTCzzU3gXPLn6nGyd+F+53INE1qaKKlu6kETErE8FYsyDh0XqXEU+uBRn0MpCzz2vfNwORhkap8qg==} electron-updater@6.6.2: resolution: {integrity: sha512-Cr4GDOkbAUqRHP5/oeOmH/L2Bn6+FQPxVLZtPbcmKZC63a1F3uu5EefYOssgZXG3u/zBlubbJ5PJdITdMVggbw==} @@ -49497,8 +49507,9 @@ packages: resolution: {integrity: sha512-8bp200rELOX72mHv0cfnuDs7LWmd3jr3Y9pxgaysjeSxnUiZxhTA0Pg+SaXkTplIK+r4CfIVAfrla4omNq6UtA==} engines: {node: '>=20'} - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} nodemailer@6.10.1: resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==} @@ -51610,12 +51621,6 @@ packages: resolution: {integrity: sha512-ZX6U1J04K1FoSUeoX1OicAhw4d0aro2qo+L8RhJkiGTNtBNkd/Fi1Wxoc9HzcVu6HfOzm0si/N15JjxFmD1z6A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true @@ -52725,7 +52730,7 @@ snapshots: dependencies: '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 @@ -56927,7 +56932,7 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 caniuse-lite: 1.0.30001754 fraction.js: 4.3.7 normalize-range: 0.1.2 @@ -57041,7 +57046,7 @@ snapshots: base64url@3.0.1: {} - baseline-browser-mapping@2.9.11: {} + baseline-browser-mapping@2.10.33: {} basic-auth@2.0.1: dependencies: @@ -57135,13 +57140,13 @@ snapshots: browser-or-node@2.1.1: {} - browserslist@4.28.1: + browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001762 - electron-to-chromium: 1.5.267 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.10.33 + caniuse-lite: 1.0.30001793 + electron-to-chromium: 1.5.366 + node-releases: 2.0.47 + update-browserslist-db: 1.2.3(browserslist@4.28.2) bs-logger@0.2.6: dependencies: @@ -57321,7 +57326,7 @@ snapshots: caniuse-lite@1.0.30001754: {} - caniuse-lite@1.0.30001762: {} + caniuse-lite@1.0.30001793: {} centra@2.7.0: dependencies: @@ -58452,7 +58457,7 @@ snapshots: conf: 10.2.0 type-fest: 2.19.0 - electron-to-chromium@1.5.267: {} + electron-to-chromium@1.5.366: {} electron-updater@6.6.2: dependencies: @@ -61607,7 +61612,7 @@ snapshots: camelcase: 6.3.0 semver: 7.7.3 - node-releases@2.0.27: {} + node-releases@2.0.47: {} nodemailer@6.10.1: {} @@ -64042,15 +64047,9 @@ snapshots: escape-string-regexp: 5.0.0 path-exists: 5.0.0 - update-browserslist-db@1.1.4(browserslist@4.28.1): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.28.1 - escalade: 3.2.0 - picocolors: 1.1.1 - - update-browserslist-db@1.2.3(browserslist@4.28.1): - dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 @@ -64306,7 +64305,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.28.1 + browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -64338,7 +64337,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.28.1 + browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -64370,7 +64369,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.28.1 + browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 diff --git a/desktop/package.json b/desktop/package.json index 024ee7468b..1fab38e2cb 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -47,8 +47,8 @@ "compression-webpack-plugin": "^10.0.0", "html-webpack-plugin": "^5.5.0", "fork-ts-checker-webpack-plugin": "^9.0.2", - "update-browserslist-db": "^1.1.3", - "browserslist": "^4.28.1", + "update-browserslist-db": "^1.2.3", + "browserslist": "^4.28.2", "typescript": "^5.9.3", "ts-node": "^10.9.2", "ts-node-dev": "^2.0.0", @@ -295,4 +295,4 @@ "typescript", "svelte" ] -} +} \ No newline at end of file diff --git a/desktop/src/ui/platform.ts b/desktop/src/ui/platform.ts index fdf240389e..0ae2a507fd 100644 --- a/desktop/src/ui/platform.ts +++ b/desktop/src/ui/platform.ts @@ -412,7 +412,7 @@ export async function configurePlatform (onWorkbenchConnect?: () => Promise l.trim()) - : ['en', 'ru', 'es', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'ko', 'tr'] + : ['en', 'ru', 'es', 'pl', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'ko', 'tr'] setMetadata(uiPlugin.metadata.Languages, languages) diff --git a/dev/docker-compose.ext.yaml b/dev/docker-compose.ext.yaml new file mode 100644 index 0000000000..49db8f8887 --- /dev/null +++ b/dev/docker-compose.ext.yaml @@ -0,0 +1,80 @@ +# Ext overlay: enables the optional billing + payment services so that the client +# can reach them and plan-limit restrictions kick in. +# Use with: docker compose -f docker-compose.yaml -f docker-compose.ext.yaml up +# or: rush docker:up:ext +# (or: docker compose -f docker-compose.yaml -f docker-compose.min.yaml -f docker-compose.ext.yaml up +# if you want to keep the small footprint and just add billing/payment on top.) +# +# The base docker-compose.yaml already advertises PAYMENT_URL=http://huly.local:3040 +# to the front. This overlay materialises the matching `payment` and `billing` +# services and adds BILLING_URL to the front so the client picks them up. +# +# Provider credentials are read from the host environment (e.g. dev/.env or shell): +# PAYMENT_USE_SANDBOX default: true +# POLAR_ACCESS_TOKEN, POLAR_WEBHOOK_SECRET, POLAR_ORGANIZATION_ID, +# POLAR_SUBSCRIPTION_PLANS +# STRIPE_API_KEY, STRIPE_WEBHOOK_SECRET, STRIPE_SUBSCRIPTION_PLANS +# All are optional — without them the service starts in sandbox mode. + +services: + payment: + image: hardcoreeng/payment + # Clear the profile gate from docker-compose.min.yaml so the service starts + # whenever this overlay is included, even alongside `min`. + profiles: !override [] + extra_hosts: + - 'huly.local:host-gateway' + depends_on: + account: + condition: service_started + ports: + - 3040:3040 + environment: + - PORT=3040 + - SECRET=secret + - ACCOUNTS_URL=http://huly.local:3000 + - FRONT_URL=http://huly.local:8087 + - USE_SANDBOX=${PAYMENT_USE_SANDBOX:-true} + # Provider credentials — supply via .env / shell env. Empty values keep + # the service running in safe "sandbox" mode without making outbound calls. + # - POLAR_ACCESS_TOKEN=${POLAR_ACCESS_TOKEN:-} + # - POLAR_WEBHOOK_SECRET=${POLAR_WEBHOOK_SECRET:-} + # - POLAR_ORGANIZATION_ID=${POLAR_ORGANIZATION_ID:-} + # - POLAR_SUBSCRIPTION_PLANS=${POLAR_SUBSCRIPTION_PLANS:-} + - STRIPE_API_KEY=${STRIPE_API_KEY:-} + - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET:-} + - STRIPE_SUBSCRIPTION_PLANS=${STRIPE_SUBSCRIPTION_PLANS:-} + - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces + restart: unless-stopped + + billing: + image: hardcoreeng/billing + profiles: !override [] + extra_hosts: + - 'huly.local:host-gateway' + depends_on: + cockroach: + condition: service_started + minio: + condition: service_healthy + account: + condition: service_started + ports: + - 4042:4042 + environment: + - PORT=4042 + - SECRET=secret + - ACCOUNTS_URL=http://huly.local:3000 + - DB_URL=${DB_CR_URL} + - STORAGE_CONFIG=${STORAGE_CONFIG} + # Recheck workspace usage every 10 minutes in dev (default is 1 hour). + - USAGE_UPDATE_INTERVAL=600 + - OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces + restart: unless-stopped + + # Make the billing endpoint discoverable by the client. PAYMENT_URL is already + # set in the base compose file so we don't repeat it here — environment values + # are merged additively. + front: + environment: + - BILLING_URL=http://huly.local:4042 diff --git a/dev/docker-compose.min.yaml b/dev/docker-compose.min.yaml index 637f5d41c4..a7a1773586 100644 --- a/dev/docker-compose.min.yaml +++ b/dev/docker-compose.min.yaml @@ -1,7 +1,7 @@ # Min override: excludes optional services from min deployment. # Use with: docker compose -f docker-compose.yaml -f docker-compose.min.yaml up # (rush docker:up:min) -# Excluded: preview, link-preview, elastic, redis, stats, payment, fulltext_cockroach, +# Excluded: preview, link-preview, elastic, redis, stats, payment, billing, fulltext_cockroach, # print, sign, hulykvs, hulygun, hulypulse, process-service, backup-cockroach, # backup-api, rating_cockroach # Overrides below remove dependencies on excluded services so the min project validates. @@ -19,6 +19,8 @@ services: profiles: ["full"] payment: profiles: ["full"] + billing: + profiles: ["full"] fulltext_cockroach: profiles: ["full"] print: diff --git a/dev/prod/package.json b/dev/prod/package.json index f54282118a..f45dff6b59 100644 --- a/dev/prod/package.json +++ b/dev/prod/package.json @@ -26,7 +26,7 @@ "@hcengineering/platform-rig": "workspace:^0.7.423", "@types/node": "^22.18.1", "autoprefixer": "^10.4.14", - "browserslist": "^4.28.1", + "browserslist": "^4.28.2", "compression-webpack-plugin": "^10.0.0", "cross-env": "~7.0.3", "css-loader": "^5.2.1", @@ -48,7 +48,7 @@ "svgo-loader": "^3.0.0", "ts-loader": "^9.2.5", "typescript": "^5.9.3", - "update-browserslist-db": "^1.1.3", + "update-browserslist-db": "^1.2.3", "webpack": "^5.97.1", "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", @@ -297,4 +297,4 @@ "readable-stream": "^4.7.0", "svelte": "^4.2.20" } -} +} \ No newline at end of file diff --git a/dev/prod/public/branding.json b/dev/prod/public/branding.json index e5739e9f36..530b1ba4ed 100644 --- a/dev/prod/public/branding.json +++ b/dev/prod/public/branding.json @@ -1,7 +1,7 @@ { "huly.local:8080": { "title": "Huly", - "languages": "en,ru,pt,pt-br,es,zh,fr,de,ja,ko,tr", + "languages": "en,ru,pl,pt,pt-br,es,zh,fr,de,ja,ko,tr", "defaultLanguage": "en", "defaultApplication": "tracker", "defaultSpace": "tracker:project:DefaultProject", @@ -30,7 +30,7 @@ }, "huly.local:8087": { "title": "Huly", - "languages": "en,ru,pt,pt-br,es,zh,fr,de,ja,ko,tr", + "languages": "en,ru,pl,pt,pt-br,es,zh,fr,de,ja,ko,tr", "defaultLanguage": "en", "defaultApplication": "tracker", "defaultSpace": "tracker:project:DefaultProject", diff --git a/dev/prod/src/platform.ts b/dev/prod/src/platform.ts index 6f3ac3f628..943b4a1529 100644 --- a/dev/prod/src/platform.ts +++ b/dev/prod/src/platform.ts @@ -545,7 +545,7 @@ export async function configurePlatform() { const languages = myBranding.languages ? myBranding.languages.split(',').map((l) => l.trim()) - : ['en', 'ru', 'es', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'ko', 'tr'] + : ['en', 'ru', 'es', 'pl', 'pt', 'pt-br', 'zh', 'fr', 'cs', 'it', 'de', 'ja', 'ko', 'tr'] setMetadata(uiPlugin.metadata.Languages, languages) diff --git a/dev/prod/webpack.config.js b/dev/prod/webpack.config.js index 357a33294d..54298c063c 100644 --- a/dev/prod/webpack.config.js +++ b/dev/prod/webpack.config.js @@ -67,11 +67,6 @@ const devProxy = { changeOrigin: true, logLevel: 'debug' }, - '/import': { - target: 'http://huly.local:8087', - changeOrigin: true, - logLevel: 'debug' - }, '/rekoni/recognize': { target: 'http://huly.local:4004', changeOrigin: true, @@ -97,11 +92,6 @@ const devProxyTest = { changeOrigin: true, logLevel: 'debug' }, - '/import': { - target: 'http://huly.local:8083', - changeOrigin: true, - logLevel: 'debug' - }, '/rekoni/recognize': { target: 'http://huly.local:4004', changeOrigin: true, diff --git a/dev/tool/src/db.ts b/dev/tool/src/db.ts index 201489a6f9..186d7a6c20 100644 --- a/dev/tool/src/db.ts +++ b/dev/tool/src/db.ts @@ -1755,7 +1755,7 @@ export async function restoreFromv6All ( } await sendTransactorEvent(uuid, 'force-maintenance') - await restore(ctx, pipeline, wsIds, storage, { + await restore(ctx, pipeline, wsIds, storage, undefined, { date: -1, merge: false, parallel: 1, @@ -1928,7 +1928,7 @@ export async function restoreTrustedV6Workspace ( } await sendTransactorEvent(uuid, 'force-maintenance') - await restore(ctx, pipeline, wsIds, backupWsStorage, { + await restore(ctx, pipeline, wsIds, backupWsStorage, undefined, { date: -1, merge: false, parallel: 1, diff --git a/dev/tool/src/index.ts b/dev/tool/src/index.ts index e748e621ba..2636f0262d 100644 --- a/dev/tool/src/index.ts +++ b/dev/tool/src/index.ts @@ -1136,6 +1136,8 @@ export function devTool ( .option('-i, --include ', 'A list of ; separated domain names to include during backup', '*') .option('-s, --skip ', 'A list of ; separated domain names to skip during backup', '') .option('--upgrade', 'Upgrade workspace', false) + .option('--noqueue', 'NoQueue', false) + .option('--accounts', 'Restore accounts (person/socialId) from backup', false) .option( '--history-file ', 'Store blob send info into file. Will skip already send documents.', @@ -1156,6 +1158,8 @@ export function devTool ( useStorage: string historyFile: string upgrade: boolean + noqueue: boolean + accounts: boolean } ) => { await withAccountDatabase(async (db) => { @@ -1174,10 +1178,10 @@ export function devTool ( const storage = await createFileBackupStorage(dirName) const storageConfig = storageConfigFromEnv() - const queue = getPlatformQueue('tool', ws.region) - const wsProducer = queue.getProducer(toolCtx, QueueTopic.Workspace) + const queue = !cmd.noqueue ? getPlatformQueue('tool', ws.region) : undefined + const wsProducer = queue?.getProducer(toolCtx, QueueTopic.Workspace) - await wsProducer.send(toolCtx, ws.uuid, [workspaceEvents.restoring()]) + await wsProducer?.send(toolCtx, ws.uuid, [workspaceEvents.restoring()]) const workspaceStorage: StorageAdapter = buildStorageFromConfig(storageConfig) @@ -1202,7 +1206,7 @@ export function devTool ( } await sendTransactorEvent(workspace, 'force-maintenance') - await restore(toolCtx, pipeline, wsIds, storage, { + await restore(toolCtx, pipeline, wsIds, storage, cmd.accounts ? db : undefined, { date: parseInt(date ?? '-1'), merge: cmd.merge, parallel: parseInt(cmd.parallel ?? '1'), @@ -1219,12 +1223,12 @@ export function devTool ( } console.log('workspace restored') - await wsProducer.send(toolCtx, ws.uuid, [workspaceEvents.restored()]) + await wsProducer?.send(toolCtx, ws.uuid, [workspaceEvents.restored()]) } catch (err) { toolCtx.error('failed to restore', { err }) } await pipeline?.close() - await queue.shutdown() + await queue?.shutdown() await workspaceStorage?.close() }) } diff --git a/docs/guides/backup-restore.en.md b/docs/guides/backup-restore.en.md new file mode 100644 index 0000000000..b3c272376e --- /dev/null +++ b/docs/guides/backup-restore.en.md @@ -0,0 +1,136 @@ +# Backup & Restore Guide + +Download a workspace backup and restore it into another Huly instance — +whether that's a self-hosted deployment or one of the hosted options. This is +the recommended way to move your data off a hosted workspace before it +becomes unavailable. + +> [!TIP] +> Don't want to run your own server? You can also move to one of the +> existing hosted Huly forks instead of self-hosting. We're glad to help +> with the data migration either way — just keep in mind that none of these +> hosting options are free, since keeping servers running costs real money. +> See [Need help?](#need-help) below to get in touch. + +## Overview + +1. **Download** — from *Settings → Backup* in the workspace you want to move, + grab a full copy of your data. +2. **Restore** — replay that backup into the destination workspace using the + platform's admin tool (`dev/tool`). + +## Step 1. Download your backup + +Open the workspace and go to: + +``` +/setting/setting/backup +``` + +(*Settings → Backup* in the left sidebar.) The page shows when the backup was +last taken, how many snapshots and files it contains, and its total size. + +> [!NOTE] +> Backups are taken periodically, not continuously, so the latest backup can +> lag behind your live data by some time. Check **Last backup** on the page +> before you rely on it, and give it time to catch up if you just made +> important changes. + +You have two ways to get the files: + +- **Download full backup** — bundles every backup file into a single `.zip` + you can keep on your computer. A `RESTORE.md` with restore instructions is + included inside the archive. +- **Copy download script** (+ **Copy token**) — copies a small shell script + that downloads every file with `curl`. The script is safe to save or share: + it does **not** contain your token — it reads it from the + `HULY_BACKUP_TOKEN` environment variable, or prompts for it when you run it. + +Either option produces a folder that the restore tool can consume directly +(see Step 2). + +> [!TIP] +> **Large workspace?** Prefer the download script over the button. "Download +> full backup" assembles the whole archive in your browser's memory before +> saving it, which can be slow or fail outright once a workspace has a lot of +> data. The script downloads files one by one with `curl`, so it handles +> large backups more reliably. + +You can also expand **Backup Snapshots** or **Backup Files** to download +individual files, e.g. to verify access or fetch a single snapshot. + +> [!IMPORTANT] +> The **Not backed up** section lists blobs that are intentionally excluded +> from the regular backup (video, audio, and any file larger than the +> server's blob-size limit). Their content isn't in the archive or script +> above — if you need them on the destination, download each one individually +> from that list before you migrate. + +## Step 2. Restore into a Huly instance + +You'll need a Huly platform you control to restore into — either +[huly-selfhost](https://github.com/hcengineering/huly-selfhost), a +self-hosted deployment built from this monorepo, or another hosted instance +where you have admin access. Create the destination workspace first if it +doesn't exist yet. + +Restoring is done with the `backup-restore` command of the platform admin +tool (`@hcengineering/tool`, `dev/tool` in this repo). Exactly how you invoke +it depends on how the destination platform is run: + +- **Docker / self-hosted setup** — run it inside your `tool` container + (e.g. `docker compose run --rm tool backup-restore ...` or + `docker compose exec tool ...`, adjusted to your compose service name). +- **Monorepo checkout with Rush** — from `dev/tool`, run it against your + configured environment, e.g. `rushx run-local backup-restore ...` for a + local dev stack, or your own script that sets the same environment + variables against your real databases. + +In both cases the command and its arguments are the same: + +``` +backup-restore [date] --accounts +``` + +- `` — the folder you downloaded/unzipped in Step 1. +- `` — the destination workspace's identifier. +- `--accounts` — also restores the original users (their profile and social + identities) into the target workspace. Without this flag only documents are + restored, and every member has to be invited again by email. +- `--upgrade` — add this if the destination runs a newer model version than + the backup. +- `-m/--merge` — don't delete documents that are missing from the backup + (useful when merging into a workspace that already has data). + +> [!NOTE] +> Restoring accounts (`--accounts`) needs its own connection to the account +> database, configured via the `ACCOUNT_DB_URL` (and optional +> `ACCOUNT_DB_NS`) environment variables. See +> [`server/backup/README.md`](../../server/backup/README.md) for the full +> reference of flags and per-entry-point environment variables. + +Once the restore finishes, users sign in with their original email via a +one-time code (OTP) — make sure the destination platform can send mail. + +## Need help? + +Our team is happy to help with migration. Join the [Huly community](https://link.huly.io/slack) to ask questions and get updates, or email us directly at [artem@hardcoreeng.com](mailto:artem@hardcoreeng.com). + +## Troubleshooting + +- **401 Unauthorized while downloading** — your token expired, or you're not + an owner/admin of the workspace. Get a fresh token from the Backup page. +- **Members are missing after restore** — re-run with `--accounts` and a + valid `ACCOUNT_DB_URL`; without both, only documents are restored. +- **A file/video is missing on the destination** — check the **Not backed + up** list on the Backup page and download it individually; it isn't part + of the regular backup. +- **Restore fails with a model/version error** — add `--upgrade` if the + destination runs a newer platform version than the source. + +## See also + +- [`server/backup/README.md`](../../server/backup/README.md) — full CLI + reference for `backup-restore`, including environment variables. +- [huly-selfhost](https://github.com/hcengineering/huly-selfhost) — official + self-hosted distribution. diff --git a/foundations/core/packages/account-client/src/client.ts b/foundations/core/packages/account-client/src/client.ts index 1a8e6ea430..0a15fa45ed 100644 --- a/foundations/core/packages/account-client/src/client.ts +++ b/foundations/core/packages/account-client/src/client.ts @@ -55,6 +55,7 @@ import type { Subscription, SubscriptionData, UserProfile, + WorkspaceConfiguration, WorkspaceLoginInfo, WorkspaceOperation } from './types' @@ -134,7 +135,11 @@ export interface AccountClient { getWorkspacesInfo: (workspaces: WorkspaceUuid[]) => Promise updateLastVisit: (workspaces: WorkspaceUuid[]) => Promise getRegionInfo: () => Promise - createWorkspace: (name: string, region?: string) => Promise + createWorkspace: ( + name: string, + region?: string, + configuration?: WorkspaceConfiguration + ) => Promise signUpOtp: (email: string, first: string, last: string) => Promise /** * Deprecated. Only to be used for dev setups without mail service. @@ -237,7 +242,7 @@ export interface AccountClient { addEmailSocialId: (email: string) => Promise addHulyAssistantSocialId: () => Promise refreshHulyAssistantToken: () => Promise - updatePasswordAgingRule: (days: number) => Promise + updatePasswordAgingRule: (days?: number) => Promise checkPasswordAging: () => Promise setMyProfile: (profile: Partial>) => Promise @@ -545,7 +550,7 @@ class AccountClientImpl implements AccountClient { await this.rpc(request) } - async updatePasswordAgingRule (days: number): Promise { + async updatePasswordAgingRule (days?: number): Promise { const request = { method: 'updatePasswordAgingRule' as const, params: { days } @@ -668,10 +673,14 @@ class AccountClientImpl implements AccountClient { return await this.rpc(request) } - async createWorkspace (workspaceName: string, region?: string): Promise { + async createWorkspace ( + workspaceName: string, + region?: string, + configuration?: WorkspaceConfiguration + ): Promise { const request = { method: 'createWorkspace' as const, - params: { workspaceName, region } + params: { workspaceName, region, configuration } } return await this.rpc(request) diff --git a/foundations/core/packages/account-client/src/types.ts b/foundations/core/packages/account-client/src/types.ts index c9f791f3c2..c7c68a45a5 100644 --- a/foundations/core/packages/account-client/src/types.ts +++ b/foundations/core/packages/account-client/src/types.ts @@ -14,6 +14,8 @@ import { IntegrationKind } from '@hcengineering/core' +export type { WorkspaceConfiguration } from '@hcengineering/core' + export interface LoginInfo { account: AccountUuid name?: string @@ -43,7 +45,7 @@ export interface LoginInfoWorkspace { role: AccountRole | null progress?: number branding?: string - passwordAgingRule?: number // in days + passwordAgingRule?: number | null // in days } export interface LoginInfoWithWorkspaces extends LoginInfo { diff --git a/foundations/core/packages/client-resources/src/connection.ts b/foundations/core/packages/client-resources/src/connection.ts index 0309ad7abe..0b3b44c772 100644 --- a/foundations/core/packages/client-resources/src/connection.ts +++ b/foundations/core/packages/client-resources/src/connection.ts @@ -777,7 +777,7 @@ class Connection implements ClientConnection { params: [_class, query, options], measure: (time, result, serverTime, queue, toReceive) => { if (typeof window !== 'undefined' && (time > 1000 || serverTime > 500)) { - console.error( + console.warn( 'measure slow findAll', time, serverTime, diff --git a/foundations/core/packages/core/lang/pl.json b/foundations/core/packages/core/lang/pl.json new file mode 100644 index 0000000000..95a4f0f1fb --- /dev/null +++ b/foundations/core/packages/core/lang/pl.json @@ -0,0 +1,77 @@ +{ + "string": { + "Id": "Identyfikator", + "Space": "Przestrzeń", + "Spaces": "Przestrzenie", + "SpacesDescription": "Zarządzaj typami przestrzeni wszystkich przestrzeni", + "TypedSpace": "Typowana przestrzeń", + "SpaceType": "Typ przestrzeni", + "Modified": "Modyfikowano", + "ModifiedDate": "Data modyfikacji", + "ModifiedBy": "Zmodyfikowane przez", + "Class": "Klasa", + "AttachedTo": "Dołączone do", + "AttachedToClass": "Dołączone do klasy", + "Name": "Nazwa", + "Description": "Opis", + "ShortDescription": "Krótki opis", + "Descriptor": "Opisujący", + "TargetClass": "Docelowa klasa", + "Role": "Rola", + "Roles": "Role", + "Private": "Prywatne", + "Archived": "Archiwizowane", + "ClassLabel": "Typ", + "ClassPropertyLabel": "Etykieta", + "String": "Tekst", + "Markup": "Znacznik", + "Number": "Liczba", + "Boolean": "Typ logiczny", + "Timestamp": "Data i godzina", + "Date": "Data", + "IntlString": "Tłumaczony tekst", + "Ref": "Odnośnik", + "Collection": "Kolekcja", + "Array": "Wielokrotny wybór", + "Enum": "Wybór", + "Members": "Członkowie", + "Hyperlink": "Hiperłącze", + "MarkupBlobRef": "Wspólny", + "Object": "Przedmiot", + "System": "System", + "CreatedBy": "Utworzone przez", + "CreatedDate": "Data utworzenia", + "Status": "Status", + "StatusCategory": "Kategoria statusu", + "Account": "Konto", + "Rank": "Ranga", + "Owners": "Właściciele", + "Permission": "Uprawnienia", + "CreateObject": "Utwórz przedmiot", + "UpdateObject": "Zaktualizuj przedmiot", + "DeleteObject": "Usuń przedmiot", + "ForbidDeleteObject": "Zabroń usunięcia przedmiotu", + "UpdateSpace": "Zaktualizuj przestrzeń", + "ArchiveSpace": "Archiwizuj przestrzeń", + "CreateObjectDescription": "Pozwala użytkownikom tworzyć obiekty w przestrzeni", + "UpdateObjectDescription": "Pozwala użytkownikom aktualizować obiekty w przestrzeni", + "DeleteObjectDescription": "Pozwala użytkownikom usuwać obiekty w przestrzeni", + "ForbidDeleteObjectDescription": "Uniemożliwia użytkownikom usuwania obiektów w przestrzeni", + "UpdateSpaceDescription": "Pozwala użytkownikom aktualizować przestrzeń", + "ArchiveSpaceDescription": "Pozwala użytkownikom archiwizować przestrzeń", + "AutoJoin": "Dołącz automatycznie", + "AutoJoinDescr": "Dodawaj automatycznie nowych członków do tej przestrzeni", + "AutoJoinGuests": "Goście dołączają automatycznie", + "AutoJoinGuestsDescr": "Kiedy opcja jest włączona, goście będą automatycznie dołączani do tej przestrzeni", + "RBAC": "Kontrola dostępu oparta na rolach", + "RBACDescr": "Wymagaj kontroli dostępu opartej na rolach, aby wykonywać akcje w tej przestrzeni", + "BlobSize": "Rozmiar pliku", + "BlobContentType": "Zawartość", + "Relation": "Skojarzenie", + "Relations": "Skojarzenia", + "AddRelation": "Dodaj skojarzenie", + "PersonId": "Osoba", + "AccountId": "Konto", + "Version": "Wersja" + } +} diff --git a/foundations/core/packages/core/src/classes.ts b/foundations/core/packages/core/src/classes.ts index c9db96401a..b9c62466d9 100644 --- a/foundations/core/packages/core/src/classes.ts +++ b/foundations/core/packages/core/src/classes.ts @@ -227,6 +227,7 @@ export interface Attribute extends Doc, UXObject { defaultValue?: any automationOnly?: boolean rank?: Rank + required?: boolean // Extra customization properties [key: string]: any @@ -899,6 +900,25 @@ export type WorkspaceUpdateEvent = | 'delete-started' | 'delete-done' +/** + * Initial-state configuration captured at workspace creation. Currently only + * carries whether the workspace should be populated with demo content. Lives + * on `WorkspaceInfo.pendingConfiguration` until consumed by workspace-service + * after model init, then cleared back to `null`. + * + * Kept as a struct (rather than a bare boolean) so future opt-in fields can + * be added without breaking the wire format. + * + * @public + */ +export interface WorkspaceConfiguration { + /** + * Whether to run the workspace init script (sample projects and other demo content). + * Defaults to `true` on the server side to preserve legacy behavior. + */ + withDemoContent?: boolean +} + export interface WorkspaceInfo { uuid: WorkspaceUuid dataId?: WorkspaceDataId // Old workspace identifier. E.g. Database name in Mongo, bucket in R2, etc. @@ -911,7 +931,10 @@ export interface WorkspaceInfo { billingAccount?: PersonUuid // Should always be set for NEW workspaces allowReadOnlyGuest?: boolean // Should always be set for NEW workspaces allowGuestSignUp?: boolean // Should always be set for NEW workspaces - passwordAgingRule?: number // in days + passwordAgingRule?: number | null // in days + // Initial-state configuration set by the user at workspace creation. Read once + // by workspace-service after model init, then cleared back to `null`. + pendingConfiguration?: WorkspaceConfiguration | null } export interface BackupStatus { @@ -928,6 +951,7 @@ export interface UsageStatus { usage: Record startTime: Timestamp updateTime: Timestamp + limitsExceededSince?: Timestamp // Timestamp when current usage first exceeded the workspace plan limits. } export interface WorkspaceInfoWithStatus extends WorkspaceInfo { diff --git a/foundations/core/packages/core/src/hierarchy.ts b/foundations/core/packages/core/src/hierarchy.ts index 9b8fc3665c..32d055c7a4 100644 --- a/foundations/core/packages/core/src/hierarchy.ts +++ b/foundations/core/packages/core/src/hierarchy.ts @@ -162,6 +162,23 @@ export class Hierarchy { return Array.from(resultSet) } + getAllPossibleMixins (_class: Ref>, to: Ref> = core.class.Doc): Ref>[] { + const result = new Set>>() + let c = this.getClass(_class) + + while (c._id !== to && c.extends !== undefined) { + const _descendants = this.descendants.get(c._id) + for (const d of _descendants ?? []) { + if (this.isMixin(d) && this.getBaseClass(d) === c._id) { + result.add(d) + } + } + c = this.getClass(c.extends) + } + + return [...result] + } + isMixin (_class: Ref>): boolean { const data = this.classifiers.get(_class) return data !== undefined && this._isMixin(data) diff --git a/foundations/core/packages/core/src/versioning.ts b/foundations/core/packages/core/src/versioning.ts index 34db2fd7bb..6ab64893a5 100644 --- a/foundations/core/packages/core/src/versioning.ts +++ b/foundations/core/packages/core/src/versioning.ts @@ -1,4 +1,4 @@ -import { Class, Doc, PersonId, Ref } from './classes' +import { Class, Doc, Mixin, PersonId, Ref } from './classes' export interface VersionableDoc extends Doc { baseId?: Ref @@ -10,4 +10,7 @@ export interface VersionableDoc extends Doc { export interface VersionableClass extends Class { enabled: boolean + excludedProperties?: string[] + excludedRelations?: string[] // ${associationId}_${a|b} + excludeMixins?: Ref>[] } diff --git a/foundations/core/packages/platform/lang/pl.json b/foundations/core/packages/platform/lang/pl.json new file mode 100644 index 0000000000..f87d0e521a --- /dev/null +++ b/foundations/core/packages/platform/lang/pl.json @@ -0,0 +1,30 @@ +{ + "status": { + "LoadingPlugin": "Wczytywanie wtyczki {plugin}...", + "UnknownError": "Nieznany błąd: {message}", + "InvalidId": "Nieprawidłowy identyfikator: {id}", + "BadRequest": "Nieprawidłowe żądanie", + "Forbidden": "Brak dostępu", + "Conflict": "Konflikt", + "ExpiredLink": "Ten link zaproszenia wygasł", + "Unauthorized": "Nieautoryzowany", + "UnknownMethod": "Nieznana metoda: {method}", + "InternalServerError": "Wewnętrzny błąd serwera", + "MaintenanceWarning": "Konserwacja zaplanowana za", + "MaintenanceWarningTime": "{time, plural, =1 {mniej niż minutę} few {# minuty} other {# minut}}", + "AccountNotFound": "Nie znaleziono konta lub podane poświadczenia są nieprawidłowe", + "AccountNotConfirmed": "Konto niepotwierdzone", + "WorkspaceNotFound": "Nie znaleziono przestrzeni roboczej", + "AccountAlreadyExists": "Konto już istnieje", + "WorkspaceRateLimit": "Serwer jest zajęty, proszę poczekać chwilę i spróbować ponownie", + "AccountAlreadyConfirmed": "Konto zostało już potwierdzone", + "WorkspaceAlreadyExists": "Przestrzeń robocza już istnieje", + "InvalidOtp": "Nieprawidłowy kod", + "PasswordLoginLocked": "Logowanie hasłem jest zablokowane z powodu zbyt wielu nieudanych prób. Użyj metody logowania hasłem jednorazowym, aby odblokować konto.", + "InviteNotFound": "Zaproszenie za pomocą adresu e-mail: nie znaleziono adresu {email}.", + "WorkspaceLimitReached": "Osiągnąłeś limit przestrzeni roboczych. Skontaktuj się z nami...", + "ReadOnlyAccount": "Wersja demonstracyjna dla anonimowego gościa", + "SystemAccount": "Konto systemowe", + "SocialIdAlreadyExists": "Identyfikator społecznościowy już istnieje" + } +} diff --git a/foundations/core/packages/text/src/nodes/reference.ts b/foundations/core/packages/text/src/nodes/reference.ts index 613a51eca1..8a9bbf685a 100644 --- a/foundations/core/packages/text/src/nodes/reference.ts +++ b/foundations/core/packages/text/src/nodes/reference.ts @@ -22,6 +22,7 @@ export interface ReferenceNodeProps { id: Ref objectclass: Ref> label: string + fixed?: boolean } export interface ReferenceOptions { @@ -42,7 +43,19 @@ export const ReferenceNode = Node.create({ return { id: getDataAttribute('id'), objectclass: getDataAttribute('objectclass'), - label: getDataAttribute('label') + label: getDataAttribute('label'), + fixed: { + default: null, + parseHTML: (element) => element.getAttribute('data-fixed') === 'true', + renderHTML: (attributes) => { + if (attributes.fixed !== true) { + return {} + } + return { + 'data-fixed': 'true' + } + } + } } }, @@ -91,6 +104,7 @@ function getAttrs (el: HTMLSpanElement): Attrs | false { const id = el.dataset.id?.trim() const label = el.dataset.label?.trim() const objectclass = el.dataset.objectclass?.trim() + const fixed = el.getAttribute('data-fixed') === 'true' if (id == null || label == null || objectclass == null) { return false @@ -99,6 +113,7 @@ function getAttrs (el: HTMLSpanElement): Attrs | false { return { id, label, - objectclass + objectclass, + fixed: fixed || null } } diff --git a/foundations/server/packages/server/src/sessionManager.ts b/foundations/server/packages/server/src/sessionManager.ts index 0b97765794..55dfa351f8 100644 --- a/foundations/server/packages/server/src/sessionManager.ts +++ b/foundations/server/packages/server/src/sessionManager.ts @@ -766,7 +766,7 @@ export class TSessionManager implements SessionManager { this.workspaceInfoCache.delete(token.workspace) } - if (wsInfo.passwordAgingRule !== undefined && wsInfo.passwordAgingRule > 0) { + if (wsInfo.passwordAgingRule != null && wsInfo.passwordAgingRule > 0) { const isPasswordAgingOk = await this.checkPasswordAging(ctx, rawToken) if (!isPasswordAgingOk) { return { error: new Status(Severity.ERROR, platform.status.PasswordExpired, {}), terminate: true } diff --git a/models/all/src/index.ts b/models/all/src/index.ts index e41b28a9da..e33dfc98ab 100644 --- a/models/all/src/index.ts +++ b/models/all/src/index.ts @@ -103,7 +103,7 @@ import documents, { documentsId, createModel as documentsModel } from '@hcengine import { hulyMailId, createModel as hulyMailModel } from '@hcengineering/model-huly-mail' import { mailId, createModel as mailModel } from '@hcengineering/model-mail' import products, { productsId, createModel as productsModel } from '@hcengineering/model-products' -import { questionsId, createModel as questionsModel } from '@hcengineering/model-questions' +import questions, { questionsId, createModel as questionsModel } from '@hcengineering/model-questions' import { serverProductsId, createModel as serverProductsModel } from '@hcengineering/model-server-products' import { serverTrainingId, createModel as serverTrainingModel } from '@hcengineering/model-server-training' import testManagement, { @@ -246,6 +246,7 @@ export default function buildModel (): Builder { description: telegram.string.ConfigDescription, enabled: true, beta: true, + icon: contact.icon.Telegram, classFilter: defaultFilter } ], @@ -269,6 +270,7 @@ export default function buildModel (): Builder { description: gmail.string.ConfigDescription, enabled: true, beta: true, + icon: contact.icon.Email, classFilter: defaultFilter } ], @@ -420,6 +422,7 @@ export default function buildModel (): Builder { description: documents.string.ConfigDescription, enabled: false, beta: false, + icon: documents.icon.DocumentApplication, classFilter: defaultFilter } ], @@ -427,10 +430,11 @@ export default function buildModel (): Builder { questionsModel, questionsId, { - label: setting.string.Configure, + label: questions.string.ConfigLabel, + description: questions.string.ConfigDescription, enabled: true, beta: false, - hidden: false, + icon: questions.icon.Question, classFilter: defaultFilter } ], @@ -442,6 +446,7 @@ export default function buildModel (): Builder { description: trainings.string.ConfigDescription, enabled: false, beta: false, + icon: trainings.icon.TrainingApplication, classFilter: defaultFilter } ], @@ -453,6 +458,7 @@ export default function buildModel (): Builder { description: products.string.ConfigDescription, enabled: false, beta: false, + icon: products.icon.ProductsApplication, classFilter: defaultFilter } ], @@ -464,6 +470,7 @@ export default function buildModel (): Builder { description: testManagement.string.ConfigDescription, enabled: true, beta: true, + icon: testManagement.icon.TestManagementApplication, classFilter: defaultFilter } ], @@ -475,6 +482,7 @@ export default function buildModel (): Builder { description: survey.string.ConfigDescription, enabled: false, beta: true, + icon: survey.icon.Survey, classFilter: defaultFilter } ], diff --git a/models/billing/src/index.ts b/models/billing/src/index.ts index 54d65f6c67..f581b00664 100644 --- a/models/billing/src/index.ts +++ b/models/billing/src/index.ts @@ -17,7 +17,7 @@ import { Model, UX, type Builder } from '@hcengineering/model' import core, { TDoc } from '@hcengineering/model-core' import { type IntlString } from '@hcengineering/platform' import setting from '@hcengineering/setting' -import billing, { type Tier } from '@hcengineering/billing' +import billing, { TIER_LIMITS_GB, type Tier } from '@hcengineering/billing' import { AccountRole, DOMAIN_MODEL } from '@hcengineering/core' import presentation from '@hcengineering/model-presentation' import workbench from '@hcengineering/workbench' @@ -58,8 +58,8 @@ export function createModel (builder: Builder): void { { label: billing.string.Common, description: billing.string.CommonDescription, - storageLimitGB: 10, - trafficLimitGB: 10, + storageLimitGB: TIER_LIMITS_GB.common.storageGB, + trafficLimitGB: TIER_LIMITS_GB.common.trafficGB, priceMonthly: 0, index: 0 }, @@ -72,8 +72,8 @@ export function createModel (builder: Builder): void { { label: billing.string.Rare, description: billing.string.RareDescription, - storageLimitGB: 100, - trafficLimitGB: 100, + storageLimitGB: TIER_LIMITS_GB.rare.storageGB, + trafficLimitGB: TIER_LIMITS_GB.rare.trafficGB, priceMonthly: 19.99, index: 1, color: 'Sky' @@ -87,8 +87,8 @@ export function createModel (builder: Builder): void { { label: billing.string.Epic, description: billing.string.EpicDescription, - storageLimitGB: 1000, - trafficLimitGB: 500, + storageLimitGB: TIER_LIMITS_GB.epic.storageGB, + trafficLimitGB: TIER_LIMITS_GB.epic.trafficGB, priceMonthly: 99.99, index: 2, color: 'Orchid' @@ -102,8 +102,8 @@ export function createModel (builder: Builder): void { { label: billing.string.Legendary, description: billing.string.LegendaryDescription, - storageLimitGB: 10000, - trafficLimitGB: 2000, + storageLimitGB: TIER_LIMITS_GB.legendary.storageGB, + trafficLimitGB: TIER_LIMITS_GB.legendary.trafficGB, priceMonthly: 399.99, index: 3, color: 'Orange' diff --git a/models/card/src/actions.ts b/models/card/src/actions.ts index c948a572a8..436b479eb0 100644 --- a/models/card/src/actions.ts +++ b/models/card/src/actions.ts @@ -264,7 +264,13 @@ export function createActions (builder: Builder): void { createAction( builder, { - action: card.actionImpl.DuplicateCard, + action: view.actionImpl.ShowPopup, + actionProps: { + component: card.component.DuplicateCard, + fillProps: { + _object: 'value' + } + }, label: card.string.Duplicate, icon: card.icon.Duplicate, input: 'focus', diff --git a/models/card/src/index.ts b/models/card/src/index.ts index 14c76397eb..8726893bb5 100644 --- a/models/card/src/index.ts +++ b/models/card/src/index.ts @@ -12,7 +12,6 @@ // limitations under the License. import activity from '@hcengineering/activity' -import communication from '@hcengineering/communication' import { type CanCreateCardResource, type Card, @@ -23,6 +22,7 @@ import { type CardViewDefaults, type CreateCardExtension, DOMAIN_CARD, + type DuplicateSetting, type ExportExtension, type ExportFunc, type FavoriteCard, @@ -34,6 +34,8 @@ import { type Tag } from '@hcengineering/card' import chunter from '@hcengineering/chunter' +import communication from '@hcengineering/communication' +import converter from '@hcengineering/converter' import core, { AccountRole, type Blobs, @@ -46,6 +48,7 @@ import core, { DOMAIN_SPACE, IndexKind, type MarkupBlobRef, + type Mixin as MixinType, type MixinData, type Rank, type Ref, @@ -77,7 +80,7 @@ import presentation from '@hcengineering/model-presentation' import setting from '@hcengineering/model-setting' import view, { type Viewlet } from '@hcengineering/model-view' import workbench, { WidgetType } from '@hcengineering/model-workbench' -import converter from '@hcengineering/converter' +import notification from '@hcengineering/notification' import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform' import time, { type ToDo } from '@hcengineering/time' import { PaletteColorIndexes } from '@hcengineering/ui/src/colors' @@ -86,7 +89,6 @@ import { type BuildModelKey, type ViewOptionModel } from '@hcengineering/view' import { createActions } from './actions' import { defineActionPermissions, definePermissions } from './permissions' import card from './plugin' -import notification from '@hcengineering/notification' export { cardId } from '@hcengineering/card' @@ -98,6 +100,9 @@ export class TMasterTag extends TClass implements MasterTag { @Prop(TypeBoolean(), card.string.SingleColumn) singleColumn?: boolean + + @Prop(TypeBoolean(), card.string.BaseType) + baseType?: boolean } @Model(card.class.Tag, core.class.Mixin) @@ -223,6 +228,13 @@ export class TExportExtension extends TDoc implements ExportExtension { func!: Resource } +@Mixin(card.mixin.DuplicateSetting, card.class.MasterTag) +export class TDuplicateSetting extends TMasterTag implements DuplicateSetting { + excludedProperties?: string[] + excludedRelations?: string[] // ${associationId}_${a|b} + excludeMixins?: Ref>[] +} + export * from './migration' const showAllVersionsOption: ViewOptionModel = { @@ -441,7 +453,8 @@ export function createModel (builder: Builder): void { TFavoriteCard, TFavoriteType, TCreateCardExtension, - TExportExtension + TExportExtension, + TDuplicateSetting ) builder.createDoc( @@ -950,6 +963,10 @@ export function createModel (builder: Builder): void { titleProvider: card.function.CardTitleProvider }) + builder.mixin(card.class.Card, core.class.Class, view.mixin.ReferenceObjectProvider, { + provider: card.function.CardReferenceObjectProvider + }) + builder.mixin(card.class.Card, core.class.Class, view.mixin.LinkProvider, { encode: card.function.GetCardLink }) @@ -1048,6 +1065,7 @@ export function createModel (builder: Builder): void { }) builder.mixin(card.class.Card, core.class.Class, view.mixin.ObjectFactory, { + component: card.component.CreateCard, create: card.function.CardFactory }) diff --git a/models/card/src/plugin.ts b/models/card/src/plugin.ts index 74a3e31fef..ee8e754b19 100644 --- a/models/card/src/plugin.ts +++ b/models/card/src/plugin.ts @@ -30,7 +30,6 @@ export default mergeIds(cardId, card, { }, actionImpl: { DeleteMasterTag: '' as ViewAction, - DuplicateCard: '' as ViewAction, EditSpace: '' as ViewAction, CreateChild: '' as ViewAction }, @@ -39,7 +38,7 @@ export default mergeIds(cardId, card, { SetParent: '' as Ref>, UnsetParent: '' as Ref>, PublicLink: '' as Ref>, - Duplicate: '' as Ref, + Duplicate: '' as Ref>, CreateChild: '' as Ref }, category: { diff --git a/models/contact/src/index.ts b/models/contact/src/index.ts index a623e3504c..8eee6fdbc8 100644 --- a/models/contact/src/index.ts +++ b/models/contact/src/index.ts @@ -274,6 +274,10 @@ export class TEmployee extends TPerson implements Employee { position?: string | null declare personUuid?: AccountUuid + + @Prop(TypeString(), contact.string.Timezone) + @Hidden() + timezone?: string } @Model(contact.class.ContactsTab, core.class.Doc, DOMAIN_MODEL) diff --git a/models/controlled-documents/src/index.ts b/models/controlled-documents/src/index.ts index 168d30159a..e699731090 100644 --- a/models/controlled-documents/src/index.ts +++ b/models/controlled-documents/src/index.ts @@ -471,6 +471,10 @@ export function createModel (builder: Builder): void { titleProvider: documents.function.DocumentMetaTitleProvider }) + builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.ReferenceVersionsProvider, { + provider: documents.function.DocumentMetaReferenceVersionsProvider + }) + builder.mixin(documents.class.DocumentMeta, core.class.Class, view.mixin.LinkProvider, { encode: documents.function.GetDocumentMetaLinkFragment }) diff --git a/models/core/src/core.ts b/models/core/src/core.ts index 02e69d551b..d6f4588a90 100644 --- a/models/core/src/core.ts +++ b/models/core/src/core.ts @@ -247,6 +247,7 @@ export class TAttribute extends TDoc implements AnyAttribute { isCustom?: boolean defaultValue?: any automationOnly?: boolean + required?: boolean } @Model(core.class.Type, core.class.Obj, DOMAIN_MODEL) @@ -447,4 +448,7 @@ export class TCollaborator extends TAttachedDoc implements Collaborator { @MMixin(core.mixin.VersionableClass, core.class.Class) export class TVersionableClass extends TClass implements VersionableClass { enabled!: boolean + excludedProperties?: string[] + excludedRelations?: string[] // ${associationId}_${a|b} + excludeMixins?: Ref>[] } diff --git a/models/process/src/functions.ts b/models/process/src/functions.ts index ba7c6f932f..11d982c12c 100644 --- a/models/process/src/functions.ts +++ b/models/process/src/functions.ts @@ -600,6 +600,19 @@ export function defineFunctions (builder: Builder): void { process.function.StringFromNumber ) + builder.createDoc( + process.class.ProcessFunction, + core.space.Model, + { + of: core.class.TypeIdentifier, + to: core.class.TypeString, + category: 'attribute', + label: process.string.TextFromIdentifier, + type: 'convert' + }, + process.function.StringFromIdentifier + ) + builder.createDoc( process.class.ProcessFunction, core.space.Model, diff --git a/models/process/src/index.ts b/models/process/src/index.ts index 562b75eeb1..4017d9373e 100644 --- a/models/process/src/index.ts +++ b/models/process/src/index.ts @@ -34,8 +34,8 @@ import { TypeAny, TypeBoolean, TypeIntlString, - TypeRecord, TypeRank, + TypeRecord, TypeRef, TypeString, UX @@ -46,7 +46,7 @@ import { TToDo } from '@hcengineering/model-time' import view, { createAction } from '@hcengineering/model-view' import workbench from '@hcengineering/model-workbench' import notification, { type NotificationGroup } from '@hcengineering/notification' -import { type Asset, getEmbeddedLabel, type IntlString, type Resource } from '@hcengineering/platform' +import { type Asset, type IntlString, type Resource, getEmbeddedLabel } from '@hcengineering/platform' import { type ApproveRequest, type CheckFunc, @@ -66,6 +66,7 @@ import { type ProcessCustomEvent, type ProcessFunction, type ProcessToDo, + type SlotModel, type State, type Step, type Transition, @@ -108,6 +109,12 @@ export class TProcess extends TDoc implements Process { @Prop(TypeBoolean(), process.string.AutomationOnly) automationOnly: boolean | undefined + @Prop(TypeRecord(), process.string.RequiredSlots) + requiredSlots?: Record + + @Prop(TypeRecord(), process.string.Bindings) + bindings?: Record + context!: Record } @@ -209,6 +216,9 @@ export class TProcessToDo extends TToDo implements ProcessToDo { @Prop(TypeBoolean(), process.string.Rollback) withRollback!: boolean + + @Prop(TypeBoolean(), process.string.AskRequired) + askRequired?: boolean } @Model(process.class.ApproveRequest, process.class.ProcessToDo) diff --git a/models/process/src/triggers.ts b/models/process/src/triggers.ts index 5d783bfbdb..3460d5b0f5 100644 --- a/models/process/src/triggers.ts +++ b/models/process/src/triggers.ts @@ -124,6 +124,21 @@ export function defineTriggers (builder: Builder): void { process.trigger.WhenFieldChanges ) + builder.createDoc( + process.class.Trigger, + core.space.Model, + { + label: process.string.WhenRequiredFieldsFilled, + icon: process.icon.WhenCardMatches, + presenter: process.component.CardUpdatePresenter, + requiredParams: [], + checkFunction: process.triggerCheck.RequiredFieldsFilledCheck, + init: false, + auto: true + }, + process.trigger.WhenRequiredFieldsFilled + ) + // builder.createDoc( // process.class.Trigger, // core.space.Model, diff --git a/models/products/src/index.ts b/models/products/src/index.ts index 8951fb52c9..bfd8efe585 100644 --- a/models/products/src/index.ts +++ b/models/products/src/index.ts @@ -254,6 +254,21 @@ function defineProduct (builder: Builder): void { builder.mixin(products.class.Product, core.class.Class, view.mixin.IgnoreActions, { actions: [tracker.action.NewRelatedIssue] }) + + createAction( + builder, + { + action: products.actionImpl.CreateProductVersion, + label: products.string.CreateProductVersion, + icon: products.icon.ProductVersion, + visibilityTester: products.function.CanCreateProductVersion, + category: view.category.General, + input: 'focus', + target: products.class.Product, + context: { mode: ['context', 'browser'], group: 'create' } + }, + products.action.CreateProductVersion + ) } function defineSpaceType (builder: Builder): void { diff --git a/models/products/src/plugin.ts b/models/products/src/plugin.ts index 64d0929906..aa0463d6af 100644 --- a/models/products/src/plugin.ts +++ b/models/products/src/plugin.ts @@ -19,12 +19,16 @@ import products from '@hcengineering/products-resources/src/plugin' import type { Client, Doc, Ref, Role } from '@hcengineering/core' import { type Resource, mergeIds } from '@hcengineering/platform' import { type AnyComponent } from '@hcengineering/ui/src/types' -import type { Action } from '@hcengineering/view' +import type { Action, ViewAction } from '@hcengineering/view' export default mergeIds(productsId, products, { action: { + CreateProductVersion: '' as Ref>, DeleteProductVersion: '' as Ref> }, + actionImpl: { + CreateProductVersion: '' as ViewAction + }, ids: { ModulePermissionGroup: '' as Ref, ModulePermissionGroupReadOnlyGuest: '' as Ref @@ -44,6 +48,7 @@ export default mergeIds(productsId, products, { ProductVersionVersionPresenter: '' as AnyComponent }, function: { + CanCreateProductVersion: '' as Resource<(doc?: Doc | Doc[]) => Promise>, CanDeleteProductVersion: '' as Resource<(doc?: Doc | Doc[]) => Promise>, ProductIdentifierProvider: '' as Resource<(client: Client, ref: Ref, doc?: T) => Promise> }, diff --git a/models/server-process/src/index.ts b/models/server-process/src/index.ts index dafae6227b..a1179e8160 100644 --- a/models/server-process/src/index.ts +++ b/models/server-process/src/index.ts @@ -70,6 +70,11 @@ export function createModel (builder: Builder): void { serverCheckFunc: serverProcess.func.FieldChangedCheck }) + builder.mixin(process.trigger.WhenRequiredFieldsFilled, process.class.Trigger, serverProcess.mixin.TriggerImpl, { + preventRollback: true, + serverCheckFunc: serverProcess.func.RequiredFieldsFilledCheck + }) + builder.mixin(process.trigger.OnApproveRequestApproved, process.class.Trigger, serverProcess.mixin.TriggerImpl, { preventRollback: true, serverCheckFunc: serverProcess.func.ApproveRequestApproved @@ -207,6 +212,10 @@ export function createModel (builder: Builder): void { func: serverProcess.transform.DateFromNumber }) + builder.mixin(process.function.StringFromIdentifier, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, { + func: serverProcess.transform.StringFromIdentifier + }) + builder.mixin(process.function.NumberFromString, process.class.ProcessFunction, serverProcess.mixin.FuncImpl, { func: serverProcess.transform.NumberFromString }) @@ -410,6 +419,17 @@ export function createModel (builder: Builder): void { }, isAsync: true }) + + builder.createDoc(serverCore.class.Trigger, core.space.Model, { + trigger: serverProcess.trigger.OnExecutionDone, + txMatch: { + _class: core.class.TxUpdateDoc, + objectClass: process.class.Execution, + 'operations.status': ExecutionStatus.Done + }, + isAsync: true + }) + builder.createDoc(serverCore.class.Trigger, core.space.Model, { trigger: serverProcess.trigger.OnExecutionCreate, txMatch: { diff --git a/models/view/src/index.ts b/models/view/src/index.ts index d8670ffb18..b5b501624c 100644 --- a/models/view/src/index.ts +++ b/models/view/src/index.ts @@ -92,6 +92,8 @@ import { type ObjectValidator, type PreviewPresenter, type ReferenceObjectProvider, + type ReferenceVersion, + type ReferenceVersionsProvider, type SortFunc, type SpaceHeader, type SpaceName, @@ -275,6 +277,11 @@ export class TReferenceObjectProvider extends TClass implements ReferenceObjectP provider!: Resource<(client: Client, ref: Ref, doc?: T) => Promise> } +@Mixin(view.mixin.ReferenceVersionsProvider, core.class.Class) +export class TReferenceVersionsProvider extends TClass implements ReferenceVersionsProvider { + provider!: Resource<(client: Client, ref: Ref, doc?: T) => Promise> +} + @Mixin(view.mixin.ObjectTooltip, core.class.Class) export class TObjectTooltip extends TClass implements ObjectTooltip { provider!: Resource<(client: Client, doc?: Doc | null) => Promise> @@ -523,6 +530,7 @@ export function createModel (builder: Builder): void { TGroupping, TObjectIdentifier, TReferenceObjectProvider, + TReferenceVersionsProvider, TObjectTooltip, TObjectIcon, TAttrPresenter, diff --git a/packages/panel/src/components/Panel.svelte b/packages/panel/src/components/Panel.svelte index e87244e632..ad6f12c5a3 100644 --- a/packages/panel/src/components/Panel.svelte +++ b/packages/panel/src/components/Panel.svelte @@ -22,6 +22,7 @@ import { Component, deviceOptionsStore as deviceInfo, + printModeStore, Panel, Scroller, resizeObserver, @@ -122,6 +123,8 @@ startScrollHeightCheck() } }) + + $: max = useMaxWidth === true || $printModeStore {#if $deviceInfo.isMobile} -
+
{#if showActivity} {#key object._id} @@ -265,7 +268,7 @@
0} style:--side-content-space={`${sideContentSpace}px`} > @@ -296,7 +299,7 @@ >
0} style:--side-content-space={`${sideContentSpace}px`} use:resizeObserver={(element) => { diff --git a/packages/presentation/lang/cs.json b/packages/presentation/lang/cs.json index 83ff9d6300..ce569b4811 100644 --- a/packages/presentation/lang/cs.json +++ b/packages/presentation/lang/cs.json @@ -56,7 +56,8 @@ "LineTool": "Čára", "RectangleTool": "Obdélník", "EllipseTool": "Elipsa", - "PaletteManagementMenu": "Spravovat barevné předvolby" + "PaletteManagementMenu": "Spravovat barevné předvolby", + "BetaVersion": "Beta verze" }, "status": { "FileTooLarge": "Soubor je příliš velký" diff --git a/packages/presentation/lang/de.json b/packages/presentation/lang/de.json index d9ea8df19f..742489df91 100644 --- a/packages/presentation/lang/de.json +++ b/packages/presentation/lang/de.json @@ -56,7 +56,8 @@ "LineTool": "Linie", "RectangleTool": "Rechteck", "EllipseTool": "Ellipse", - "PaletteManagementMenu": "Farbpresets verwalten" + "PaletteManagementMenu": "Farbpresets verwalten", + "BetaVersion": "Beta-Version" }, "status": { "FileTooLarge": "Datei zu groß" diff --git a/packages/presentation/lang/en.json b/packages/presentation/lang/en.json index 5bc374d2af..2bbb1fb431 100644 --- a/packages/presentation/lang/en.json +++ b/packages/presentation/lang/en.json @@ -56,7 +56,8 @@ "LineTool": "Line", "RectangleTool": "Rectangle", "EllipseTool": "Ellipse", - "PaletteManagementMenu": "Manage color presets" + "PaletteManagementMenu": "Manage color presets", + "BetaVersion": "Beta version" }, "status": { "FileTooLarge": "File too large" diff --git a/packages/presentation/lang/es.json b/packages/presentation/lang/es.json index 9381530fae..ddf2d04552 100644 --- a/packages/presentation/lang/es.json +++ b/packages/presentation/lang/es.json @@ -56,7 +56,8 @@ "LineTool": "Línea", "RectangleTool": "Rectángulo", "EllipseTool": "Elipse", - "PaletteManagementMenu": "Gestionar preajustes de color" + "PaletteManagementMenu": "Gestionar preajustes de color", + "BetaVersion": "Versión beta" }, "status": { "FileTooLarge": "Archivo demasiado grande" diff --git a/packages/presentation/lang/fr.json b/packages/presentation/lang/fr.json index ca016bba89..c3c2188f45 100644 --- a/packages/presentation/lang/fr.json +++ b/packages/presentation/lang/fr.json @@ -56,7 +56,8 @@ "LineTool": "Ligne", "RectangleTool": "Rectangle", "EllipseTool": "Ellipse", - "PaletteManagementMenu": "Gérer les préréglages de couleur" + "PaletteManagementMenu": "Gérer les préréglages de couleur", + "BetaVersion": "Version bêta" }, "status": { "FileTooLarge": "Fichier trop volumineux" diff --git a/packages/presentation/lang/it.json b/packages/presentation/lang/it.json index 8331b58e27..6a54c2fbc4 100644 --- a/packages/presentation/lang/it.json +++ b/packages/presentation/lang/it.json @@ -56,7 +56,8 @@ "LineTool": "Linea", "RectangleTool": "Rettangolo", "EllipseTool": "Ellisse", - "PaletteManagementMenu": "Gestisci i preset di colore" + "PaletteManagementMenu": "Gestisci i preset di colore", + "BetaVersion": "Versione beta" }, "status": { "FileTooLarge": "File troppo grande" diff --git a/packages/presentation/lang/ja.json b/packages/presentation/lang/ja.json index 55de4e0de7..fbedfaadb1 100644 --- a/packages/presentation/lang/ja.json +++ b/packages/presentation/lang/ja.json @@ -56,7 +56,8 @@ "LineTool": "直線", "RectangleTool": "長方形", "EllipseTool": "楕円", - "PaletteManagementMenu": "カラープリセットを管理" + "PaletteManagementMenu": "カラープリセットを管理", + "BetaVersion": "ベータ版" }, "status": { "FileTooLarge": "ファイルサイズが大きすぎます" diff --git a/packages/presentation/lang/pl.json b/packages/presentation/lang/pl.json new file mode 100644 index 0000000000..9df2871a16 --- /dev/null +++ b/packages/presentation/lang/pl.json @@ -0,0 +1,65 @@ +{ + "string": { + "Create": "Utwórz", + "Cancel": "Anuluj", + "Ok": "W porządku", + "Save": "Zapisz", + "Saved": "Zapisano...", + "Download": "Pobierz", + "DownloadOriginal": "Pobierz oryginał", + "Delete": "Usuń", + "Close": "Zamknij", + "NotSelected": "Nie wybrano", + "Deselect": "Odznacz", + "Archived": "(zarchiwizowany)", + "AddSocialLinks": "Dodaj dane kontaktowe", + "EditSocialLinks": "Edytuj dane kontaktowe", + "Change": "Zmień", + "Remove": "Usuń", + "Search": "Wyszukaj...", + "Spaces": "Przestrzenie", + "NumberSpaces": "{count, plural, =0 {} =1 {W 1 miejscu} other {W # miejscach}}", + "InThis": "W tej przestrzeni {space}", + "NoMatchesInThis": "Brak pasujących elementów w tej przestrzeni {space}", + "NoMatchesFound": "Nie znaleziono pasujących elementów", + "NotInThis": "Nie w tej przestrzeni {space}", + "Match": "Pasujący element", + "Add": "Dodaj", + "Edit": "Edytuj", + "DocumentPreview": "Podgląd", + "MakePrivate": "Uczyń prywatnym", + "MakePrivateDescription": "Tylko członkowie mogą to zobaczyć", + "Created": "Utworzono", + "Selected": "Wybrano", + "NoResults": "Brak wyników do wyświetlenia", + "Next": "Dalej", + "FailedToPreview": "Nie udało się wyświetlić podglądu", + "ContentType": "Typ zawartości", + "ContentTypeNotSupported": "Podgląd nie jest dostępny dla tego typu zawartości", + "StartDrawing": "Pobazgraj", + "DrawingHistory": "Historia bohomazów", + "ColorAdd": "Dodaj początkowy kolor", + "ColorRemove": "Usuń początkowy kolor", + "ColorReset": "Resetuj początkowe kolory", + "Copy": "Kopiuj", + "DocumentUrlCopied": "Skopiowane!", + "CopyLink": "Skopiuj link", + "AccessDenied": "Obiekt nie istnieje lub nie masz uprawnień do dostępu.", + "UnableToFollowMention": "Nie można otworzyć wspomnianego obiektu", + "Undo": "Cofnij", + "Redo": "Ponów", + "ClearCanvas": "Wyczyść płótno", + "PenTool": "Pióro", + "EraserTool": "Gumka", + "PanTool": "Przesuwanie", + "TextTool": "Tekst", + "LineTool": "Linia", + "RectangleTool": "Prostokąt", + "EllipseTool": "Elipsa", + "PaletteManagementMenu": "Zarządzaj początkowymi kolorami", + "BetaVersion": "Wersja testowa" + }, + "status": { + "FileTooLarge": "Plik zbyt duży" + } +} diff --git a/packages/presentation/lang/pt-br.json b/packages/presentation/lang/pt-br.json index 3769f744a9..d67a79afb6 100644 --- a/packages/presentation/lang/pt-br.json +++ b/packages/presentation/lang/pt-br.json @@ -56,7 +56,8 @@ "LineTool": "Linha", "RectangleTool": "Retângulo", "EllipseTool": "Elipse", - "PaletteManagementMenu": "Gerenciar predefinições de cor" + "PaletteManagementMenu": "Gerenciar predefinições de cor", + "BetaVersion": "Versão beta" }, "status": { "FileTooLarge": "Arquivo muito grande" diff --git a/packages/presentation/lang/pt.json b/packages/presentation/lang/pt.json index 3fbb5c8d63..38a79eece4 100644 --- a/packages/presentation/lang/pt.json +++ b/packages/presentation/lang/pt.json @@ -56,7 +56,8 @@ "LineTool": "Linha", "RectangleTool": "Retângulo", "EllipseTool": "Elipse", - "PaletteManagementMenu": "Gerenciar predefinições de cor" + "PaletteManagementMenu": "Gerenciar predefinições de cor", + "BetaVersion": "Versão beta" }, "status": { "FileTooLarge": "Ficheiro demasiado grande" diff --git a/packages/presentation/lang/ru.json b/packages/presentation/lang/ru.json index 5bda323ec9..eb88f8bc23 100644 --- a/packages/presentation/lang/ru.json +++ b/packages/presentation/lang/ru.json @@ -56,7 +56,8 @@ "LineTool": "Линия", "RectangleTool": "Прямоугольник", "EllipseTool": "Эллипс", - "PaletteManagementMenu": "Управление цветовыми пресетами" + "PaletteManagementMenu": "Управление цветовыми пресетами", + "BetaVersion": "Бета-версия" }, "status": { "FileTooLarge": "Файл слишком большой" diff --git a/packages/presentation/lang/tr.json b/packages/presentation/lang/tr.json index 4fd772345e..8dd146aafb 100644 --- a/packages/presentation/lang/tr.json +++ b/packages/presentation/lang/tr.json @@ -56,7 +56,8 @@ "LineTool": "Çizgi", "RectangleTool": "Dikdörtgen", "EllipseTool": "Elips", - "PaletteManagementMenu": "Renk önayarlarını yönet" + "PaletteManagementMenu": "Renk önayarlarını yönet", + "BetaVersion": "Beta sürümü" }, "status": { "FileTooLarge": "Dosya çok büyük" diff --git a/packages/presentation/lang/zh.json b/packages/presentation/lang/zh.json index 6b11007086..d31e4e0249 100644 --- a/packages/presentation/lang/zh.json +++ b/packages/presentation/lang/zh.json @@ -56,7 +56,8 @@ "LineTool": "直线", "RectangleTool": "矩形", "EllipseTool": "椭圆", - "PaletteManagementMenu": "管理颜色预设" + "PaletteManagementMenu": "管理颜色预设", + "BetaVersion": "Beta 版本" }, "status": { "FileTooLarge": "文件太大" diff --git a/packages/presentation/src/components/AttributeBarEditor.svelte b/packages/presentation/src/components/AttributeBarEditor.svelte index 68d0dec7ba..ebf6621b9d 100644 --- a/packages/presentation/src/components/AttributeBarEditor.svelte +++ b/packages/presentation/src/components/AttributeBarEditor.svelte @@ -14,9 +14,10 @@ // limitations under the License. --> {#if editor} {#if showHeader}
{:else}