@open-mercato/core 0.6.8-develop.6924.1.a8d208fcdc → 0.6.8-develop.6930.1.1e5976efc3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/eudr_due_diligence_statement/index.js +47 -0
- package/dist/generated/entities/eudr_due_diligence_statement/index.js.map +7 -0
- package/dist/generated/entities/eudr_evidence_submission/index.js +51 -0
- package/dist/generated/entities/eudr_evidence_submission/index.js.map +7 -0
- package/dist/generated/entities/eudr_mitigation_action/index.js +31 -0
- package/dist/generated/entities/eudr_mitigation_action/index.js.map +7 -0
- package/dist/generated/entities/eudr_plot/index.js +39 -0
- package/dist/generated/entities/eudr_plot/index.js.map +7 -0
- package/dist/generated/entities/eudr_product_mapping/index.js +31 -0
- package/dist/generated/entities/eudr_product_mapping/index.js.map +7 -0
- package/dist/generated/entities/eudr_risk_assessment/index.js +35 -0
- package/dist/generated/entities/eudr_risk_assessment/index.js.map +7 -0
- package/dist/generated/entities/role/index.js +2 -0
- package/dist/generated/entities/role/index.js.map +2 -2
- package/dist/generated/entities.ids.generated.js +9 -0
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +121 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/helpers/integration/api.js +4 -0
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/modules/attachments/fields/attachment.js +5 -1
- package/dist/modules/attachments/fields/attachment.js.map +2 -2
- package/dist/modules/attachments/lib/crud.js +3 -1
- package/dist/modules/attachments/lib/crud.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +5 -0
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +4 -4
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/components/AuditLogsActions.js +9 -2
- package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
- package/dist/modules/auth/api/login.js +5 -2
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +8 -5
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +15 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +7 -1
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/commands/users.js +175 -76
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/data/entities.js +4 -0
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/lib/sessionIntegrity.js +1 -1
- package/dist/modules/auth/lib/sessionIntegrity.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +1 -1
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/migrations/Migration20260728134212_auth.js +14 -0
- package/dist/modules/auth/migrations/Migration20260728134212_auth.js.map +7 -0
- package/dist/modules/eudr/acl.js +43 -0
- package/dist/modules/eudr/acl.js.map +7 -0
- package/dist/modules/eudr/ai-agents.js +205 -0
- package/dist/modules/eudr/ai-agents.js.map +7 -0
- package/dist/modules/eudr/ai-tools/compliance-pack.js +495 -0
- package/dist/modules/eudr/ai-tools/compliance-pack.js.map +7 -0
- package/dist/modules/eudr/ai-tools/types.js +10 -0
- package/dist/modules/eudr/ai-tools/types.js.map +7 -0
- package/dist/modules/eudr/ai-tools.js +10 -0
- package/dist/modules/eudr/ai-tools.js.map +7 -0
- package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js +386 -0
- package/dist/modules/eudr/api/dashboard/widgets/compliance-overview/route.js.map +7 -0
- package/dist/modules/eudr/api/evidence-submissions/route.js +318 -0
- package/dist/modules/eudr/api/evidence-submissions/route.js.map +7 -0
- package/dist/modules/eudr/api/mitigation-actions/route.js +249 -0
- package/dist/modules/eudr/api/mitigation-actions/route.js.map +7 -0
- package/dist/modules/eudr/api/openapi.js +27 -0
- package/dist/modules/eudr/api/openapi.js.map +7 -0
- package/dist/modules/eudr/api/plots/import/route.js +294 -0
- package/dist/modules/eudr/api/plots/import/route.js.map +7 -0
- package/dist/modules/eudr/api/plots/route.js +279 -0
- package/dist/modules/eudr/api/plots/route.js.map +7 -0
- package/dist/modules/eudr/api/product-mappings/route.js +257 -0
- package/dist/modules/eudr/api/product-mappings/route.js.map +7 -0
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js +298 -0
- package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +7 -0
- package/dist/modules/eudr/api/product-mappings/suggestions/route.js +132 -0
- package/dist/modules/eudr/api/product-mappings/suggestions/route.js.map +7 -0
- package/dist/modules/eudr/api/reports/annual/route.js +266 -0
- package/dist/modules/eudr/api/reports/annual/route.js.map +7 -0
- package/dist/modules/eudr/api/risk-assessments/route.js +300 -0
- package/dist/modules/eudr/api/risk-assessments/route.js.map +7 -0
- package/dist/modules/eudr/api/statements/[id]/export/route.js +631 -0
- package/dist/modules/eudr/api/statements/[id]/export/route.js.map +7 -0
- package/dist/modules/eudr/api/statements/[id]/readiness/route.js +111 -0
- package/dist/modules/eudr/api/statements/[id]/readiness/route.js.map +7 -0
- package/dist/modules/eudr/api/statements/route.js +314 -0
- package/dist/modules/eudr/api/statements/route.js.map +7 -0
- package/dist/modules/eudr/api/suppliers/compliance/route.js +173 -0
- package/dist/modules/eudr/api/suppliers/compliance/route.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js +468 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js +351 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js +17 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +313 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/page.js +360 -0
- package/dist/modules/eudr/backend/eudr/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.js +346 -0
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/plots/[id]/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/create/page.js +253 -0
- package/dist/modules/eudr/backend/eudr/plots/create/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js +17 -0
- package/dist/modules/eudr/backend/eudr/plots/create/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/page.js +386 -0
- package/dist/modules/eudr/backend/eudr/plots/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/plots/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/plots/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js +253 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js +174 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js +17 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/create/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js +306 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js +335 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js +220 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js +17 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/create/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +324 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.js +648 -0
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/statements/[id]/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/create/page.js +432 -0
- package/dist/modules/eudr/backend/eudr/statements/create/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js +17 -0
- package/dist/modules/eudr/backend/eudr/statements/create/page.meta.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/page.js +309 -0
- package/dist/modules/eudr/backend/eudr/statements/page.js.map +7 -0
- package/dist/modules/eudr/backend/eudr/statements/page.meta.js +16 -0
- package/dist/modules/eudr/backend/eudr/statements/page.meta.js.map +7 -0
- package/dist/modules/eudr/ce.js +56 -0
- package/dist/modules/eudr/ce.js.map +7 -0
- package/dist/modules/eudr/commands/evidence-submissions.js +571 -0
- package/dist/modules/eudr/commands/evidence-submissions.js.map +7 -0
- package/dist/modules/eudr/commands/index.js +7 -0
- package/dist/modules/eudr/commands/index.js.map +7 -0
- package/dist/modules/eudr/commands/lifecycle-events.js +19 -0
- package/dist/modules/eudr/commands/lifecycle-events.js.map +7 -0
- package/dist/modules/eudr/commands/mitigation-actions.js +488 -0
- package/dist/modules/eudr/commands/mitigation-actions.js.map +7 -0
- package/dist/modules/eudr/commands/plots.js +517 -0
- package/dist/modules/eudr/commands/plots.js.map +7 -0
- package/dist/modules/eudr/commands/product-mappings.js +468 -0
- package/dist/modules/eudr/commands/product-mappings.js.map +7 -0
- package/dist/modules/eudr/commands/risk-assessments.js +599 -0
- package/dist/modules/eudr/commands/risk-assessments.js.map +7 -0
- package/dist/modules/eudr/commands/statements.js +747 -0
- package/dist/modules/eudr/commands/statements.js.map +7 -0
- package/dist/modules/eudr/components/CountrySelectField.js +64 -0
- package/dist/modules/eudr/components/CountrySelectField.js.map +7 -0
- package/dist/modules/eudr/components/GeometryInput.js +133 -0
- package/dist/modules/eudr/components/GeometryInput.js.map +7 -0
- package/dist/modules/eudr/components/MappingSuggestionsDialog.js +307 -0
- package/dist/modules/eudr/components/MappingSuggestionsDialog.js.map +7 -0
- package/dist/modules/eudr/components/MitigationActionsSection.js +345 -0
- package/dist/modules/eudr/components/MitigationActionsSection.js.map +7 -0
- package/dist/modules/eudr/components/OrderSelectField.js +88 -0
- package/dist/modules/eudr/components/OrderSelectField.js.map +7 -0
- package/dist/modules/eudr/components/PlotImportDialog.js +229 -0
- package/dist/modules/eudr/components/PlotImportDialog.js.map +7 -0
- package/dist/modules/eudr/components/PlotMapPreview.js +35 -0
- package/dist/modules/eudr/components/PlotMapPreview.js.map +7 -0
- package/dist/modules/eudr/components/PlotMapPreviewImpl.js +97 -0
- package/dist/modules/eudr/components/PlotMapPreviewImpl.js.map +7 -0
- package/dist/modules/eudr/components/PlotMultiSelectField.js +198 -0
- package/dist/modules/eudr/components/PlotMultiSelectField.js.map +7 -0
- package/dist/modules/eudr/components/RiskCriteriaField.js +128 -0
- package/dist/modules/eudr/components/RiskCriteriaField.js.map +7 -0
- package/dist/modules/eudr/components/StatementLifecycleBar.js +274 -0
- package/dist/modules/eudr/components/StatementLifecycleBar.js.map +7 -0
- package/dist/modules/eudr/components/StatementReadinessChecklist.js +56 -0
- package/dist/modules/eudr/components/StatementReadinessChecklist.js.map +7 -0
- package/dist/modules/eudr/components/StatementRiskSection.js +179 -0
- package/dist/modules/eudr/components/StatementRiskSection.js.map +7 -0
- package/dist/modules/eudr/components/crudErrorI18n.js +31 -0
- package/dist/modules/eudr/components/crudErrorI18n.js.map +7 -0
- package/dist/modules/eudr/components/formConfig.js +593 -0
- package/dist/modules/eudr/components/formConfig.js.map +7 -0
- package/dist/modules/eudr/components/plotForm.js +68 -0
- package/dist/modules/eudr/components/plotForm.js.map +7 -0
- package/dist/modules/eudr/data/enrichers.js +62 -0
- package/dist/modules/eudr/data/enrichers.js.map +7 -0
- package/dist/modules/eudr/data/entities.js +437 -0
- package/dist/modules/eudr/data/entities.js.map +7 -0
- package/dist/modules/eudr/data/validators.js +271 -0
- package/dist/modules/eudr/data/validators.js.map +7 -0
- package/dist/modules/eudr/encryption.js +33 -0
- package/dist/modules/eudr/encryption.js.map +7 -0
- package/dist/modules/eudr/events.js +38 -0
- package/dist/modules/eudr/events.js.map +7 -0
- package/dist/modules/eudr/index.js +15 -0
- package/dist/modules/eudr/index.js.map +7 -0
- package/dist/modules/eudr/lib/annual-report.js +104 -0
- package/dist/modules/eudr/lib/annual-report.js.map +7 -0
- package/dist/modules/eudr/lib/completeness.js +64 -0
- package/dist/modules/eudr/lib/completeness.js.map +7 -0
- package/dist/modules/eudr/lib/detail-read-scope.js +16 -0
- package/dist/modules/eudr/lib/detail-read-scope.js.map +7 -0
- package/dist/modules/eudr/lib/geometry.js +142 -0
- package/dist/modules/eudr/lib/geometry.js.map +7 -0
- package/dist/modules/eudr/lib/notifications.js +56 -0
- package/dist/modules/eudr/lib/notifications.js.map +7 -0
- package/dist/modules/eudr/lib/plural.js +26 -0
- package/dist/modules/eudr/lib/plural.js.map +7 -0
- package/dist/modules/eudr/lib/reference-data.js +240 -0
- package/dist/modules/eudr/lib/reference-data.js.map +7 -0
- package/dist/modules/eudr/lib/species.js +10 -0
- package/dist/modules/eudr/lib/species.js.map +7 -0
- package/dist/modules/eudr/lib/statement-lifecycle.js +84 -0
- package/dist/modules/eudr/lib/statement-lifecycle.js.map +7 -0
- package/dist/modules/eudr/lib/statement-seeding.js +49 -0
- package/dist/modules/eudr/lib/statement-seeding.js.map +7 -0
- package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js +15 -0
- package/dist/modules/eudr/migrations/Migration20260706102002_eudr.js.map +7 -0
- package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js +24 -0
- package/dist/modules/eudr/migrations/Migration20260706152612_eudr.js.map +7 -0
- package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js +15 -0
- package/dist/modules/eudr/migrations/Migration20260722100000_eudr.js.map +7 -0
- package/dist/modules/eudr/notifications.js +58 -0
- package/dist/modules/eudr/notifications.js.map +7 -0
- package/dist/modules/eudr/search.js +186 -0
- package/dist/modules/eudr/search.js.map +7 -0
- package/dist/modules/eudr/setup.js +12 -0
- package/dist/modules/eudr/setup.js.map +7 -0
- package/dist/modules/eudr/subscribers/notify-mitigation-completed.js +34 -0
- package/dist/modules/eudr/subscribers/notify-mitigation-completed.js.map +7 -0
- package/dist/modules/eudr/subscribers/notify-risk-concluded.js +35 -0
- package/dist/modules/eudr/subscribers/notify-risk-concluded.js.map +7 -0
- package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js +37 -0
- package/dist/modules/eudr/subscribers/notify-statement-reference-issued.js.map +7 -0
- package/dist/modules/eudr/subscribers/notify-statement-submitted.js +34 -0
- package/dist/modules/eudr/subscribers/notify-statement-submitted.js.map +7 -0
- package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js +34 -0
- package/dist/modules/eudr/subscribers/notify-statement-withdrawn.js.map +7 -0
- package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js +124 -0
- package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-created.js.map +7 -0
- package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js +22 -0
- package/dist/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.js.map +7 -0
- package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js +203 -0
- package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.client.js.map +7 -0
- package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js +23 -0
- package/dist/modules/eudr/widgets/dashboard/compliance-overview/widget.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js +19 -0
- package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.client.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js +31 -0
- package/dist/modules/eudr/widgets/injection/eudr-product-column/widget.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js +100 -0
- package/dist/modules/eudr/widgets/injection/order-compliance/widget.client.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/order-compliance/widget.js +17 -0
- package/dist/modules/eudr/widgets/injection/order-compliance/widget.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js +116 -0
- package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.client.js.map +7 -0
- package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js +17 -0
- package/dist/modules/eudr/widgets/injection/supplier-compliance/widget.js.map +7 -0
- package/dist/modules/eudr/widgets/injection-table.js +32 -0
- package/dist/modules/eudr/widgets/injection-table.js.map +7 -0
- package/dist/modules/notifications/lib/notificationService.js +67 -3
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +121 -48
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/sales/api/documents/factory.js +17 -0
- package/dist/modules/sales/api/documents/factory.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +32 -18
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/data/entities.js +1 -0
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js +17 -0
- package/dist/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.js.map +7 -0
- package/generated/entities/eudr_due_diligence_statement/index.ts +22 -0
- package/generated/entities/eudr_evidence_submission/index.ts +24 -0
- package/generated/entities/eudr_mitigation_action/index.ts +14 -0
- package/generated/entities/eudr_plot/index.ts +18 -0
- package/generated/entities/eudr_product_mapping/index.ts +14 -0
- package/generated/entities/eudr_risk_assessment/index.ts +16 -0
- package/generated/entities/role/index.ts +1 -0
- package/generated/entities.ids.generated.ts +9 -0
- package/generated/entity-fields-registry.ts +121 -0
- package/package.json +7 -7
- package/src/helpers/integration/api.ts +13 -0
- package/src/modules/attachments/fields/attachment.tsx +5 -0
- package/src/modules/attachments/lib/crud.ts +2 -0
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +7 -0
- package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +9 -0
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +4 -4
- package/src/modules/audit_logs/components/AuditLogsActions.tsx +20 -4
- package/src/modules/auth/api/login.ts +9 -2
- package/src/modules/auth/api/users/route.ts +8 -4
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +17 -1
- package/src/modules/auth/backend/users/page.tsx +11 -0
- package/src/modules/auth/commands/users.ts +251 -85
- package/src/modules/auth/data/entities.ts +3 -0
- package/src/modules/auth/i18n/de.json +6 -0
- package/src/modules/auth/i18n/en.json +6 -0
- package/src/modules/auth/i18n/es.json +6 -0
- package/src/modules/auth/i18n/ko.json +6 -0
- package/src/modules/auth/i18n/pl.json +6 -0
- package/src/modules/auth/lib/sessionIntegrity.ts +1 -1
- package/src/modules/auth/lib/setup-app.ts +1 -1
- package/src/modules/auth/migrations/.snapshot-open-mercato.json +16 -0
- package/src/modules/auth/migrations/Migration20260728134212_auth.ts +14 -0
- package/src/modules/eudr/acl.ts +39 -0
- package/src/modules/eudr/ai-agents.ts +258 -0
- package/src/modules/eudr/ai-tools/compliance-pack.ts +618 -0
- package/src/modules/eudr/ai-tools/types.ts +44 -0
- package/src/modules/eudr/ai-tools.ts +15 -0
- package/src/modules/eudr/api/dashboard/widgets/compliance-overview/route.ts +481 -0
- package/src/modules/eudr/api/evidence-submissions/route.ts +335 -0
- package/src/modules/eudr/api/mitigation-actions/route.ts +263 -0
- package/src/modules/eudr/api/openapi.ts +28 -0
- package/src/modules/eudr/api/plots/import/route.ts +369 -0
- package/src/modules/eudr/api/plots/route.ts +296 -0
- package/src/modules/eudr/api/product-mappings/route.ts +269 -0
- package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +382 -0
- package/src/modules/eudr/api/product-mappings/suggestions/route.ts +169 -0
- package/src/modules/eudr/api/reports/annual/route.ts +457 -0
- package/src/modules/eudr/api/risk-assessments/route.ts +323 -0
- package/src/modules/eudr/api/statements/[id]/export/route.ts +713 -0
- package/src/modules/eudr/api/statements/[id]/readiness/route.ts +129 -0
- package/src/modules/eudr/api/statements/route.ts +329 -0
- package/src/modules/eudr/api/suppliers/compliance/route.ts +241 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/[id]/page.tsx +545 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.meta.ts +13 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/create/page.tsx +383 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +379 -0
- package/src/modules/eudr/backend/eudr/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/page.tsx +477 -0
- package/src/modules/eudr/backend/eudr/plots/[id]/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/plots/[id]/page.tsx +404 -0
- package/src/modules/eudr/backend/eudr/plots/create/page.meta.ts +13 -0
- package/src/modules/eudr/backend/eudr/plots/create/page.tsx +267 -0
- package/src/modules/eudr/backend/eudr/plots/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/plots/page.tsx +442 -0
- package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/product-mappings/[id]/page.tsx +311 -0
- package/src/modules/eudr/backend/eudr/product-mappings/create/page.meta.ts +13 -0
- package/src/modules/eudr/backend/eudr/product-mappings/create/page.tsx +190 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +344 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/[id]/page.tsx +421 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/create/page.meta.ts +13 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/create/page.tsx +247 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +383 -0
- package/src/modules/eudr/backend/eudr/statements/[id]/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/statements/[id]/page.tsx +777 -0
- package/src/modules/eudr/backend/eudr/statements/create/page.meta.ts +13 -0
- package/src/modules/eudr/backend/eudr/statements/create/page.tsx +511 -0
- package/src/modules/eudr/backend/eudr/statements/page.meta.ts +12 -0
- package/src/modules/eudr/backend/eudr/statements/page.tsx +372 -0
- package/src/modules/eudr/ce.ts +52 -0
- package/src/modules/eudr/commands/evidence-submissions.ts +699 -0
- package/src/modules/eudr/commands/index.ts +6 -0
- package/src/modules/eudr/commands/lifecycle-events.ts +28 -0
- package/src/modules/eudr/commands/mitigation-actions.ts +574 -0
- package/src/modules/eudr/commands/plots.ts +627 -0
- package/src/modules/eudr/commands/product-mappings.ts +556 -0
- package/src/modules/eudr/commands/risk-assessments.ts +728 -0
- package/src/modules/eudr/commands/statements.ts +933 -0
- package/src/modules/eudr/components/CountrySelectField.tsx +77 -0
- package/src/modules/eudr/components/GeometryInput.tsx +170 -0
- package/src/modules/eudr/components/MappingSuggestionsDialog.tsx +385 -0
- package/src/modules/eudr/components/MitigationActionsSection.tsx +417 -0
- package/src/modules/eudr/components/OrderSelectField.tsx +116 -0
- package/src/modules/eudr/components/PlotImportDialog.tsx +277 -0
- package/src/modules/eudr/components/PlotMapPreview.tsx +46 -0
- package/src/modules/eudr/components/PlotMapPreviewImpl.tsx +111 -0
- package/src/modules/eudr/components/PlotMultiSelectField.tsx +238 -0
- package/src/modules/eudr/components/RiskCriteriaField.tsx +173 -0
- package/src/modules/eudr/components/StatementLifecycleBar.tsx +363 -0
- package/src/modules/eudr/components/StatementReadinessChecklist.tsx +82 -0
- package/src/modules/eudr/components/StatementRiskSection.tsx +242 -0
- package/src/modules/eudr/components/crudErrorI18n.ts +36 -0
- package/src/modules/eudr/components/formConfig.tsx +717 -0
- package/src/modules/eudr/components/plotForm.ts +75 -0
- package/src/modules/eudr/data/enrichers.ts +82 -0
- package/src/modules/eudr/data/entities.ts +369 -0
- package/src/modules/eudr/data/validators.ts +357 -0
- package/src/modules/eudr/encryption.ts +31 -0
- package/src/modules/eudr/events.ts +38 -0
- package/src/modules/eudr/i18n/de.json +747 -0
- package/src/modules/eudr/i18n/en.json +747 -0
- package/src/modules/eudr/i18n/es.json +747 -0
- package/src/modules/eudr/i18n/ko.json +747 -0
- package/src/modules/eudr/i18n/pl.json +747 -0
- package/src/modules/eudr/index.ts +13 -0
- package/src/modules/eudr/lib/annual-report.ts +120 -0
- package/src/modules/eudr/lib/completeness.ts +90 -0
- package/src/modules/eudr/lib/detail-read-scope.ts +44 -0
- package/src/modules/eudr/lib/geometry.ts +191 -0
- package/src/modules/eudr/lib/notifications.ts +85 -0
- package/src/modules/eudr/lib/plural.ts +36 -0
- package/src/modules/eudr/lib/reference-data.ts +128 -0
- package/src/modules/eudr/lib/species.ts +12 -0
- package/src/modules/eudr/lib/statement-lifecycle.ts +109 -0
- package/src/modules/eudr/lib/statement-seeding.ts +86 -0
- package/src/modules/eudr/migrations/.snapshot-open-mercato.json +2046 -0
- package/src/modules/eudr/migrations/Migration20260706102002_eudr.ts +16 -0
- package/src/modules/eudr/migrations/Migration20260706152612_eudr.ts +30 -0
- package/src/modules/eudr/migrations/Migration20260722100000_eudr.ts +17 -0
- package/src/modules/eudr/notifications.ts +56 -0
- package/src/modules/eudr/search.ts +212 -0
- package/src/modules/eudr/setup.ts +10 -0
- package/src/modules/eudr/subscribers/notify-mitigation-completed.ts +56 -0
- package/src/modules/eudr/subscribers/notify-risk-concluded.ts +58 -0
- package/src/modules/eudr/subscribers/notify-statement-reference-issued.ts +59 -0
- package/src/modules/eudr/subscribers/notify-statement-submitted.ts +49 -0
- package/src/modules/eudr/subscribers/notify-statement-withdrawn.ts +49 -0
- package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-created.ts +189 -0
- package/src/modules/eudr/subscribers/recompute-completeness-on-attachment-deleted.ts +31 -0
- package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.client.tsx +271 -0
- package/src/modules/eudr/widgets/dashboard/compliance-overview/widget.ts +24 -0
- package/src/modules/eudr/widgets/injection/eudr-product-column/widget.client.tsx +37 -0
- package/src/modules/eudr/widgets/injection/eudr-product-column/widget.ts +30 -0
- package/src/modules/eudr/widgets/injection/order-compliance/widget.client.tsx +131 -0
- package/src/modules/eudr/widgets/injection/order-compliance/widget.ts +16 -0
- package/src/modules/eudr/widgets/injection/supplier-compliance/widget.client.tsx +168 -0
- package/src/modules/eudr/widgets/injection/supplier-compliance/widget.ts +16 -0
- package/src/modules/eudr/widgets/injection-table.ts +31 -0
- package/src/modules/notifications/lib/notificationService.ts +83 -4
- package/src/modules/query_index/lib/engine.ts +139 -51
- package/src/modules/sales/api/documents/factory.ts +29 -0
- package/src/modules/sales/commands/documents.ts +65 -18
- package/src/modules/sales/data/entities.ts +1 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +10 -0
- package/src/modules/sales/migrations/Migration20260806120000_sales_tag_assignment_document_idx.ts +59 -0
|
@@ -47,6 +47,7 @@ type LoadedUser = {
|
|
|
47
47
|
roles: string[]
|
|
48
48
|
roleIds: string[]
|
|
49
49
|
hasPassword: boolean
|
|
50
|
+
isConfirmed: boolean
|
|
50
51
|
updatedAt: string | null
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -61,6 +62,7 @@ type UserApiItem = {
|
|
|
61
62
|
roles?: unknown
|
|
62
63
|
roleIds?: unknown
|
|
63
64
|
hasPassword?: boolean
|
|
65
|
+
isConfirmed?: boolean
|
|
64
66
|
updatedAt?: string | null
|
|
65
67
|
updated_at?: string | null
|
|
66
68
|
}
|
|
@@ -268,6 +270,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
|
|
|
268
270
|
roles: roleNames,
|
|
269
271
|
roleIds: roleIds.length > 0 ? roleIds : roleNames,
|
|
270
272
|
hasPassword: item.hasPassword !== false,
|
|
273
|
+
isConfirmed: item.isConfirmed !== false,
|
|
271
274
|
updatedAt: typeof item.updatedAt === 'string'
|
|
272
275
|
? item.updatedAt
|
|
273
276
|
: typeof item.updated_at === 'string'
|
|
@@ -399,11 +402,20 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
|
|
|
399
402
|
options: initialRoleOptions,
|
|
400
403
|
loadOptions: loadRoleOptions,
|
|
401
404
|
})
|
|
405
|
+
items.push({
|
|
406
|
+
id: 'isConfirmed',
|
|
407
|
+
label: t('auth.users.form.field.active', 'Active'),
|
|
408
|
+
type: 'checkbox',
|
|
409
|
+
description: t(
|
|
410
|
+
'auth.users.form.field.activeHint',
|
|
411
|
+
'Clearing this deactivates the account: the user can no longer sign in and every active session is revoked.',
|
|
412
|
+
),
|
|
413
|
+
})
|
|
402
414
|
return items
|
|
403
415
|
}, [actorIsSuperAdmin, initialRoleOptions, loadRoleOptions, passwordDescription, preloadedTenants, selectedOrgId, selectedTenantId, t, userHasPassword])
|
|
404
416
|
|
|
405
417
|
const detailFieldIds = React.useMemo(() => {
|
|
406
|
-
const base: string[] = ['email', 'name', 'password', 'organizationId', 'roles']
|
|
418
|
+
const base: string[] = ['email', 'name', 'password', 'organizationId', 'roles', 'isConfirmed']
|
|
407
419
|
if (actorIsSuperAdmin) base.splice(2, 0, 'tenantId')
|
|
408
420
|
return base
|
|
409
421
|
}, [actorIsSuperAdmin])
|
|
@@ -464,6 +476,7 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
|
|
|
464
476
|
tenantId: initialUser.tenantId,
|
|
465
477
|
organizationId: initialUser.organizationId,
|
|
466
478
|
roles: initialUser.roleIds,
|
|
479
|
+
isConfirmed: initialUser.isConfirmed,
|
|
467
480
|
updatedAt: initialUser.updatedAt,
|
|
468
481
|
...customFieldValues,
|
|
469
482
|
}
|
|
@@ -555,6 +568,9 @@ export default function EditUserPage({ params }: { params?: { id?: string } }) {
|
|
|
555
568
|
password: values.password && values.password.trim() ? values.password : undefined,
|
|
556
569
|
organizationId: values.organizationId ? values.organizationId : undefined,
|
|
557
570
|
roles: Array.isArray(values.roles) ? values.roles : [],
|
|
571
|
+
// Only sent when the checkbox actually resolved to a boolean — never
|
|
572
|
+
// default a missing value to `false`, which would silently deactivate.
|
|
573
|
+
...(typeof values.isConfirmed === 'boolean' ? { isConfirmed: values.isConfirmed } : {}),
|
|
558
574
|
...(Object.keys(customFields).length ? { customFields } : {}),
|
|
559
575
|
}
|
|
560
576
|
const userOptimisticLockHeader = buildOptimisticLockHeader(initialUser?.updatedAt)
|
|
@@ -9,6 +9,7 @@ import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
|
9
9
|
import type { SortingState } from '@tanstack/react-table'
|
|
10
10
|
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
11
11
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
12
|
+
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
12
13
|
import { RowActions } from '@open-mercato/ui/backend/RowActions'
|
|
13
14
|
import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
14
15
|
import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
|
|
@@ -30,6 +31,7 @@ type Row = {
|
|
|
30
31
|
tenantId: string | null
|
|
31
32
|
tenantName?: string | null
|
|
32
33
|
roles: string[]
|
|
34
|
+
isConfirmed?: boolean
|
|
33
35
|
updatedAt?: string | null
|
|
34
36
|
}
|
|
35
37
|
|
|
@@ -378,6 +380,15 @@ export default function UsersListPage() {
|
|
|
378
380
|
{ accessorKey: 'name', header: t('auth.users.list.columns.name', 'Display name') },
|
|
379
381
|
{ accessorKey: 'organizationName', header: 'Organization' },
|
|
380
382
|
{ accessorKey: 'roles', header: 'Roles', cell: ({ row }) => (row.original.roles || []).join(', ') },
|
|
383
|
+
{
|
|
384
|
+
accessorKey: 'isConfirmed',
|
|
385
|
+
header: t('auth.users.list.columns.status', 'Status'),
|
|
386
|
+
cell: ({ row }) => (
|
|
387
|
+
row.original.isConfirmed === false
|
|
388
|
+
? <Badge variant="neutral" dot>{t('auth.users.list.status.deactivated', 'Deactivated')}</Badge>
|
|
389
|
+
: <Badge variant="success" dot>{t('auth.users.list.status.active', 'Active')}</Badge>
|
|
390
|
+
),
|
|
391
|
+
},
|
|
381
392
|
]
|
|
382
393
|
if (showTenantColumn) {
|
|
383
394
|
base.splice(1, 0, { accessorKey: 'tenantName', header: 'Tenant' })
|
|
@@ -13,7 +13,7 @@ import type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/l
|
|
|
13
13
|
import type { DataEngine } from '@open-mercato/shared/lib/data/engine'
|
|
14
14
|
import type { CommandRuntimeContext } from '@open-mercato/shared/lib/commands'
|
|
15
15
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
16
|
-
import { UniqueConstraintViolationException } from '@mikro-orm/core'
|
|
16
|
+
import { UniqueConstraintViolationException, LockMode } from '@mikro-orm/core'
|
|
17
17
|
import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'
|
|
18
18
|
import { User, UserRole, Role, UserAcl, Session, PasswordReset } from '@open-mercato/core/modules/auth/data/entities'
|
|
19
19
|
import { Organization } from '@open-mercato/core/modules/directory/data/entities'
|
|
@@ -122,6 +122,7 @@ const updateSchema = z.object({
|
|
|
122
122
|
password: passwordSchema.optional(),
|
|
123
123
|
organizationId: z.string().uuid().optional(),
|
|
124
124
|
roles: z.array(z.string()).optional(),
|
|
125
|
+
isConfirmed: z.boolean().optional(),
|
|
125
126
|
})
|
|
126
127
|
|
|
127
128
|
export const userCrudEvents: CrudEventsConfig = {
|
|
@@ -329,9 +330,22 @@ const createUserCommand: CommandHandler<Record<string, unknown>, CreateUserResul
|
|
|
329
330
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
330
331
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
331
332
|
|
|
333
|
+
// Evaluate the floor against the user's CURRENT tenant, not the one recorded at
|
|
334
|
+
// creation. A user moved to another tenant after being created would otherwise be
|
|
335
|
+
// checked against the origin tenant — where they no longer hold anything — and the
|
|
336
|
+
// undo would hard-delete the destination tenant's last administrator unchecked.
|
|
337
|
+
// The create snapshot is only a fallback for a row that is already gone.
|
|
332
338
|
let removed: User | null = null
|
|
333
339
|
await withAtomicFlush(em, [
|
|
334
340
|
async () => {
|
|
341
|
+
const current = await findOneWithDecryption(em, User, { id: userId, deletedAt: null }, {}, { tenantId: null, organizationId: null })
|
|
342
|
+
const floorTenantId = current?.tenantId ? String(current.tenantId) : (snapshot?.tenantId ?? null)
|
|
343
|
+
|
|
344
|
+
// Undoing a create hard-deletes the user, so it can strip a tenant's last active
|
|
345
|
+
// admin exactly like `auth.users.delete` can — promote a second admin, delete the
|
|
346
|
+
// first, then undo the promotion's create. Same guard applies.
|
|
347
|
+
await enforceProtectedRoleFloor(em, floorTenantId, userId, { deleting: true }, ctx)
|
|
348
|
+
|
|
335
349
|
await em.nativeDelete(UserAcl, { user: userId })
|
|
336
350
|
await em.nativeDelete(UserRole, { user: userId })
|
|
337
351
|
await em.nativeDelete(Session, { user: userId })
|
|
@@ -357,7 +371,7 @@ const createUserCommand: CommandHandler<Record<string, unknown>, CreateUserResul
|
|
|
357
371
|
soft: false,
|
|
358
372
|
})
|
|
359
373
|
},
|
|
360
|
-
], { transaction: true })
|
|
374
|
+
], { transaction: true, label: 'auth.users.create.undo' })
|
|
361
375
|
|
|
362
376
|
await emitCrudUndoSideEffects({
|
|
363
377
|
dataEngine: de,
|
|
@@ -522,14 +536,17 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
522
536
|
async execute(rawInput, ctx) {
|
|
523
537
|
const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)
|
|
524
538
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
539
|
+
const actorTenantScope = resolveActorTenantScope(ctx)
|
|
540
|
+
const existing = await findOneWithDecryption(em, User, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })
|
|
541
|
+
if (!existing) throw new CrudHttpError(404, { error: 'User not found' })
|
|
542
|
+
if (actorTenantScope && existing.tenantId && String(existing.tenantId) !== actorTenantScope) {
|
|
543
|
+
throw new CrudHttpError(404, { error: 'User not found' })
|
|
544
|
+
}
|
|
545
|
+
|
|
525
546
|
const rolesBefore = Array.isArray(parsed.roles)
|
|
526
547
|
? await loadUserRoleNames(em, parsed.id)
|
|
527
548
|
: null
|
|
528
549
|
|
|
529
|
-
// Resolve the tenant the user will belong to after this update first, so the email
|
|
530
|
-
// duplicate check below can be scoped to it. Email is unique per-tenant, not globally
|
|
531
|
-
// (see Migration20260610120000: users_tenant_email_hash_uniq) — a matching email in another
|
|
532
|
-
// tenant must not block the update or leak cross-tenant account existence (#2934).
|
|
533
550
|
let tenantId: string | null | undefined
|
|
534
551
|
if (parsed.organizationId !== undefined) {
|
|
535
552
|
const organization = await findOneWithDecryption(
|
|
@@ -543,25 +560,11 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
543
560
|
tenantId = organization.tenant?.id ? String(organization.tenant.id) : null
|
|
544
561
|
}
|
|
545
562
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
: await resolveUserTenantId(em, parsed.id)
|
|
550
|
-
const duplicate = await findOneWithDecryption(
|
|
551
|
-
em,
|
|
552
|
-
User,
|
|
553
|
-
{
|
|
554
|
-
$or: [{ email: parsed.email }, { emailHash: { $in: emailHashLookupValues(parsed.email) } }],
|
|
555
|
-
deletedAt: null,
|
|
556
|
-
tenantId: targetTenantId,
|
|
557
|
-
id: { $ne: parsed.id } as any,
|
|
558
|
-
} as FilterQuery<User>,
|
|
559
|
-
{},
|
|
560
|
-
{ tenantId: null, organizationId: null },
|
|
561
|
-
)
|
|
562
|
-
if (duplicate) await throwDuplicateEmailError()
|
|
563
|
-
}
|
|
563
|
+
const userTenantId = existing.tenantId ? String(existing.tenantId) : null
|
|
564
|
+
const targetTenantId = tenantId !== undefined ? tenantId : userTenantId
|
|
565
|
+
const isTenantChanging = targetTenantId !== userTenantId
|
|
564
566
|
|
|
567
|
+
// Hash password BEFORE transaction begins to avoid holding locks during CPU-heavy tasks
|
|
565
568
|
let hashed: string | null = null
|
|
566
569
|
let emailHash: string | null = null
|
|
567
570
|
if (parsed.password) {
|
|
@@ -572,53 +575,87 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
572
575
|
emailHash = computeEmailHash(parsed.email)
|
|
573
576
|
}
|
|
574
577
|
|
|
575
|
-
const actorTenantScope = resolveActorTenantScope(ctx)
|
|
576
578
|
const updateWhere: Record<string, unknown> = { id: parsed.id, deletedAt: null }
|
|
577
579
|
if (actorTenantScope) updateWhere.tenantId = actorTenantScope
|
|
578
580
|
|
|
579
581
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
580
|
-
let user
|
|
581
|
-
try {
|
|
582
|
-
user = await de.updateOrmEntity({
|
|
583
|
-
entity: User,
|
|
584
|
-
where: updateWhere as FilterQuery<User>,
|
|
585
|
-
apply: (entity) => {
|
|
586
|
-
if (parsed.email !== undefined) {
|
|
587
|
-
entity.email = parsed.email
|
|
588
|
-
entity.emailHash = emailHash
|
|
589
|
-
}
|
|
590
|
-
if (parsed.name !== undefined) {
|
|
591
|
-
entity.name = parsed.name
|
|
592
|
-
}
|
|
593
|
-
if (parsed.organizationId !== undefined) {
|
|
594
|
-
entity.organizationId = parsed.organizationId
|
|
595
|
-
entity.tenantId = tenantId ?? null
|
|
596
|
-
}
|
|
597
|
-
if (hashed) entity.passwordHash = hashed
|
|
598
|
-
},
|
|
599
|
-
})
|
|
600
|
-
} catch (error) {
|
|
601
|
-
if (isUniqueViolation(error)) await throwDuplicateEmailError()
|
|
602
|
-
throw error
|
|
603
|
-
}
|
|
604
|
-
if (!user) throw new CrudHttpError(404, { error: 'User not found' })
|
|
582
|
+
let user!: User
|
|
605
583
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
584
|
+
await withAtomicFlush(em, [
|
|
585
|
+
async () => {
|
|
586
|
+
// Floor check must run inside the transaction so that LockMode.PESSIMISTIC_WRITE locks Role rows properly
|
|
587
|
+
await enforceProtectedRoleFloor(em, userTenantId, parsed.id, {
|
|
588
|
+
deactivating: parsed.isConfirmed === false || isTenantChanging,
|
|
589
|
+
newRoles: parsed.roles,
|
|
590
|
+
}, ctx)
|
|
591
|
+
|
|
592
|
+
// Email is unique per-tenant, not globally (see Migration20260610120000:
|
|
593
|
+
// users_tenant_email_hash_uniq) — a matching email in another tenant must not block
|
|
594
|
+
// the update or leak cross-tenant account existence (#2934). `targetTenantId` is the
|
|
595
|
+
// tenant the user will belong to after this update, so the check follows a move.
|
|
596
|
+
if (parsed.email !== undefined) {
|
|
597
|
+
const duplicate = await findOneWithDecryption(
|
|
598
|
+
em,
|
|
599
|
+
User,
|
|
600
|
+
{
|
|
601
|
+
$or: [{ email: parsed.email }, { emailHash: { $in: emailHashLookupValues(parsed.email) } }],
|
|
602
|
+
deletedAt: null,
|
|
603
|
+
tenantId: targetTenantId,
|
|
604
|
+
id: { $ne: parsed.id } as any,
|
|
605
|
+
} as FilterQuery<User>,
|
|
606
|
+
{},
|
|
607
|
+
{ tenantId: null, organizationId: null },
|
|
608
|
+
)
|
|
609
|
+
if (duplicate) await throwDuplicateEmailError()
|
|
610
|
+
}
|
|
609
611
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
612
|
+
try {
|
|
613
|
+
const updated = await de.updateOrmEntity({
|
|
614
|
+
entity: User,
|
|
615
|
+
where: updateWhere as FilterQuery<User>,
|
|
616
|
+
apply: (entity) => {
|
|
617
|
+
if (parsed.email !== undefined) {
|
|
618
|
+
entity.email = parsed.email
|
|
619
|
+
entity.emailHash = emailHash
|
|
620
|
+
}
|
|
621
|
+
if (parsed.name !== undefined) {
|
|
622
|
+
entity.name = parsed.name
|
|
623
|
+
}
|
|
624
|
+
if (parsed.organizationId !== undefined) {
|
|
625
|
+
entity.organizationId = parsed.organizationId
|
|
626
|
+
entity.tenantId = tenantId ?? null
|
|
627
|
+
}
|
|
628
|
+
if (parsed.isConfirmed !== undefined) {
|
|
629
|
+
entity.isConfirmed = parsed.isConfirmed
|
|
630
|
+
}
|
|
631
|
+
if (hashed) entity.passwordHash = hashed
|
|
632
|
+
},
|
|
633
|
+
})
|
|
634
|
+
if (updated) user = updated
|
|
635
|
+
} catch (error) {
|
|
636
|
+
if (isUniqueViolation(error)) await throwDuplicateEmailError()
|
|
637
|
+
throw error
|
|
638
|
+
}
|
|
639
|
+
if (!user) throw new CrudHttpError(404, { error: 'User not found' })
|
|
613
640
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
641
|
+
if (hashed || parsed.isConfirmed === false) {
|
|
642
|
+
await em.nativeDelete(Session, { user: parsed.id })
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (Array.isArray(parsed.roles)) {
|
|
646
|
+
await syncUserRoles(em, user, parsed.roles, user.tenantId ? String(user.tenantId) : tenantId ?? null)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
await setCustomFieldsIfAny({
|
|
650
|
+
dataEngine: de,
|
|
651
|
+
entityId: E.auth.user,
|
|
652
|
+
recordId: String(user.id),
|
|
653
|
+
organizationId: user.organizationId ? String(user.organizationId) : null,
|
|
654
|
+
tenantId: user.tenantId ? String(user.tenantId) : tenantId ?? null,
|
|
655
|
+
values: custom,
|
|
656
|
+
})
|
|
657
|
+
}
|
|
658
|
+
], { transaction: true })
|
|
622
659
|
|
|
623
660
|
const identifiers = {
|
|
624
661
|
id: String(user.id),
|
|
@@ -706,23 +743,42 @@ const updateUserCommand: CommandHandler<Record<string, unknown>, User> = {
|
|
|
706
743
|
const userId = before.id
|
|
707
744
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
708
745
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
709
|
-
const updated = await de.updateOrmEntity({
|
|
710
|
-
entity: User,
|
|
711
|
-
where: { id: userId, deletedAt: null } as FilterQuery<User>,
|
|
712
|
-
apply: (entity) => {
|
|
713
|
-
entity.email = before.email
|
|
714
|
-
entity.organizationId = before.organizationId ?? null
|
|
715
|
-
entity.tenantId = before.tenantId ?? null
|
|
716
|
-
entity.passwordHash = before.passwordHash ?? null
|
|
717
|
-
entity.name = before.name ?? null
|
|
718
|
-
entity.isConfirmed = before.isConfirmed
|
|
719
|
-
},
|
|
720
|
-
})
|
|
721
746
|
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
747
|
+
// Reverting an update can drop the tenant below a protected role's floor just as the
|
|
748
|
+
// forward path can — restoring an empty `before.roles` on what is now the last admin,
|
|
749
|
+
// or restoring `isConfirmed: false`. Undo is reachable from the audit-log UI, so the
|
|
750
|
+
// guard has to run here too, inside a transaction so the row lock is valid.
|
|
751
|
+
const restoredTenantId = before.tenantId ? String(before.tenantId) : null
|
|
752
|
+
|
|
753
|
+
let updated: User | null = null
|
|
754
|
+
await withAtomicFlush(em, [
|
|
755
|
+
async () => {
|
|
756
|
+
const current = await findOneWithDecryption(em, User, { id: userId, deletedAt: null }, {}, { tenantId: null, organizationId: null })
|
|
757
|
+
const currentTenantId = current?.tenantId ? String(current.tenantId) : null
|
|
758
|
+
|
|
759
|
+
await enforceProtectedRoleFloor(em, currentTenantId, userId, {
|
|
760
|
+
deactivating: before.isConfirmed === false || restoredTenantId !== currentTenantId,
|
|
761
|
+
newRoles: before.roles,
|
|
762
|
+
}, ctx)
|
|
763
|
+
|
|
764
|
+
updated = await de.updateOrmEntity({
|
|
765
|
+
entity: User,
|
|
766
|
+
where: { id: userId, deletedAt: null } as FilterQuery<User>,
|
|
767
|
+
apply: (entity) => {
|
|
768
|
+
entity.email = before.email
|
|
769
|
+
entity.organizationId = before.organizationId ?? null
|
|
770
|
+
entity.tenantId = before.tenantId ?? null
|
|
771
|
+
entity.passwordHash = before.passwordHash ?? null
|
|
772
|
+
entity.name = before.name ?? null
|
|
773
|
+
entity.isConfirmed = before.isConfirmed
|
|
774
|
+
},
|
|
775
|
+
})
|
|
776
|
+
|
|
777
|
+
if (updated) {
|
|
778
|
+
await syncUserRoles(em, updated, before.roles, before.tenantId)
|
|
779
|
+
}
|
|
780
|
+
},
|
|
781
|
+
], { transaction: true, label: 'auth.users.update.undo' })
|
|
726
782
|
|
|
727
783
|
const reset = buildCustomFieldResetMap(before.custom, after?.custom)
|
|
728
784
|
if (Object.keys(reset).length) {
|
|
@@ -781,12 +837,22 @@ const deleteUserCommand: CommandHandler<{ body?: Record<string, unknown>; query?
|
|
|
781
837
|
const em = (ctx.container.resolve('em') as EntityManager)
|
|
782
838
|
const de = (ctx.container.resolve('dataEngine') as DataEngine)
|
|
783
839
|
const actorTenantScope = resolveActorTenantScope(ctx)
|
|
840
|
+
|
|
841
|
+
const existing = await findOneWithDecryption(em, User, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })
|
|
842
|
+
if (!existing) throw new CrudHttpError(404, { error: 'User not found' })
|
|
843
|
+
if (actorTenantScope && existing.tenantId && String(existing.tenantId) !== actorTenantScope) {
|
|
844
|
+
throw new CrudHttpError(404, { error: 'User not found' })
|
|
845
|
+
}
|
|
846
|
+
|
|
784
847
|
const deleteWhere: Record<string, unknown> = { id, deletedAt: null }
|
|
785
848
|
if (actorTenantScope) deleteWhere.tenantId = actorTenantScope
|
|
786
849
|
|
|
787
850
|
let user!: User
|
|
788
851
|
await withAtomicFlush(em, [
|
|
789
852
|
async () => {
|
|
853
|
+
const userTenantId = existing.tenantId ? String(existing.tenantId) : null
|
|
854
|
+
await enforceProtectedRoleFloor(em, userTenantId, id, { deleting: true }, ctx)
|
|
855
|
+
|
|
790
856
|
await em.nativeDelete(UserAcl, { user: id })
|
|
791
857
|
await em.nativeDelete(UserRole, { user: id })
|
|
792
858
|
await em.nativeDelete(Session, { user: id })
|
|
@@ -1085,11 +1151,6 @@ function arrayEquals(left: string[] | undefined, right: string[]): boolean {
|
|
|
1085
1151
|
return left.every((value, idx) => value === right[idx])
|
|
1086
1152
|
}
|
|
1087
1153
|
|
|
1088
|
-
async function resolveUserTenantId(em: EntityManager, id: string): Promise<string | null> {
|
|
1089
|
-
const existing = await findOneWithDecryption(em, User, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })
|
|
1090
|
-
return existing?.tenantId ? String(existing.tenantId) : null
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
1154
|
async function throwDuplicateEmailError(): Promise<never> {
|
|
1094
1155
|
const { translate } = await resolveTranslations()
|
|
1095
1156
|
const message = translate('auth.users.errors.emailExists', 'Email already in use')
|
|
@@ -1099,3 +1160,108 @@ async function throwDuplicateEmailError(): Promise<never> {
|
|
|
1099
1160
|
details: [{ path: ['email'], message, code: 'duplicate', origin: 'validation' }],
|
|
1100
1161
|
})
|
|
1101
1162
|
}
|
|
1163
|
+
|
|
1164
|
+
type ProtectedRoleFloorOptions = {
|
|
1165
|
+
deactivating?: boolean
|
|
1166
|
+
newRoles?: string[]
|
|
1167
|
+
deleting?: boolean
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
/**
|
|
1171
|
+
* True when the requested mutation can lower a role's active holder count. Guards the
|
|
1172
|
+
* floor check so that ordinary edits (display name, password, email) never take the
|
|
1173
|
+
* tenant-wide role lock — `PUT /api/auth/profile` routes every self-service password
|
|
1174
|
+
* change through `auth.users.update`, so an unconditional lock would serialize them.
|
|
1175
|
+
*/
|
|
1176
|
+
function couldReduceActiveHolders(options: ProtectedRoleFloorOptions): boolean {
|
|
1177
|
+
return options.deleting === true || options.deactivating === true || options.newRoles !== undefined
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
async function enforceProtectedRoleFloor(
|
|
1181
|
+
em: EntityManager,
|
|
1182
|
+
tenantId: string | null,
|
|
1183
|
+
userId: string,
|
|
1184
|
+
options: ProtectedRoleFloorOptions,
|
|
1185
|
+
ctx?: CommandRuntimeContext,
|
|
1186
|
+
): Promise<void> {
|
|
1187
|
+
// Internal automation (CLI, migrations, tenant teardown) must never be blocked by the
|
|
1188
|
+
// floor. Superadmins are deliberately NOT exempt — see the spec's Risks section.
|
|
1189
|
+
if (ctx?.systemActor === true) return
|
|
1190
|
+
if (!couldReduceActiveHolders(options)) return
|
|
1191
|
+
|
|
1192
|
+
const normalizedTenantId = normalizeTenantId(tenantId) ?? null
|
|
1193
|
+
if (!normalizedTenantId) return
|
|
1194
|
+
|
|
1195
|
+
// Find all protected roles in this tenant, acquiring a pessimistic write lock in a deterministic primary key order
|
|
1196
|
+
const protectedRoles = await findWithDecryption(em, Role, {
|
|
1197
|
+
tenantId: normalizedTenantId,
|
|
1198
|
+
minActiveHolders: { $gt: 0 },
|
|
1199
|
+
deletedAt: null
|
|
1200
|
+
}, {
|
|
1201
|
+
lockMode: LockMode.PESSIMISTIC_WRITE,
|
|
1202
|
+
orderBy: { id: 'ASC' }
|
|
1203
|
+
}, { tenantId: normalizedTenantId, organizationId: null })
|
|
1204
|
+
|
|
1205
|
+
if (protectedRoles.length === 0) return
|
|
1206
|
+
|
|
1207
|
+
const { translate } = await resolveTranslations()
|
|
1208
|
+
|
|
1209
|
+
for (const role of protectedRoles) {
|
|
1210
|
+
const minFloor = role.minActiveHolders ?? 0
|
|
1211
|
+
if (minFloor <= 0) continue
|
|
1212
|
+
|
|
1213
|
+
// Active links for this role, scoped to the tenant by the nested user filter so the
|
|
1214
|
+
// database — not a post-filter — enforces isolation. Deliberately NOT populated:
|
|
1215
|
+
// `findWithDecryption` runs `decryptEntityGraph` over loaded relations, which would
|
|
1216
|
+
// decrypt every admin's email and name on each check just to read their ids.
|
|
1217
|
+
const activeLinks = await findWithDecryption(em, UserRole, {
|
|
1218
|
+
role: role.id,
|
|
1219
|
+
deletedAt: null,
|
|
1220
|
+
user: {
|
|
1221
|
+
deletedAt: null,
|
|
1222
|
+
isConfirmed: true,
|
|
1223
|
+
tenantId: normalizedTenantId
|
|
1224
|
+
}
|
|
1225
|
+
}, {}, { tenantId: null, organizationId: null })
|
|
1226
|
+
|
|
1227
|
+
// Count distinct user IDs in the tenant holding this role to prevent overcounting due to duplicate links
|
|
1228
|
+
const activeUserIds = Array.from(
|
|
1229
|
+
new Set(
|
|
1230
|
+
activeLinks
|
|
1231
|
+
.map((link) => {
|
|
1232
|
+
const userRef = link.user as unknown as { id?: string } | string | null | undefined
|
|
1233
|
+
const linkedUserId = typeof userRef === 'string' ? userRef : userRef?.id
|
|
1234
|
+
return linkedUserId ? String(linkedUserId) : null
|
|
1235
|
+
})
|
|
1236
|
+
.filter((id): id is string => !!id)
|
|
1237
|
+
)
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
// Is the target user currently one of the active holders?
|
|
1241
|
+
if (activeUserIds.includes(userId)) {
|
|
1242
|
+
// Determine if they will remain an active holder
|
|
1243
|
+
let willStillBeActiveHolder = true
|
|
1244
|
+
|
|
1245
|
+
if (options.deleting || options.deactivating) {
|
|
1246
|
+
willStillBeActiveHolder = false
|
|
1247
|
+
} else if (options.newRoles !== undefined) {
|
|
1248
|
+
// Checking role list
|
|
1249
|
+
const desiredUnique = Array.from(new Set(options.newRoles.map((r) => r.trim().toLowerCase()).filter(Boolean)))
|
|
1250
|
+
const roleNameLower = role.name.toLowerCase()
|
|
1251
|
+
const hasRoleByName = desiredUnique.includes(roleNameLower) || desiredUnique.includes(String(role.id).toLowerCase())
|
|
1252
|
+
if (!hasRoleByName) {
|
|
1253
|
+
willStillBeActiveHolder = false
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
if (!willStillBeActiveHolder) {
|
|
1258
|
+
const remaining = activeUserIds.length - 1
|
|
1259
|
+
if (remaining < minFloor) {
|
|
1260
|
+
throw new CrudHttpError(400, {
|
|
1261
|
+
error: translate('auth.users.errors.lastHolderOfCriticalRole', 'Cannot remove the last active holder of role "{roleName}"', { roleName: role.name })
|
|
1262
|
+
})
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
@@ -62,6 +62,9 @@ export class Role {
|
|
|
62
62
|
@Property({ name: 'tenant_id', type: 'uuid' })
|
|
63
63
|
tenantId!: string
|
|
64
64
|
|
|
65
|
+
@Property({ name: 'min_active_holders', type: 'integer', default: 0 })
|
|
66
|
+
minActiveHolders: number = 0
|
|
67
|
+
|
|
65
68
|
@Property({ name: 'created_at', type: Date, onCreate: () => new Date() })
|
|
66
69
|
createdAt: Date = new Date()
|
|
67
70
|
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"auth.users.consents.loading": "Einwilligungen werden geladen...",
|
|
163
163
|
"auth.users.consents.withdrawn": "Widerrufen",
|
|
164
164
|
"auth.users.errors.emailExists": "E-Mail-Adresse wird bereits verwendet",
|
|
165
|
+
"auth.users.errors.lastHolderOfCriticalRole": "Der letzte aktive Inhaber der Rolle \"{roleName}\" kann nicht entfernt werden",
|
|
165
166
|
"auth.users.flash.created": "Benutzer erstellt",
|
|
166
167
|
"auth.users.flash.createdEmailFailed": "Benutzer erstellt, aber die Einladungs-E-Mail konnte nicht gesendet werden. Sie können sie über die Benutzerseite erneut senden.",
|
|
167
168
|
"auth.users.flash.createdWithInvite": "Benutzer erstellt und Einladung gesendet",
|
|
@@ -182,6 +183,8 @@
|
|
|
182
183
|
"auth.users.form.errors.noId": "Keine Benutzer-ID angegeben",
|
|
183
184
|
"auth.users.form.errors.notFound": "Benutzer nicht gefunden",
|
|
184
185
|
"auth.users.form.errors.widgetsAssign": "Dashboard-Widgets konnten dem neuen Benutzer nicht zugewiesen werden",
|
|
186
|
+
"auth.users.form.field.active": "Aktiv",
|
|
187
|
+
"auth.users.form.field.activeHint": "Das Entfernen des Hakens deaktiviert das Konto: Die Anmeldung ist nicht mehr möglich und alle aktiven Sitzungen werden beendet.",
|
|
185
188
|
"auth.users.form.field.email": "E-Mail",
|
|
186
189
|
"auth.users.form.field.name": "Anzeigename",
|
|
187
190
|
"auth.users.form.field.newPassword": "Neues Passwort",
|
|
@@ -203,10 +206,13 @@
|
|
|
203
206
|
"auth.users.form.title.create": "Benutzer erstellen",
|
|
204
207
|
"auth.users.form.title.edit": "Benutzer bearbeiten",
|
|
205
208
|
"auth.users.list.columns.name": "Anzeigename",
|
|
209
|
+
"auth.users.list.columns.status": "Status",
|
|
206
210
|
"auth.users.list.error.delete": "Benutzer konnte nicht gelöscht werden",
|
|
207
211
|
"auth.users.list.error.load": "Benutzer konnten nicht geladen werden",
|
|
208
212
|
"auth.users.list.filters.name": "Anzeigename",
|
|
209
213
|
"auth.users.list.filters.namePlaceholder": "Nach Anzeigename filtern",
|
|
214
|
+
"auth.users.list.status.active": "Aktiv",
|
|
215
|
+
"auth.users.list.status.deactivated": "Deaktiviert",
|
|
210
216
|
"auth.users.widgets.errors.load": "Dashboard-Widgets können nicht geladen werden. Sie können sie später auf der Benutzerseite konfigurieren.",
|
|
211
217
|
"auth.users.widgets.loading": "Widgets werden geladen…",
|
|
212
218
|
"auth.users.widgets.mode.hint": "Neue Benutzer erben Widgets von ihren zugewiesenen Rollen. Überschreiben Sie die Auswahl, um eine eigene Zusammenstellung festzulegen.",
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"auth.users.consents.loading": "Loading consents...",
|
|
163
163
|
"auth.users.consents.withdrawn": "Withdrawn",
|
|
164
164
|
"auth.users.errors.emailExists": "Email already in use",
|
|
165
|
+
"auth.users.errors.lastHolderOfCriticalRole": "Cannot remove the last active holder of role \"{roleName}\"",
|
|
165
166
|
"auth.users.flash.created": "User created",
|
|
166
167
|
"auth.users.flash.createdEmailFailed": "User created but invitation email could not be sent. You can resend it from the user page.",
|
|
167
168
|
"auth.users.flash.createdWithInvite": "User created and invitation sent",
|
|
@@ -182,6 +183,8 @@
|
|
|
182
183
|
"auth.users.form.errors.noId": "No user ID provided",
|
|
183
184
|
"auth.users.form.errors.notFound": "User not found",
|
|
184
185
|
"auth.users.form.errors.widgetsAssign": "Failed to assign dashboard widgets to the new user",
|
|
186
|
+
"auth.users.form.field.active": "Active",
|
|
187
|
+
"auth.users.form.field.activeHint": "Clearing this deactivates the account: the user can no longer sign in and every active session is revoked.",
|
|
185
188
|
"auth.users.form.field.email": "Email",
|
|
186
189
|
"auth.users.form.field.name": "Display name",
|
|
187
190
|
"auth.users.form.field.newPassword": "New Password",
|
|
@@ -203,10 +206,13 @@
|
|
|
203
206
|
"auth.users.form.title.create": "Create User",
|
|
204
207
|
"auth.users.form.title.edit": "Edit User",
|
|
205
208
|
"auth.users.list.columns.name": "Display name",
|
|
209
|
+
"auth.users.list.columns.status": "Status",
|
|
206
210
|
"auth.users.list.error.delete": "Failed to delete user",
|
|
207
211
|
"auth.users.list.error.load": "Failed to load users",
|
|
208
212
|
"auth.users.list.filters.name": "Display name",
|
|
209
213
|
"auth.users.list.filters.namePlaceholder": "Filter by display name",
|
|
214
|
+
"auth.users.list.status.active": "Active",
|
|
215
|
+
"auth.users.list.status.deactivated": "Deactivated",
|
|
210
216
|
"auth.users.widgets.errors.load": "Unable to load dashboard widgets. You can configure them later from the user page.",
|
|
211
217
|
"auth.users.widgets.loading": "Loading widgets…",
|
|
212
218
|
"auth.users.widgets.mode.hint": "New users inherit widgets from their assigned roles. Override to pick a custom set.",
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"auth.users.consents.loading": "Cargando consentimientos...",
|
|
163
163
|
"auth.users.consents.withdrawn": "Retirado",
|
|
164
164
|
"auth.users.errors.emailExists": "El correo ya está en uso",
|
|
165
|
+
"auth.users.errors.lastHolderOfCriticalRole": "No se puede eliminar al último titular activo del rol \"{roleName}\"",
|
|
165
166
|
"auth.users.flash.created": "Usuario creado",
|
|
166
167
|
"auth.users.flash.createdEmailFailed": "Usuario creado pero no se pudo enviar el correo de invitación. Puede reenviarlo desde la página del usuario.",
|
|
167
168
|
"auth.users.flash.createdWithInvite": "Usuario creado e invitación enviada",
|
|
@@ -182,6 +183,8 @@
|
|
|
182
183
|
"auth.users.form.errors.noId": "No se proporcionó ID de usuario",
|
|
183
184
|
"auth.users.form.errors.notFound": "Usuario no encontrado",
|
|
184
185
|
"auth.users.form.errors.widgetsAssign": "No se pudieron asignar los widgets del panel al nuevo usuario",
|
|
186
|
+
"auth.users.form.field.active": "Activo",
|
|
187
|
+
"auth.users.form.field.activeHint": "Al desmarcarlo se desactiva la cuenta: el usuario ya no puede iniciar sesión y se revocan todas las sesiones activas.",
|
|
185
188
|
"auth.users.form.field.email": "Correo electrónico",
|
|
186
189
|
"auth.users.form.field.name": "Nombre visible",
|
|
187
190
|
"auth.users.form.field.newPassword": "Nueva contraseña",
|
|
@@ -203,10 +206,13 @@
|
|
|
203
206
|
"auth.users.form.title.create": "Crear usuario",
|
|
204
207
|
"auth.users.form.title.edit": "Editar usuario",
|
|
205
208
|
"auth.users.list.columns.name": "Nombre visible",
|
|
209
|
+
"auth.users.list.columns.status": "Estado",
|
|
206
210
|
"auth.users.list.error.delete": "No se pudo eliminar el usuario",
|
|
207
211
|
"auth.users.list.error.load": "No se pudieron cargar los usuarios",
|
|
208
212
|
"auth.users.list.filters.name": "Nombre visible",
|
|
209
213
|
"auth.users.list.filters.namePlaceholder": "Filtrar por nombre visible",
|
|
214
|
+
"auth.users.list.status.active": "Activo",
|
|
215
|
+
"auth.users.list.status.deactivated": "Desactivado",
|
|
210
216
|
"auth.users.widgets.errors.load": "No se pudieron cargar los widgets del panel. Puedes configurarlos más tarde desde la página del usuario.",
|
|
211
217
|
"auth.users.widgets.loading": "Cargando widgets…",
|
|
212
218
|
"auth.users.widgets.mode.hint": "Los nuevos usuarios heredan los widgets de las funciones asignadas. Sobrescribe para elegir un conjunto personalizado.",
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
"auth.users.consents.loading": "동의 정보를 불러오는 중...",
|
|
163
163
|
"auth.users.consents.withdrawn": "철회함",
|
|
164
164
|
"auth.users.errors.emailExists": "이미 사용 중인 이메일입니다",
|
|
165
|
+
"auth.users.errors.lastHolderOfCriticalRole": "\"{roleName}\" 역할의 마지막 활성 보유자는 제거할 수 없습니다",
|
|
165
166
|
"auth.users.flash.created": "사용자가 생성되었습니다",
|
|
166
167
|
"auth.users.flash.createdEmailFailed": "사용자가 생성되었지만 초대 이메일을 보낼 수 없었습니다. 사용자 페이지에서 다시 보낼 수 있습니다.",
|
|
167
168
|
"auth.users.flash.createdWithInvite": "사용자가 생성되고 초대가 전송되었습니다",
|
|
@@ -182,6 +183,8 @@
|
|
|
182
183
|
"auth.users.form.errors.noId": "제공된 사용자 ID가 없습니다",
|
|
183
184
|
"auth.users.form.errors.notFound": "사용자를 찾을 수 없습니다",
|
|
184
185
|
"auth.users.form.errors.widgetsAssign": "새 사용자에게 대시보드 위젯을 할당하지 못했습니다",
|
|
186
|
+
"auth.users.form.field.active": "활성",
|
|
187
|
+
"auth.users.form.field.activeHint": "선택을 해제하면 계정이 비활성화됩니다. 사용자는 로그인할 수 없으며 활성 세션이 모두 취소됩니다.",
|
|
185
188
|
"auth.users.form.field.email": "이메일",
|
|
186
189
|
"auth.users.form.field.name": "표시 이름",
|
|
187
190
|
"auth.users.form.field.newPassword": "새 비밀번호",
|
|
@@ -203,10 +206,13 @@
|
|
|
203
206
|
"auth.users.form.title.create": "사용자 생성",
|
|
204
207
|
"auth.users.form.title.edit": "사용자 편집",
|
|
205
208
|
"auth.users.list.columns.name": "표시 이름",
|
|
209
|
+
"auth.users.list.columns.status": "상태",
|
|
206
210
|
"auth.users.list.error.delete": "사용자 삭제에 실패했습니다",
|
|
207
211
|
"auth.users.list.error.load": "사용자를 불러오지 못했습니다",
|
|
208
212
|
"auth.users.list.filters.name": "표시 이름",
|
|
209
213
|
"auth.users.list.filters.namePlaceholder": "표시 이름으로 필터링",
|
|
214
|
+
"auth.users.list.status.active": "활성",
|
|
215
|
+
"auth.users.list.status.deactivated": "비활성",
|
|
210
216
|
"auth.users.widgets.errors.load": "대시보드 위젯을 불러올 수 없습니다. 나중에 사용자 페이지에서 구성할 수 있습니다.",
|
|
211
217
|
"auth.users.widgets.loading": "위젯을 불러오는 중…",
|
|
212
218
|
"auth.users.widgets.mode.hint": "새 사용자는 할당된 역할에서 위젯을 상속받습니다. 커스텀 세트를 선택하려면 재정의하세요.",
|