@antelopejs/dms 0.4.4 → 0.4.6
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/CHANGELOG.md +43 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js.map +1 -1
- package/dist/hooks/invite-extensions.js +2 -2
- package/dist/hooks/invite-extensions.js.map +1 -1
- package/dist/implementations/dms/layout-banners.d.ts +18 -0
- package/dist/implementations/dms/layout-banners.js +60 -0
- package/dist/implementations/dms/layout-banners.js.map +1 -0
- package/dist/implementations/dms/page.d.ts +3 -0
- package/dist/implementations/dms/page.js +90 -28
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/quick-actions.d.ts +5 -0
- package/dist/implementations/dms/quick-actions.js.map +1 -1
- package/dist/implementations/dms/resolver-timeout.d.ts +1 -0
- package/dist/implementations/dms/resolver-timeout.js +13 -0
- package/dist/implementations/dms/resolver-timeout.js.map +1 -0
- package/dist/implementations/dms/saas-mode.d.ts +8 -0
- package/dist/implementations/dms/saas-mode.js +20 -0
- package/dist/implementations/dms/saas-mode.js.map +1 -0
- package/dist/implementations/dms/stale-session.d.ts +2 -0
- package/dist/implementations/dms/stale-session.js +16 -0
- package/dist/implementations/dms/stale-session.js.map +1 -0
- package/dist/implementations/dms-auth/index.d.ts +3 -1
- package/dist/implementations/dms-auth/index.js +38 -8
- package/dist/implementations/dms-auth/index.js.map +1 -1
- package/dist/implementations/dms-html-render/service-jwt.js +2 -1
- package/dist/implementations/dms-html-render/service-jwt.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/pages/settings/users/index.d.ts +1 -0
- package/dist/pages/settings/users/index.js +1 -0
- package/dist/pages/settings/users/index.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.d.ts +4 -0
- package/dist/pages/settings/users/invite-extension-routes.js +57 -0
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -0
- package/dist/pages/settings/users/invites.d.ts +2 -1
- package/dist/pages/settings/users/invites.js +17 -5
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/members.d.ts +4 -1
- package/dist/pages/settings/users/members.js +10 -6
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.js +1 -1
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/quick-actions.d.ts +1 -0
- package/dist/pages/settings/users/quick-actions.js +22 -0
- package/dist/pages/settings/users/quick-actions.js.map +1 -0
- package/dist/routes/auth/client-ip.d.ts +7 -0
- package/dist/routes/auth/client-ip.js +42 -0
- package/dist/routes/auth/client-ip.js.map +1 -0
- package/dist/routes/auth/index.d.ts +1 -0
- package/dist/routes/auth/index.js +1 -0
- package/dist/routes/auth/index.js.map +1 -1
- package/dist/routes/auth/signup.d.ts +2 -0
- package/dist/routes/auth/signup.js +7 -2
- package/dist/routes/auth/signup.js.map +1 -1
- package/dist/routes/auth.d.ts +3 -0
- package/dist/routes/auth.js +15 -4
- package/dist/routes/auth.js.map +1 -1
- package/dist/test/integration/auth/client-ip.test.d.ts +1 -0
- package/dist/test/integration/auth/client-ip.test.js +14 -0
- package/dist/test/integration/auth/client-ip.test.js.map +1 -0
- package/dist/test/integration/invite-extension-edit.test.d.ts +1 -0
- package/dist/test/integration/invite-extension-edit.test.js +122 -0
- package/dist/test/integration/invite-extension-edit.test.js.map +1 -0
- package/dist/test/integration/page-session.test.d.ts +1 -0
- package/dist/test/integration/page-session.test.js +52 -0
- package/dist/test/integration/page-session.test.js.map +1 -0
- package/dist/test/integration/saas-mode-ownership.test.d.ts +1 -0
- package/dist/test/integration/saas-mode-ownership.test.js +81 -0
- package/dist/test/integration/saas-mode-ownership.test.js.map +1 -0
- package/dist/test/integration/tenant-lifecycle.test.js +38 -0
- package/dist/test/integration/tenant-lifecycle.test.js.map +1 -1
- package/dist/test/unit/client-ip.test.d.ts +1 -0
- package/dist/test/unit/client-ip.test.js +77 -0
- package/dist/test/unit/client-ip.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/layout-banners.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/layout-banners.test.js +217 -0
- package/dist/test/unit/implementations/dms/layout-banners.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/quick-actions-button.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/quick-actions-button.test.js +220 -0
- package/dist/test/unit/implementations/dms/quick-actions-button.test.js.map +1 -0
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +1 -1
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/service-jwt.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms-html-render/service-jwt.test.js +37 -0
- package/dist/test/unit/implementations/dms-html-render/service-jwt.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/invite-extension.test.js +138 -12
- package/dist/test/unit/interfaces/dms/invite-extension.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/saas-mode.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms/saas-mode.test.js +42 -0
- package/dist/test/unit/interfaces/dms/saas-mode.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/tenant-invite-email.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms/tenant-invite-email.test.js +53 -0
- package/dist/test/unit/interfaces/dms/tenant-invite-email.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-form-slots.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-form-slots.test.js +80 -0
- package/dist/test/unit/interfaces/dms-base/table-view-form-slots.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-tab-count-warning.test.d.ts +1 -0
- package/dist/test/unit/interfaces/dms-base/table-view-tab-count-warning.test.js +96 -0
- package/dist/test/unit/interfaces/dms-base/table-view-tab-count-warning.test.js.map +1 -0
- package/dist/test/unit/pages/settings/invite-member-quick-action.test.d.ts +1 -0
- package/dist/test/unit/pages/settings/invite-member-quick-action.test.js +104 -0
- package/dist/test/unit/pages/settings/invite-member-quick-action.test.js.map +1 -0
- package/dist/test/unit/pages/settings/notifications-access-gate.test.d.ts +1 -0
- package/dist/test/unit/pages/settings/notifications-access-gate.test.js +78 -0
- package/dist/test/unit/pages/settings/notifications-access-gate.test.js.map +1 -0
- package/dist/test/unit/signup-account-guard.test.js +20 -2
- package/dist/test/unit/signup-account-guard.test.js.map +1 -1
- package/dist/test/unit/utils/admin-invite-email.test.d.ts +1 -0
- package/dist/test/unit/utils/admin-invite-email.test.js +69 -0
- package/dist/test/unit/utils/admin-invite-email.test.js.map +1 -0
- package/dist/utils/admin-invite-email.d.ts +14 -0
- package/dist/utils/admin-invite-email.js +51 -0
- package/dist/utils/admin-invite-email.js.map +1 -0
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/validation/auth.schema.d.ts +3 -3
- package/dist/validation/auth.schema.js +1 -1
- package/dist/validation/auth.schema.js.map +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +37 -8
- package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -2
- package/frontend-vue/layers/dms-auth/i18n/locales/auth-en-GB.json +3 -1
- package/frontend-vue/layers/dms-auth/i18n/locales/auth-fr-FR.json +3 -1
- package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +27 -2
- package/frontend-vue/layers/dms-core/app/utils/menu.ts +26 -10
- package/frontend-vue/layers/dms-core/app/utils/routePath.ts +23 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +3 -0
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +3 -0
- package/frontend-vue/layers/dms-core/shared/types/runtime-config.d.ts +4 -1
- package/frontend-vue/layers/dms-layout/app/app.config.ts +9 -1
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +50 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +16 -13
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +2 -2
- package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +11 -5
- package/frontend-vue/layers/dms-layout/app/build/components/notification/widgetRequest.ts +20 -0
- package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -5
- package/frontend-vue/layers/dms-layout/app/components/profile/ProfileLanguage.vue +3 -23
- package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +59 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useQuickActions.ts +15 -2
- package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +33 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +2 -0
- package/frontend-vue/layers/dms-layout/app/emails/EmailAdminInvite.vue +54 -9
- package/frontend-vue/layers/dms-layout/app/error.vue +17 -2
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/types/page.ts +22 -0
- package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +48 -0
- package/frontend-vue/layers/dms-layout/app/utils/favorites.ts +94 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +21 -2
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +21 -2
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/index.ts +1 -1
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/{useOnboardingMiddleware.ts → useOnboarding.ts} +0 -12
- package/frontend-vue/layers/dms-onboarding/app/custom-pages/onboarding.vue +4 -1
- package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +21 -4
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +13 -3
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +13 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +31 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationBadges.ts +36 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +2 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/columnVisibility.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/build/types/tree.ts +5 -10
- package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/activity/ActivityItem.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/banner/Banner.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/card/NavCard.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +6 -16
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/internal/TrendBadge.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/copy/CopyButton.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +3 -3
- package/frontend-vue/layers/dms-ui/app/components/master-detail/MasterDetail.vue +5 -1
- package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/status/StatusSummary.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +33 -10
- package/frontend-vue/layers/dms-ui/app/composables/chart/donutRecords.ts +26 -0
- package/frontend-vue/layers/dms-ui/app/composables/tab/types/props.ts +4 -2
- package/frontend-vue/layers/dms-ui/app/composables/tab/useTab.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/column.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/custom-button.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/types/quick-actions.ts +38 -3
- package/frontend-vue/tests/admin-invite-email.test.ts +86 -0
- package/frontend-vue/tests/api-error-title.test.ts +24 -0
- package/frontend-vue/tests/auth-middleware-error.test.ts +58 -0
- package/frontend-vue/tests/breadcrumb-target.test.ts +50 -0
- package/frontend-vue/tests/button-disabled-theme.test.ts +21 -0
- package/frontend-vue/tests/chart-nested-donut.test.ts +97 -0
- package/frontend-vue/tests/error-page-message.test.ts +83 -0
- package/frontend-vue/tests/favorite-page.test.ts +104 -0
- package/frontend-vue/tests/icon-aria-hidden.test.ts +29 -0
- package/frontend-vue/tests/layout-banners.test.ts +224 -0
- package/frontend-vue/tests/menu-navigation.test.ts +39 -10
- package/frontend-vue/tests/notification-popover-errors.test.ts +147 -0
- package/frontend-vue/tests/onboarding-middleware.test.ts +103 -0
- package/frontend-vue/tests/password-strength-meter.test.ts +33 -0
- package/frontend-vue/tests/quick-action-intent.test.ts +74 -0
- package/frontend-vue/tests/quick-actions.test.ts +23 -0
- package/frontend-vue/tests/relation-badges.test.ts +32 -0
- package/frontend-vue/tests/signup-invitation-link.test.ts +126 -0
- package/frontend-vue/tests/skeleton-paragraph.test.ts +30 -0
- package/frontend-vue/tests/tab-without-component-id.test.ts +51 -0
- package/frontend-vue/tests/table-column-visibility.test.ts +20 -0
- package/frontend-vue/tests/user-language.test.ts +58 -0
- package/frontend-vue/tests/vue-inventory.test.ts +1 -1
- package/package.json +2 -2
- package/skills/dms/REFERENCE.md +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const admin_invite_email_1 = require("../../../utils/admin-invite-email");
|
|
5
|
+
describe("[unit] utils/admin-invite-email", () => {
|
|
6
|
+
describe("resolveAdminInviteLanguage", () => {
|
|
7
|
+
it("keeps a supported language, regional variants included", () => {
|
|
8
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)("fr")).to.equal("fr");
|
|
9
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)("fr-FR")).to.equal("fr");
|
|
10
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)("en-GB")).to.equal("en");
|
|
11
|
+
});
|
|
12
|
+
it("falls back to English for a missing or unsupported language", () => {
|
|
13
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)()).to.equal("en");
|
|
14
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)("de")).to.equal("en");
|
|
15
|
+
(0, chai_1.expect)((0, admin_invite_email_1.resolveAdminInviteLanguage)("constructor")).to.equal("en");
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("buildAdminInviteSubject", () => {
|
|
19
|
+
it("keeps the generic subject without any name", () => {
|
|
20
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({})).to.equal("You're Invited to Join");
|
|
21
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({ inviterName: "Ada" })).to.equal("You're Invited to Join");
|
|
22
|
+
});
|
|
23
|
+
it("names the platform alone", () => {
|
|
24
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({ platformName: "Acme" })).to.equal("You're invited to join Acme");
|
|
25
|
+
});
|
|
26
|
+
it("names the workspace, the platform and the inviter", () => {
|
|
27
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({
|
|
28
|
+
workspaceName: "Storefront",
|
|
29
|
+
platformName: "Acme",
|
|
30
|
+
inviterName: "Ada",
|
|
31
|
+
})).to.equal("Ada invited you to join Storefront on Acme");
|
|
32
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({ workspaceName: "Storefront" })).to.equal("You're invited to join Storefront");
|
|
33
|
+
});
|
|
34
|
+
it("writes the subject in French", () => {
|
|
35
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({}, "fr")).to.equal("Vous êtes invité à nous rejoindre");
|
|
36
|
+
(0, chai_1.expect)((0, admin_invite_email_1.buildAdminInviteSubject)({
|
|
37
|
+
workspaceName: "Storefront",
|
|
38
|
+
platformName: "Acme",
|
|
39
|
+
inviterName: "Ada",
|
|
40
|
+
}, "fr-FR")).to.equal("Ada vous invite à rejoindre Storefront sur Acme");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("buildAdminInviteSignupLink", () => {
|
|
44
|
+
const BASE_URL = "https://app.local";
|
|
45
|
+
it("carries the token, the address and the invitation's language", () => {
|
|
46
|
+
const link = new URL((0, admin_invite_email_1.buildAdminInviteSignupLink)(BASE_URL, {
|
|
47
|
+
email: "ada+team@acme.dev",
|
|
48
|
+
token: "t0ken",
|
|
49
|
+
inviteeName: "Ada Lovelace",
|
|
50
|
+
language: "fr",
|
|
51
|
+
}));
|
|
52
|
+
(0, chai_1.expect)(link.origin + link.pathname).to.equal(`${BASE_URL}/auth/signup`);
|
|
53
|
+
(0, chai_1.expect)(Object.fromEntries(link.searchParams)).to.deep.equal({
|
|
54
|
+
token: "t0ken",
|
|
55
|
+
email: "ada+team@acme.dev",
|
|
56
|
+
name: "Ada Lovelace",
|
|
57
|
+
lang: "fr",
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
it("leaves out the name and the language when unknown", () => {
|
|
61
|
+
const link = new URL((0, admin_invite_email_1.buildAdminInviteSignupLink)(BASE_URL, {
|
|
62
|
+
email: "ada@acme.dev",
|
|
63
|
+
token: "t0ken",
|
|
64
|
+
}));
|
|
65
|
+
(0, chai_1.expect)([...link.searchParams.keys()]).to.deep.equal(["token", "email"]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=admin-invite-email.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-invite-email.test.js","sourceRoot":"","sources":["../../../../src/test/unit/utils/admin-invite-email.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface AdminInviteEmailNames {
|
|
2
|
+
workspaceName?: string;
|
|
3
|
+
inviterName?: string;
|
|
4
|
+
platformName?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveAdminInviteLanguage(language?: string): string;
|
|
7
|
+
export declare function buildAdminInviteSubject(names: AdminInviteEmailNames, language?: string): string;
|
|
8
|
+
export interface AdminInviteSignupTarget {
|
|
9
|
+
email: string;
|
|
10
|
+
token: string;
|
|
11
|
+
inviteeName?: string;
|
|
12
|
+
language?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function buildAdminInviteSignupLink(clientBaseUrl: string, { email, token, inviteeName, language }: AdminInviteSignupTarget): string;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveAdminInviteLanguage = resolveAdminInviteLanguage;
|
|
4
|
+
exports.buildAdminInviteSubject = buildAdminInviteSubject;
|
|
5
|
+
exports.buildAdminInviteSignupLink = buildAdminInviteSignupLink;
|
|
6
|
+
const DEFAULT_LANGUAGE = "en";
|
|
7
|
+
const LANGUAGE_CODE_LENGTH = 2;
|
|
8
|
+
const SUBJECT_COPY = {
|
|
9
|
+
en: {
|
|
10
|
+
generic: "You're Invited to Join",
|
|
11
|
+
join: (target) => `You're invited to join ${target}`,
|
|
12
|
+
joinFromInviter: (inviter, target) => `${inviter} invited you to join ${target}`,
|
|
13
|
+
workspaceOnPlatform: (workspace, platform) => `${workspace} on ${platform}`,
|
|
14
|
+
},
|
|
15
|
+
fr: {
|
|
16
|
+
generic: "Vous êtes invité à nous rejoindre",
|
|
17
|
+
join: (target) => `Vous êtes invité à rejoindre ${target}`,
|
|
18
|
+
joinFromInviter: (inviter, target) => `${inviter} vous invite à rejoindre ${target}`,
|
|
19
|
+
workspaceOnPlatform: (workspace, platform) => `${workspace} sur ${platform}`,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
function resolveAdminInviteLanguage(language) {
|
|
23
|
+
const code = language?.slice(0, LANGUAGE_CODE_LENGTH).toLowerCase();
|
|
24
|
+
return code && Object.hasOwn(SUBJECT_COPY, code) ? code : DEFAULT_LANGUAGE;
|
|
25
|
+
}
|
|
26
|
+
function joinTarget(copy, names) {
|
|
27
|
+
const { workspaceName, platformName } = names;
|
|
28
|
+
if (workspaceName && platformName) {
|
|
29
|
+
return copy.workspaceOnPlatform(workspaceName, platformName);
|
|
30
|
+
}
|
|
31
|
+
return workspaceName || platformName;
|
|
32
|
+
}
|
|
33
|
+
function buildAdminInviteSubject(names, language) {
|
|
34
|
+
const copy = SUBJECT_COPY[resolveAdminInviteLanguage(language)];
|
|
35
|
+
const target = joinTarget(copy, names);
|
|
36
|
+
if (!target) {
|
|
37
|
+
return copy.generic;
|
|
38
|
+
}
|
|
39
|
+
return names.inviterName
|
|
40
|
+
? copy.joinFromInviter(names.inviterName, target)
|
|
41
|
+
: copy.join(target);
|
|
42
|
+
}
|
|
43
|
+
function buildAdminInviteSignupLink(clientBaseUrl, { email, token, inviteeName, language }) {
|
|
44
|
+
const query = new URLSearchParams({ token, email });
|
|
45
|
+
if (inviteeName)
|
|
46
|
+
query.set("name", inviteeName);
|
|
47
|
+
if (language)
|
|
48
|
+
query.set("lang", language);
|
|
49
|
+
return `${clientBaseUrl}/auth/signup?${query.toString()}`;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=admin-invite-email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-invite-email.js","sourceRoot":"","sources":["../../src/utils/admin-invite-email.ts"],"names":[],"mappings":";;AAyCA,gEAGC;AAiBD,0DAYC;AAcD,gEAQC;AA/FD,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAkB/B,MAAM,YAAY,GAA2C;IAC3D,EAAE,EAAE;QACF,OAAO,EAAE,wBAAwB;QACjC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,0BAA0B,MAAM,EAAE;QACpD,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CACnC,GAAG,OAAO,wBAAwB,MAAM,EAAE;QAC5C,mBAAmB,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,CAAC,GAAG,SAAS,OAAO,QAAQ,EAAE;KAC5E;IACD,EAAE,EAAE;QACF,OAAO,EAAE,mCAAmC;QAC5C,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,gCAAgC,MAAM,EAAE;QAC1D,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CACnC,GAAG,OAAO,4BAA4B,MAAM,EAAE;QAChD,mBAAmB,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,EAAE,CAC3C,GAAG,SAAS,QAAQ,QAAQ,EAAE;KACjC;CACF,CAAC;AAMF,SAAgB,0BAA0B,CAAC,QAAiB;IAC1D,MAAM,IAAI,GAAG,QAAQ,EAAE,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,WAAW,EAAE,CAAC;IACpE,OAAO,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC;AAC7E,CAAC;AAED,SAAS,UAAU,CACjB,IAA4B,EAC5B,KAA4B;IAE5B,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,KAAK,CAAC;IAC9C,IAAI,aAAa,IAAI,YAAY,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,aAAa,IAAI,YAAY,CAAC;AACvC,CAAC;AAMD,SAAgB,uBAAuB,CACrC,KAA4B,EAC5B,QAAiB;IAEjB,MAAM,IAAI,GAAG,YAAY,CAAC,0BAA0B,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IACD,OAAO,KAAK,CAAC,WAAW;QACtB,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,WAAW,EAAE,MAAM,CAAC;QACjD,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACxB,CAAC;AAcD,SAAgB,0BAA0B,CACxC,aAAqB,EACrB,EAAE,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAA2B;IAEhE,MAAM,KAAK,GAAG,IAAI,eAAe,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,IAAI,WAAW;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAChD,IAAI,QAAQ;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC1C,OAAO,GAAG,aAAa,gBAAgB,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC5D,CAAC"}
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -4,6 +4,5 @@ export * from "./export-jobs-run";
|
|
|
4
4
|
export * from "./run-in-batches";
|
|
5
5
|
export * from "./tenant-export-archive";
|
|
6
6
|
export * from "@antelopejs/interface-dms/utils/row-instance";
|
|
7
|
-
export * from "@antelopejs/interface-dms/utils/saas-mode";
|
|
8
7
|
export * from "@antelopejs/interface-dms/utils/time";
|
|
9
8
|
export * from "@antelopejs/interface-dms/utils/value-parser";
|
package/dist/utils/index.js
CHANGED
|
@@ -20,7 +20,6 @@ __exportStar(require("./export-jobs-run"), exports);
|
|
|
20
20
|
__exportStar(require("./run-in-batches"), exports);
|
|
21
21
|
__exportStar(require("./tenant-export-archive"), exports);
|
|
22
22
|
__exportStar(require("@antelopejs/interface-dms/utils/row-instance"), exports);
|
|
23
|
-
__exportStar(require("@antelopejs/interface-dms/utils/saas-mode"), exports);
|
|
24
23
|
__exportStar(require("@antelopejs/interface-dms/utils/time"), exports);
|
|
25
24
|
__exportStar(require("@antelopejs/interface-dms/utils/value-parser"), exports);
|
|
26
25
|
//# sourceMappingURL=index.js.map
|
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,gDAA8B;AAC9B,oDAAkC;AAClC,mDAAiC;AACjC,0DAAwC;AACxC,+EAA6D;AAC7D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAwC;AACxC,gDAA8B;AAC9B,oDAAkC;AAClC,mDAAiC;AACjC,0DAAwC;AACxC,+EAA6D;AAC7D,uEAAqD;AACrD,+EAA6D"}
|
|
@@ -4,20 +4,20 @@ export declare const authSchema: {
|
|
|
4
4
|
name: z.ZodString;
|
|
5
5
|
email: z.ZodString;
|
|
6
6
|
password: z.ZodString;
|
|
7
|
-
lang: z.ZodString
|
|
7
|
+
lang: z.ZodOptional<z.ZodString>;
|
|
8
8
|
token: z.ZodString;
|
|
9
9
|
}, "strip", z.ZodTypeAny, {
|
|
10
10
|
token: string;
|
|
11
11
|
name: string;
|
|
12
12
|
password: string;
|
|
13
13
|
email: string;
|
|
14
|
-
lang
|
|
14
|
+
lang?: string | undefined;
|
|
15
15
|
}, {
|
|
16
16
|
token: string;
|
|
17
17
|
name: string;
|
|
18
18
|
password: string;
|
|
19
19
|
email: string;
|
|
20
|
-
lang
|
|
20
|
+
lang?: string | undefined;
|
|
21
21
|
}>;
|
|
22
22
|
login: z.ZodObject<{
|
|
23
23
|
email: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.schema.js","sourceRoot":"","sources":["../../src/validation/auth.schema.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AAEzB,yCAA4C;AAE5C,MAAM,+BAA+B,GAAG,IAAI,CAAC;AAC7C,MAAM,uBAAuB,GAAG,EAAE,CAAC;AACnC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AAEvB,QAAA,UAAU,GAAG;IACxB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE;QACzB,QAAQ,EAAE,yBAAc;QACxB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;
|
|
1
|
+
{"version":3,"file":"auth.schema.js","sourceRoot":"","sources":["../../src/validation/auth.schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import type { FormSubmitEvent } from "@nuxt/ui";
|
|
|
4
4
|
|
|
5
5
|
const MIN_VALID_PASSWORD_SCORE = 4;
|
|
6
6
|
|
|
7
|
-
const { locale } = useI18n();
|
|
7
|
+
const { locale, locales, setLocale } = useI18n();
|
|
8
8
|
const config = useDmsRuntimeConfig();
|
|
9
9
|
const homepage = useHomepage();
|
|
10
10
|
|
|
@@ -12,13 +12,20 @@ const route = useDmsRoute();
|
|
|
12
12
|
const queryToken = computed(() => route.query.token as string);
|
|
13
13
|
const queryEmail = computed(() => route.query.email as string);
|
|
14
14
|
const queryName = computed(() => route.query.name as string | undefined);
|
|
15
|
+
// Only an invitation email links here, so a link without its token was cut
|
|
16
|
+
// short on the way: say so, rather than answering with a server error.
|
|
17
|
+
const hasInvitationToken = computed(
|
|
18
|
+
() => typeof queryToken.value === "string" && queryToken.value !== "",
|
|
19
|
+
);
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
// The invitation link carries the language the invitee was invited in: the
|
|
22
|
+
// page opens in it, and the footer switcher still lets them pick another one
|
|
23
|
+
// before the account is created with whichever is current.
|
|
24
|
+
const invitationLanguage = locales.value.find(
|
|
25
|
+
(available) => available.code === route.query.lang,
|
|
26
|
+
);
|
|
27
|
+
if (invitationLanguage && invitationLanguage.code !== locale.value) {
|
|
28
|
+
void setLocale(invitationLanguage.code);
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
const isLoading = ref(false);
|
|
@@ -66,7 +73,29 @@ async function onSubmit(payload: FormSubmitEvent<Schema>) {
|
|
|
66
73
|
</script>
|
|
67
74
|
|
|
68
75
|
<template>
|
|
69
|
-
<div class="mx-auto max-w-
|
|
76
|
+
<div v-if="!hasInvitationToken" class="mx-auto max-w-md">
|
|
77
|
+
<DmsCard
|
|
78
|
+
variant="elevated"
|
|
79
|
+
:padded="false"
|
|
80
|
+
class="p-6 sm:p-12"
|
|
81
|
+
data-testid="signup-invalid-invitation"
|
|
82
|
+
>
|
|
83
|
+
<UIcon
|
|
84
|
+
name="i-ph-link-break"
|
|
85
|
+
class="text-warning mb-4 size-10"
|
|
86
|
+
:aria-hidden="true"
|
|
87
|
+
/>
|
|
88
|
+
<h1 class="pb-5 text-2xl font-bold">
|
|
89
|
+
{{ $t("page.signup.invalid_invitation_title") }}
|
|
90
|
+
</h1>
|
|
91
|
+
<p class="text-muted pb-7 text-sm font-normal">
|
|
92
|
+
{{ $t("page.signup.invalid_invitation_description") }}
|
|
93
|
+
</p>
|
|
94
|
+
<UButton :label="$t('button.login')" to="/auth" block />
|
|
95
|
+
</DmsCard>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<div v-else class="mx-auto max-w-xl">
|
|
70
99
|
<DmsCard variant="elevated" :padded="false" class="p-6 sm:p-12">
|
|
71
100
|
<h1 class="pb-11 text-xl font-bold sm:text-2xl">
|
|
72
101
|
{{ $t("page.signup.create_account_title") }}
|
|
@@ -72,8 +72,11 @@ async function isPublicAccessRoute(to: ReturnType<typeof useDmsRoute>) {
|
|
|
72
72
|
export default defineDmsMiddleware(async (to) => {
|
|
73
73
|
if (to.matched.length === 0) return;
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
// An error the page already raised (a failed `validation`, a missing page)
|
|
76
|
+
// is committed and rendered with its own status. Re-throwing it here would
|
|
77
|
+
// reject the pending navigation the SSR render awaits, turning that 404 into
|
|
78
|
+
// a 500 fallback render.
|
|
79
|
+
if (useError().value) return abortNavigation();
|
|
77
80
|
|
|
78
81
|
const options = to.meta.auth as MiddlewareMeta | undefined;
|
|
79
82
|
if (!options) return;
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"signup": {
|
|
17
17
|
"create_account_title": "Create your account",
|
|
18
|
-
"error_title": "Account creation error"
|
|
18
|
+
"error_title": "Account creation error",
|
|
19
|
+
"invalid_invitation_title": "This invitation link is invalid or incomplete",
|
|
20
|
+
"invalid_invitation_description": "Open the link from your invitation email again, or ask the workspace owner to send you a new invitation. Already have an account? Log in."
|
|
19
21
|
},
|
|
20
22
|
"2fa": {
|
|
21
23
|
"title": "Two-factor verification",
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
},
|
|
16
16
|
"signup": {
|
|
17
17
|
"create_account_title": "Créer votre compte",
|
|
18
|
-
"error_title": "Erreur de création de compte"
|
|
18
|
+
"error_title": "Erreur de création de compte",
|
|
19
|
+
"invalid_invitation_title": "Ce lien d'invitation est invalide ou incomplet",
|
|
20
|
+
"invalid_invitation_description": "Rouvrez le lien de votre e-mail d'invitation, ou demandez au propriétaire de l'espace de travail de vous envoyer une nouvelle invitation. Vous avez déjà un compte ? Connectez-vous."
|
|
19
21
|
},
|
|
20
22
|
"2fa": {
|
|
21
23
|
"title": "Vérification à deux facteurs",
|
|
@@ -6,12 +6,16 @@ interface Options {
|
|
|
6
6
|
|
|
7
7
|
interface ApiError {
|
|
8
8
|
statusCode?: number;
|
|
9
|
+
status?: number;
|
|
9
10
|
message?: string;
|
|
10
11
|
data?: unknown;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
+
const SERVER_ERROR_TITLE_KEY = "error.500.title";
|
|
15
|
+
const CLIENT_ERROR_TITLE_KEY = "error.request_refused.title";
|
|
14
16
|
const FALLBACK_ERROR_KEY = "error.500.description";
|
|
17
|
+
const CLIENT_ERROR_STATUS_MIN = 400;
|
|
18
|
+
const CLIENT_ERROR_STATUS_MAX = 499;
|
|
15
19
|
|
|
16
20
|
function isApiError(error: unknown): error is ApiError {
|
|
17
21
|
return typeof error === "object" && error !== null;
|
|
@@ -32,6 +36,27 @@ function resolveDescription(error: ApiError): string {
|
|
|
32
36
|
return FALLBACK_ERROR_KEY;
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
function isClientErrorStatus(status: number | undefined): boolean {
|
|
40
|
+
return (
|
|
41
|
+
status !== undefined &&
|
|
42
|
+
status >= CLIENT_ERROR_STATUS_MIN &&
|
|
43
|
+
status <= CLIENT_ERROR_STATUS_MAX
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The i18n title key matching a fetch error's HTTP status: a 4xx is a request
|
|
49
|
+
* the backend refused, anything else (5xx, network failure) a server error.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveApiErrorTitle(error: unknown): string {
|
|
52
|
+
const status = isApiError(error)
|
|
53
|
+
? (error.statusCode ?? error.status)
|
|
54
|
+
: undefined;
|
|
55
|
+
return isClientErrorStatus(status)
|
|
56
|
+
? CLIENT_ERROR_TITLE_KEY
|
|
57
|
+
: SERVER_ERROR_TITLE_KEY;
|
|
58
|
+
}
|
|
59
|
+
|
|
35
60
|
/**
|
|
36
61
|
* Extract the backend's error body from a fetch error — an i18n message key
|
|
37
62
|
* per the API error contract — falling back to the generic 500 key.
|
|
@@ -46,7 +71,7 @@ export function useApiError(error: unknown, options?: Options) {
|
|
|
46
71
|
const { t } = dmsApp.$i18n;
|
|
47
72
|
const translate = (message: string) => resolveApiMessage(t, message);
|
|
48
73
|
|
|
49
|
-
const title = options?.title ||
|
|
74
|
+
const title = options?.title || resolveApiErrorTitle(error);
|
|
50
75
|
const description = options?.description || resolveApiErrorMessage(error);
|
|
51
76
|
|
|
52
77
|
toast.add({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { NavigationMenuItem } from "@nuxt/ui";
|
|
2
|
+
import { parseLinkQuery, stripQueryAndHash } from "./routePath";
|
|
2
3
|
|
|
3
4
|
// Menu items carry the registered page/category's dot-separated `fullId`
|
|
4
5
|
// (e.g. `pages.erp.orders`) plus the DMS semantic fields; Nuxt UI's
|
|
@@ -88,7 +89,9 @@ const matchesRoutePrefix = (currentPath: string, to: string): boolean => {
|
|
|
88
89
|
const resolveItemTarget = (
|
|
89
90
|
to: NavigationMenuItem["to"],
|
|
90
91
|
): MenuItemTarget | null => {
|
|
91
|
-
if (typeof to === "string")
|
|
92
|
+
if (typeof to === "string") {
|
|
93
|
+
return { path: stripQueryAndHash(to), query: parseLinkQuery(to) };
|
|
94
|
+
}
|
|
92
95
|
const target = to as MenuItemTarget | undefined;
|
|
93
96
|
if (target && typeof target.path === "string") {
|
|
94
97
|
return { path: target.path, query: target.query };
|
|
@@ -96,9 +99,11 @@ const resolveItemTarget = (
|
|
|
96
99
|
return null;
|
|
97
100
|
};
|
|
98
101
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
+
const hasQuery = (target: MenuItemTarget | null): boolean =>
|
|
103
|
+
Object.keys(target?.query ?? {}).length > 0;
|
|
104
|
+
|
|
105
|
+
// Subset match: the entry's own parameters must be the current ones, while
|
|
106
|
+
// unrelated parameters the page adds (tabs, filters) are ignored.
|
|
102
107
|
const queryMatches = (
|
|
103
108
|
declared: MenuItemTarget["query"],
|
|
104
109
|
currentQuery: Record<string, unknown>,
|
|
@@ -133,18 +138,29 @@ export const isMenuItemActive = (
|
|
|
133
138
|
return matchedFullId !== null && fullId === matchedFullId;
|
|
134
139
|
};
|
|
135
140
|
|
|
136
|
-
// Only the boolean `true` is set: Nuxt UI forwards an explicit
|
|
137
|
-
// underlying link, and a `false` there would suppress the
|
|
138
|
-
//
|
|
141
|
+
// Only the boolean `true` is set on plain entries: Nuxt UI forwards an explicit
|
|
142
|
+
// `active` to the underlying link, and a `false` there would suppress the
|
|
143
|
+
// built-in route match. Entries carrying a query always get an explicit value,
|
|
144
|
+
// because that built-in match is a URL prefix test that would light up
|
|
145
|
+
// `?project=a` on `?project=ab`.
|
|
146
|
+
const resolveActiveState = (
|
|
147
|
+
item: NavigationMenuItem,
|
|
148
|
+
matchedFullId: string | null,
|
|
149
|
+
currentRoute: MenuRouteState,
|
|
150
|
+
): Partial<Pick<NavigationMenuItem, "active">> => {
|
|
151
|
+
if (isMenuItemActive(item, matchedFullId, currentRoute)) {
|
|
152
|
+
return { active: true };
|
|
153
|
+
}
|
|
154
|
+
return hasQuery(resolveItemTarget(item.to)) ? { active: false } : {};
|
|
155
|
+
};
|
|
156
|
+
|
|
139
157
|
export const addActiveStateToMenuItem = (
|
|
140
158
|
item: NavigationMenuItem,
|
|
141
159
|
matchedFullId: string | null,
|
|
142
160
|
currentRoute: MenuRouteState,
|
|
143
161
|
): NavigationMenuItem => ({
|
|
144
162
|
...item,
|
|
145
|
-
...(
|
|
146
|
-
? { active: true }
|
|
147
|
-
: {}),
|
|
163
|
+
...resolveActiveState(item, matchedFullId, currentRoute),
|
|
148
164
|
children: item.children?.map((child) =>
|
|
149
165
|
addActiveStateToMenuItem(child, matchedFullId, currentRoute),
|
|
150
166
|
),
|
|
@@ -9,3 +9,26 @@ export function stripQueryAndHash(link: string): string {
|
|
|
9
9
|
const [path] = link.split(QUERY_OR_HASH);
|
|
10
10
|
return path ?? link;
|
|
11
11
|
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A link to `path` narrowed by `query`, as the single string every link
|
|
15
|
+
* renderer accepts: the Inertia-backed `ULink` only takes a string `to`, and
|
|
16
|
+
* stringifies an object into `[object Object]`.
|
|
17
|
+
*/
|
|
18
|
+
export function buildLinkWithQuery(
|
|
19
|
+
path: string,
|
|
20
|
+
query: Record<string, string> | undefined,
|
|
21
|
+
): string {
|
|
22
|
+
const search = new URLSearchParams(query ?? {}).toString();
|
|
23
|
+
return search === "" ? path : `${path}?${search}`;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The query parameters a link carries, the inverse of `buildLinkWithQuery`. */
|
|
27
|
+
export function parseLinkQuery(link: string): Record<string, string> {
|
|
28
|
+
const [withoutHash = ""] = link.split("#");
|
|
29
|
+
const queryStart = withoutHash.indexOf("?");
|
|
30
|
+
if (queryStart === -1) return {};
|
|
31
|
+
return Object.fromEntries(
|
|
32
|
+
new URLSearchParams(withoutHash.slice(queryStart + 1)),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
"title": "Server error",
|
|
38
38
|
"description": "An unexpected error occurred. Please try again later."
|
|
39
39
|
},
|
|
40
|
+
"request_refused": {
|
|
41
|
+
"title": "Request refused"
|
|
42
|
+
},
|
|
40
43
|
"recovering": {
|
|
41
44
|
"title": "Restoring your session...",
|
|
42
45
|
"description": "Your session has expired. Refreshing your access and reloading the page."
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
"title": "Erreur serveur",
|
|
38
38
|
"description": "Une erreur inattendue s'est produite. Veuillez réessayer plus tard."
|
|
39
39
|
},
|
|
40
|
+
"request_refused": {
|
|
41
|
+
"title": "Requête refusée"
|
|
42
|
+
},
|
|
40
43
|
"recovering": {
|
|
41
44
|
"title": "Restauration de votre session...",
|
|
42
45
|
"description": "Votre session a expiré. Renouvellement de l'accès et rechargement de la page."
|
|
@@ -43,7 +43,10 @@ interface DmsPrivateConfig {
|
|
|
43
43
|
relaySecret: string;
|
|
44
44
|
/**
|
|
45
45
|
* Whether a trusted reverse proxy sits immediately in front and appends
|
|
46
|
-
* to `x-forwarded-for
|
|
46
|
+
* to `x-forwarded-for`.
|
|
47
|
+
*
|
|
48
|
+
* @deprecated Mirrors the deprecated `auth.oauth.trustProxy`. The frontend
|
|
49
|
+
* server keys its OAuth rate limit on its own `DMS_TRUSTED_PROXY_HOPS`.
|
|
47
50
|
*/
|
|
48
51
|
trustProxy?: boolean;
|
|
49
52
|
};
|
|
@@ -134,7 +134,15 @@ export default {
|
|
|
134
134
|
button: {
|
|
135
135
|
slots: {
|
|
136
136
|
// Tactile press + full transition (design .btn active:scale(.97)).
|
|
137
|
-
|
|
137
|
+
// Nuxt UI only fades a disabled button to 75%, which still reads as
|
|
138
|
+
// enabled on a filled primary: fade it further and drop the press and
|
|
139
|
+
// the inset highlight so every variant looks inert.
|
|
140
|
+
base: [
|
|
141
|
+
"transition active:scale-[0.98]",
|
|
142
|
+
"disabled:opacity-50 aria-disabled:opacity-50",
|
|
143
|
+
"disabled:active:scale-100 aria-disabled:active:scale-100",
|
|
144
|
+
"disabled:shadow-none aria-disabled:shadow-none",
|
|
145
|
+
].join(" "),
|
|
138
146
|
},
|
|
139
147
|
compoundVariants: [
|
|
140
148
|
// Inset top highlight on the solid primary fill (design .btn-primary).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Stacks the global layout banners under the dashboard header, on every page
|
|
4
|
+
* of the dashboard layout. Each banner carries its own live-region role, so a
|
|
5
|
+
* banner appearing after a navigation is announced as well.
|
|
6
|
+
*/
|
|
7
|
+
const { banners, dismiss } = useLayoutBanners();
|
|
8
|
+
const { processI18n } = useTranslation();
|
|
9
|
+
|
|
10
|
+
const entries = computed(() =>
|
|
11
|
+
banners.value.map((banner) => ({
|
|
12
|
+
banner,
|
|
13
|
+
presentation: resolveLayoutBannerPresentation(banner),
|
|
14
|
+
})),
|
|
15
|
+
);
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<div
|
|
20
|
+
v-if="entries.length"
|
|
21
|
+
data-dms-layout-banners
|
|
22
|
+
class="border-default flex flex-col border-b"
|
|
23
|
+
>
|
|
24
|
+
<div
|
|
25
|
+
v-for="{ banner, presentation } in entries"
|
|
26
|
+
:key="banner.key"
|
|
27
|
+
:role="presentation.role"
|
|
28
|
+
:data-dms-layout-banner="banner.key"
|
|
29
|
+
>
|
|
30
|
+
<UAlert
|
|
31
|
+
variant="subtle"
|
|
32
|
+
orientation="horizontal"
|
|
33
|
+
:color="presentation.color"
|
|
34
|
+
:icon="presentation.icon"
|
|
35
|
+
:close="banner.dismissible"
|
|
36
|
+
class="rounded-none px-4 py-2.5 sm:px-6"
|
|
37
|
+
@update:open="dismiss(banner.key)"
|
|
38
|
+
>
|
|
39
|
+
<template #description>
|
|
40
|
+
<component
|
|
41
|
+
:is="banner.component"
|
|
42
|
+
v-if="banner.component"
|
|
43
|
+
v-bind="banner.props"
|
|
44
|
+
/>
|
|
45
|
+
<template v-else>{{ processI18n(banner.text ?? "") }}</template>
|
|
46
|
+
</template>
|
|
47
|
+
</UAlert>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
@@ -58,22 +58,21 @@ const { processI18n } = useTranslation();
|
|
|
58
58
|
const favoritePages = useFavoritePages();
|
|
59
59
|
|
|
60
60
|
const currentPageInfo = computed((): FavoritePage | null => {
|
|
61
|
-
const
|
|
62
|
-
const matchedRoute = siteLayout.findMatchingRoute(currentPath);
|
|
61
|
+
const matchedRoute = siteLayout.findMatchingRoute(route.path);
|
|
63
62
|
|
|
64
63
|
if (!matchedRoute) {
|
|
65
64
|
return null;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
return {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
matchedRoute.metadata.displayName ||
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
};
|
|
67
|
+
return buildFavoritePage({
|
|
68
|
+
path: route.path,
|
|
69
|
+
metadata: {
|
|
70
|
+
...matchedRoute.metadata,
|
|
71
|
+
displayName: matchedRoute.metadata.displayName || route.name?.toString(),
|
|
72
|
+
},
|
|
73
|
+
query: route.query,
|
|
74
|
+
translate: processI18n,
|
|
75
|
+
});
|
|
77
76
|
});
|
|
78
77
|
|
|
79
78
|
const isCurrentPageFavorite = computed(() => {
|
|
@@ -112,11 +111,15 @@ const breadcrumb = computed((): BreadcrumbItem[] => {
|
|
|
112
111
|
return null;
|
|
113
112
|
}
|
|
114
113
|
|
|
115
|
-
const
|
|
114
|
+
const target = buildBreadcrumbTarget(
|
|
115
|
+
to,
|
|
116
|
+
matchingRoute.metadata,
|
|
117
|
+
route.query,
|
|
118
|
+
);
|
|
116
119
|
|
|
117
120
|
return {
|
|
118
121
|
label: processI18n(matchingRoute.metadata?.displayName || path),
|
|
119
|
-
...(
|
|
122
|
+
...(target ? { to: target } : {}),
|
|
120
123
|
};
|
|
121
124
|
})
|
|
122
125
|
.filter((item) => item !== null),
|
|
@@ -42,8 +42,8 @@ if (!siteLayout.siteLayout.value) {
|
|
|
42
42
|
|
|
43
43
|
const favoritePages = useFavoritePages();
|
|
44
44
|
|
|
45
|
-
favoritePages.cleanupInvalidFavorites(
|
|
46
|
-
(path
|
|
45
|
+
favoritePages.cleanupInvalidFavorites((path) =>
|
|
46
|
+
isFavoritePathValid(path, siteLayout.findMatchingRoute),
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
const isOwner = useIsOwner();
|