@antelopejs/dms 0.7.21 → 0.8.1
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 +55 -0
- package/dist/implementations/dms/resolver-timeout.d.ts +1 -0
- package/dist/implementations/dms/resolver-timeout.js +13 -3
- package/dist/implementations/dms/resolver-timeout.js.map +1 -1
- package/dist/implementations/dms-auth/account-emails.d.ts +4 -0
- package/dist/implementations/dms-auth/account-emails.js +62 -0
- package/dist/implementations/dms-auth/account-emails.js.map +1 -0
- package/dist/implementations/dms-auth/index.d.ts +1 -3
- package/dist/implementations/dms-auth/index.js +15 -61
- package/dist/implementations/dms-auth/index.js.map +1 -1
- package/dist/pages/settings/users/email-change.js +3 -4
- package/dist/pages/settings/users/email-change.js.map +1 -1
- package/dist/pages/settings/users/invites.js +1 -1
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/members.js +1 -1
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.js +1 -0
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/test/antelope.test.js +2 -1
- package/dist/test/antelope.test.js.map +1 -1
- package/dist/test/helpers/db.js +19 -11
- package/dist/test/helpers/db.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +4 -2
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js +8 -5
- package/dist/test/unit/implementations/dms/dynamic-menu-hardening.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/layout-banners.test.js +20 -7
- package/dist/test/unit/implementations/dms/layout-banners.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/built-in-emails.test.js +34 -0
- package/dist/test/unit/implementations/dms-html-render/built-in-emails.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/hook-handler-results.test.js +49 -0
- package/dist/test/unit/interfaces/dms/hook-handler-results.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.js +17 -0
- package/dist/test/unit/interfaces/dms/page-topics.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js +2 -2
- package/dist/test/unit/interfaces/dms-base/confirm-dialog-secondary-action.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/query-response-delta.test.js +29 -0
- package/dist/test/unit/interfaces/dms-base/query-response-delta.test.js.map +1 -0
- package/dist/test/unit/interfaces/dms-base/resource-form.test.js +5 -0
- package/dist/test/unit/interfaces/dms-base/resource-form.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js +12 -2
- package/dist/test/unit/interfaces/dms-base/table-view-form-redirect.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js +29 -4
- package/dist/test/unit/interfaces/dms-base/table-view-reduced-chrome.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js +14 -11
- package/dist/test/unit/interfaces/dms-base/table-view-source-reorder.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-view-views.test.js +8 -5
- package/dist/test/unit/interfaces/dms-base/table-view-views.test.js.map +1 -1
- package/dist/test/unit/utils/email-subjects.test.js +27 -0
- package/dist/test/unit/utils/email-subjects.test.js.map +1 -0
- package/dist/utils/admin-invite-email.js +2 -4
- package/dist/utils/admin-invite-email.js.map +1 -1
- package/dist/utils/email-change-mail.d.ts +1 -1
- package/dist/utils/email-change-mail.js +11 -6
- package/dist/utils/email-change-mail.js.map +1 -1
- package/dist/utils/email-subjects.d.ts +3 -0
- package/dist/utils/email-subjects.js +32 -0
- package/dist/utils/email-subjects.js.map +1 -0
- package/dist/utils/export-jobs.js +15 -10
- package/dist/utils/export-jobs.js.map +1 -1
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +29 -11
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +17 -0
- package/frontend-vue/layers/dms-layout/app/app.config.ts +5 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +18 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +16 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/useHeaderActionControls.ts +14 -0
- package/frontend-vue/layers/dms-layout/app/components/layout/HStack.vue +16 -0
- package/frontend-vue/layers/dms-layout/app/components/layout/VStack.vue +2 -0
- package/frontend-vue/layers/dms-layout/app/composables/useFooterLinks.ts +11 -1
- package/frontend-vue/layers/dms-layout/app/custom-pages/settings/roles.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/emails/EmailAdminInvite.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/emails/EmailChangeNotice.vue +13 -7
- package/frontend-vue/layers/dms-layout/app/emails/EmailChangeVerification.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/emails/EmailExportReady.vue +23 -12
- package/frontend-vue/layers/dms-layout/app/emails/EmailResetPassword.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/emails/EmailTwoFactor.vue +23 -4
- package/frontend-vue/layers/dms-layout/app/emails/EmailUserValidation.vue +18 -6
- package/frontend-vue/layers/dms-layout/app/emails/components/EmailOTP.vue +8 -2
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +40 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +40 -0
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormLoadError.vue +11 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +27 -36
- package/frontend-vue/layers/dms-ui/app/build/composables/chart/apexTheme.ts +59 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +27 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/useActionConfirm.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +0 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryOrder.ts +23 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +19 -15
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +26 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/page/pageUrlValues.ts +41 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/registerDefaultFunctions.ts +2 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +3 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewConfig.ts +7 -12
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/link.ts +7 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +32 -9
- package/frontend-vue/layers/dms-ui/app/components/activity-feed/ActivityFeed.vue +8 -1
- package/frontend-vue/layers/dms-ui/app/components/banner/Banner.vue +9 -4
- package/frontend-vue/layers/dms-ui/app/components/blocks/BannerBlock.vue +5 -11
- package/frontend-vue/layers/dms-ui/app/components/blocks/NavCardGridBlock.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +32 -1
- package/frontend-vue/layers/dms-ui/app/components/check-list/CheckList.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +12 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +7 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +7 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +3 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +14 -6
- package/frontend-vue/layers/dms-ui/app/components/grid/columns.ts +52 -3
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/list-row/ListRow.vue +9 -2
- package/frontend-vue/layers/dms-ui/app/components/save-bar/InstantSaveBadge.vue +13 -5
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +3 -2
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +53 -31
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexPlotOptions.ts +15 -4
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexYRange.ts +123 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +49 -49
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartTheme.ts +20 -1
- package/frontend-vue/layers/dms-ui/app/composables/confirm/types.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/composables/form/types/props.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +8 -6
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +3 -5
- package/frontend-vue/layers/dms-ui/app/utils/bannerToneIcons.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/utils/cascader.ts +26 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +4 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +4 -0
- package/frontend-vue/package.json +1 -1
- package/frontend-vue/pnpm-lock.yaml +5 -5
- package/frontend-vue/tests/activity-feed-fill-height.test.ts +7 -0
- package/frontend-vue/tests/banner-tone-icon.test.ts +55 -0
- package/frontend-vue/tests/builtin-emails.test.ts +141 -0
- package/frontend-vue/tests/cascader.test.ts +27 -0
- package/frontend-vue/tests/chart-auto-y-range.test.ts +25 -9
- package/frontend-vue/tests/chart-colors.test.ts +8 -0
- package/frontend-vue/tests/chart-resize-after-animation.test.ts +100 -0
- package/frontend-vue/tests/chart-type-defaults.test.ts +128 -0
- package/frontend-vue/tests/check-list-marker.test.ts +58 -0
- package/frontend-vue/tests/confirm-from.test.ts +1 -1
- package/frontend-vue/tests/confirm-secondary-action.test.ts +2 -2
- package/frontend-vue/tests/context-links.test.ts +2 -2
- package/frontend-vue/tests/custom-action-confirm-from.test.ts +2 -2
- package/frontend-vue/tests/download-action-target.test.ts +2 -2
- package/frontend-vue/tests/file-drop-prompt.test.ts +72 -0
- package/frontend-vue/tests/footer-config.test.ts +135 -0
- package/frontend-vue/tests/form-actions.test.ts +7 -0
- package/frontend-vue/tests/form-enclosing-form.test.ts +0 -21
- package/frontend-vue/tests/form-entry-order.test.ts +45 -0
- package/frontend-vue/tests/form-footer-kind.test.ts +11 -6
- package/frontend-vue/tests/form-load-error.test.ts +38 -23
- package/frontend-vue/tests/grid-responsive-columns.test.ts +39 -31
- package/frontend-vue/tests/header-actions.test.ts +55 -9
- package/frontend-vue/tests/instant-save-badge.test.ts +11 -4
- package/frontend-vue/tests/list-row-truncate.test.ts +46 -0
- package/frontend-vue/tests/nuxt-ui-aria-labels.test.ts +1 -28
- package/frontend-vue/tests/period-live-pause.test.ts +0 -13
- package/frontend-vue/tests/period-max-days.test.ts +0 -14
- package/frontend-vue/tests/roles-name-errors.test.ts +2 -3
- package/frontend-vue/tests/row-side-panel.test.ts +0 -12
- package/frontend-vue/tests/save-bar.test.ts +15 -3
- package/frontend-vue/tests/save-status-error.test.ts +2 -2
- package/frontend-vue/tests/table-cell-wrap.test.ts +0 -12
- package/frontend-vue/tests/table-density-menu.test.ts +0 -21
- package/frontend-vue/tests/table-live-hold.test.ts +0 -44
- package/frontend-vue/tests/table-row-action-errors.test.ts +5 -5
- package/frontend-vue/tests/table-row-hover.test.ts +2 -12
- package/frontend-vue/tests/table-scroll-area.test.ts +0 -8
- package/frontend-vue/tests/table-view-card-props.test.ts +0 -17
- package/frontend-vue/tests/table-view-cards-reorder.test.ts +0 -16
- package/frontend-vue/tests/table-view-grouped.test.ts +0 -55
- package/frontend-vue/tests/table-view-pinned-rows.test.ts +0 -15
- package/frontend-vue/tests/table-view-record-link.test.ts +0 -17
- package/frontend-vue/tests/table-view-refresh.test.ts +0 -17
- package/frontend-vue/tests/table-view-sources.test.ts +1 -1
- package/frontend-vue/tests/table-view-url-tab.test.ts +0 -14
- package/frontend-vue/tests/template-tokens.test.ts +235 -0
- package/frontend-vue/tests/tone.test.ts +0 -19
- package/frontend-vue/tests/url-interpolation.test.ts +6 -6
- package/package.json +6 -6
- package/skills/dms/SKILL.md +2 -1
- package/skills/dms-pages/REFERENCE.md +2 -2
- package/skills/dms-project/SKILL.md +0 -1
- package/dist/test/integration/auth/login.test.js +0 -57
- package/dist/test/integration/auth/login.test.js.map +0 -1
- package/dist/test/integration/auth/refresh.test.js +0 -59
- package/dist/test/integration/auth/refresh.test.js.map +0 -1
- package/dist/test/integration/auth/sessions.test.js +0 -58
- package/dist/test/integration/auth/sessions.test.js.map +0 -1
- package/dist/test/integration/auth/signup.test.js +0 -99
- package/dist/test/integration/auth/signup.test.js.map +0 -1
- package/dist/test/unit/skills/dms-dev-script.test.d.ts +0 -1
- package/dist/test/unit/skills/dms-dev-script.test.js +0 -137
- package/dist/test/unit/skills/dms-dev-script.test.js.map +0 -1
- package/frontend-vue/tests/form-reload-on-reopen.test.ts +0 -20
- package/frontend-vue/tests/icon-aria-hidden.test.ts +0 -29
- package/frontend-vue/tests/skeleton-paragraph.test.ts +0 -30
- package/frontend-vue/tests/tab-slot-spacing.test.ts +0 -29
- package/frontend-vue/tests/table-empty-body-viewport.test.ts +0 -25
- package/frontend-vue/tests/table-header-placement.test.ts +0 -21
- package/frontend-vue/tests/vue-inventory.test.ts +0 -107
- /package/dist/test/{integration/auth/login.test.d.ts → unit/implementations/dms-html-render/built-in-emails.test.d.ts} +0 -0
- /package/dist/test/{integration/auth/sessions.test.d.ts → unit/interfaces/dms/hook-handler-results.test.d.ts} +0 -0
- /package/dist/test/{integration/auth/refresh.test.d.ts → unit/interfaces/dms-base/query-response-delta.test.d.ts} +0 -0
- /package/dist/test/{integration/auth/signup.test.d.ts → unit/utils/email-subjects.test.d.ts} +0 -0
|
@@ -30,6 +30,7 @@ const html_render_1 = require("@antelopejs/interface-dms/html-render");
|
|
|
30
30
|
const sync_1 = require("csv-stringify/sync");
|
|
31
31
|
const config_1 = require("../config");
|
|
32
32
|
const exportJobs_model_1 = require("@antelopejs/interface-dms/db/internal/exportJobs.model");
|
|
33
|
+
const email_subjects_1 = require("./email-subjects");
|
|
33
34
|
const run_in_batches_1 = require("./run-in-batches");
|
|
34
35
|
const export_jobs_1 = require("@antelopejs/interface-dms/base/export-jobs");
|
|
35
36
|
const time_1 = require("@antelopejs/interface-dms/utils/internal/time");
|
|
@@ -92,7 +93,6 @@ const downloadDeliverer = {
|
|
|
92
93
|
async deliver() { },
|
|
93
94
|
};
|
|
94
95
|
const ExportReadyTemplate = (0, html_render_1.RegisterHtmlTemplate)("EmailExportReady");
|
|
95
|
-
const EXPORT_READY_SUBJECT = "Your data export is ready";
|
|
96
96
|
function buildDeliveryLink(exportRecord) {
|
|
97
97
|
const baseUrl = (0, config_1.getClientBaseUrl)();
|
|
98
98
|
(0, interface_api_util_1.assert)(baseUrl, 500, "clientBaseUrl is required to deliver an export link");
|
|
@@ -103,6 +103,18 @@ function readJobResult(exportRecord) {
|
|
|
103
103
|
return undefined;
|
|
104
104
|
return JSON.parse(exportRecord.json_result);
|
|
105
105
|
}
|
|
106
|
+
function exportReadyEmailData(user, exportRecord) {
|
|
107
|
+
const result = readJobResult(exportRecord);
|
|
108
|
+
const expiresInHours = export_jobs_1.EXPORT_TTL_MS / time_1.MILLISECONDS_PER_HOUR;
|
|
109
|
+
return {
|
|
110
|
+
userName: user.name || user.email,
|
|
111
|
+
downloadLink: buildDeliveryLink(exportRecord),
|
|
112
|
+
expiresIn: `${expiresInHours} hours`,
|
|
113
|
+
expiresInHours,
|
|
114
|
+
partial: result?.partial ?? false,
|
|
115
|
+
failedSources: (result?.failures ?? []).map((failure) => failure.source),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
106
118
|
const emailDeliverer = {
|
|
107
119
|
validate(options) {
|
|
108
120
|
(0, interface_api_util_1.assert)(options.deliveryPath, 400, "Email delivery requires a deliveryPath to build the download link");
|
|
@@ -110,17 +122,10 @@ const emailDeliverer = {
|
|
|
110
122
|
async deliver(exportRecord) {
|
|
111
123
|
const user = await (0, interface_database_decorators_1.GetModel)(db_2.UserModel).get(exportRecord.userId);
|
|
112
124
|
(0, interface_api_util_1.assert)(user, 500, "Export recipient not found");
|
|
113
|
-
const
|
|
114
|
-
const html = await (0, html_render_1.GenerateHtml)(ExportReadyTemplate, {
|
|
115
|
-
userName: user.name || user.email,
|
|
116
|
-
downloadLink: buildDeliveryLink(exportRecord),
|
|
117
|
-
expiresIn: `${export_jobs_1.EXPORT_TTL_MS / time_1.MILLISECONDS_PER_HOUR} hours`,
|
|
118
|
-
partial: result?.partial ?? false,
|
|
119
|
-
failedSources: (result?.failures ?? []).map((failure) => failure.source),
|
|
120
|
-
});
|
|
125
|
+
const html = await (0, html_render_1.GenerateHtml)(ExportReadyTemplate, exportReadyEmailData(user, exportRecord), user.language);
|
|
121
126
|
const sent = await (0, interface_email_1.Send)({
|
|
122
127
|
to: user.email,
|
|
123
|
-
subject:
|
|
128
|
+
subject: (0, email_subjects_1.builtInEmailSubject)("exportReady", user.language),
|
|
124
129
|
html,
|
|
125
130
|
});
|
|
126
131
|
(0, interface_api_util_1.assert)(sent.success, 500, `Failed to send export email: ${sent.error?.message}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-jobs.js","sourceRoot":"","sources":["../../src/utils/export-jobs.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"export-jobs.js","sourceRoot":"","sources":["../../src/utils/export-jobs.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { get } from "@nuxt/ui/runtime/utils/index.js";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// A row field is named `{{row.<field>}}` (a dotted path reaching into it), as
|
|
4
|
+
// the page's values are `{{params.X}}` / `{{query.X}}`, filled elsewhere.
|
|
5
|
+
const ROW_TOKEN = /\{\{\s*row\.([^{}\s]+)\s*\}\}/g;
|
|
6
|
+
|
|
7
|
+
/** Whether a template names a field of a row (`{{row.X}}`). */
|
|
8
|
+
export const namesRowField = (template: string): boolean =>
|
|
9
|
+
new RegExp(ROW_TOKEN.source).test(template);
|
|
10
|
+
|
|
11
|
+
const rowValue = (data: Record<string, unknown>, key: string): string => {
|
|
12
|
+
const value: unknown = get(data, key);
|
|
13
|
+
return value === undefined || value === null ? "" : String(value);
|
|
14
|
+
};
|
|
15
|
+
|
|
4
16
|
// A colon is a valid path character (RFC 3986 pchar) and the API router
|
|
5
17
|
// matches path parameters without decoding them: tenant-scoped ids
|
|
6
18
|
// ("default:<uuid>") must reach a `/:id/...` route as they are.
|
|
@@ -11,7 +23,7 @@ const SAFE_SCHEMES: ReadonlySet<string> = new Set(["http", "https", "mailto"]);
|
|
|
11
23
|
const REFUSED_URL = "";
|
|
12
24
|
|
|
13
25
|
// The colon survives the encoding, so a template starting with a field
|
|
14
|
-
// (`{website}`) takes its scheme from row data: `javascript:` must not reach
|
|
26
|
+
// (`{{row.website}}`) takes its scheme from row data: `javascript:` must not reach
|
|
15
27
|
// a link or `location.href`.
|
|
16
28
|
function hasSafeScheme(url: string): boolean {
|
|
17
29
|
const scheme = SCHEME_PATTERN.exec(url)?.[1];
|
|
@@ -19,19 +31,25 @@ function hasSafeScheme(url: string): boolean {
|
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
/**
|
|
22
|
-
* Fills `{field}`
|
|
23
|
-
* result with a scheme other than http,
|
|
34
|
+
* Fills the `{{row.field}}` tokens of a URL template with encoded row values,
|
|
35
|
+
* a field the row lacks left empty. A result with a scheme other than http,
|
|
36
|
+
* https or mailto comes back empty.
|
|
24
37
|
*/
|
|
25
38
|
export const interpolateUrl = (
|
|
26
39
|
url: string,
|
|
27
40
|
data: Record<string, unknown>,
|
|
28
41
|
): string => {
|
|
29
|
-
const interpolated = url.replace(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
return "";
|
|
33
|
-
}
|
|
34
|
-
return encodeURIComponent(String(value)).replace(ENCODED_COLON, ":");
|
|
35
|
-
});
|
|
42
|
+
const interpolated = url.replace(ROW_TOKEN, (_, key: string) =>
|
|
43
|
+
encodeURIComponent(rowValue(data, key)).replace(ENCODED_COLON, ":"),
|
|
44
|
+
);
|
|
36
45
|
return hasSafeScheme(interpolated) ? interpolated : REFUSED_URL;
|
|
37
46
|
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Fills the `{{row.field}}` tokens of a plain value (a file name) with the
|
|
50
|
+
* row's values as they are, a field the row lacks left empty.
|
|
51
|
+
*/
|
|
52
|
+
export const interpolateRowText = (
|
|
53
|
+
text: string,
|
|
54
|
+
data: Record<string, unknown>,
|
|
55
|
+
): string => text.replace(ROW_TOKEN, (_, key: string) => rowValue(data, key));
|
|
@@ -12,6 +12,22 @@ export interface DmsLogoSources {
|
|
|
12
12
|
dark: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* What the footer of the public and auth pages shows besides its links
|
|
17
|
+
* (`registerFooterLink`).
|
|
18
|
+
*/
|
|
19
|
+
export interface DmsFooterConfig {
|
|
20
|
+
/**
|
|
21
|
+
* Name after the `©`, a literal or a `$`-prefixed i18n key; `false` drops
|
|
22
|
+
* the copyright.
|
|
23
|
+
*/
|
|
24
|
+
copyright?: string | false;
|
|
25
|
+
/** The GitHub "Star" button of the DMS repository. */
|
|
26
|
+
githubStar?: boolean;
|
|
27
|
+
/** The language picker. */
|
|
28
|
+
localeSwitcher?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
15
31
|
export interface DmsBrandingConfig {
|
|
16
32
|
/**
|
|
17
33
|
* Absent when an instance ships no logo of its own; the layer's app config
|
|
@@ -21,6 +37,7 @@ export interface DmsBrandingConfig {
|
|
|
21
37
|
default: DmsLogoSources;
|
|
22
38
|
collapsed: DmsLogoSources;
|
|
23
39
|
};
|
|
40
|
+
footer?: DmsFooterConfig;
|
|
24
41
|
}
|
|
25
42
|
|
|
26
43
|
export interface DmsAppConfig {
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { BreadcrumbItem, DropdownMenuItem } from "@nuxt/ui";
|
|
3
|
+
import { onMounted, ref } from "vue";
|
|
3
4
|
import NotificationPopover from "../notification/NotificationPopover.vue";
|
|
4
5
|
import QuickActionsPopover from "./QuickActionsPopover.vue";
|
|
5
6
|
import { useSidebarState } from "./sidebarState";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
isPlainDataHeaderAction,
|
|
9
|
+
useHeaderActionControls,
|
|
10
|
+
} from "../../composables/layout/useHeaderActionControls";
|
|
7
11
|
import { usePageRecordLabel } from "#dms-core/app/composables/page/usePageRecordLabel";
|
|
8
12
|
import type { CategoryInfo } from "../../../types/page";
|
|
9
13
|
import { resolveModuleLandingSlug } from "../../../utils/moduleLanding";
|
|
@@ -30,7 +34,19 @@ const { open: sidebarOpen, collapsed: sidebarCollapsed } = useSidebarState();
|
|
|
30
34
|
|
|
31
35
|
// Modules register their own header buttons through this generic registry, so
|
|
32
36
|
// core has no knowledge of any specific module.
|
|
33
|
-
const { actions:
|
|
37
|
+
const { actions: visibleActions } = useHeaderActions();
|
|
38
|
+
// A `.client` plugin's action is missing from the server markup: drawn while
|
|
39
|
+
// hydrating, its button would be patched onto the next one's node. It joins
|
|
40
|
+
// once the header has mounted.
|
|
41
|
+
const isMounted = ref(false);
|
|
42
|
+
onMounted(() => {
|
|
43
|
+
isMounted.value = true;
|
|
44
|
+
});
|
|
45
|
+
const registeredActions = computed(() =>
|
|
46
|
+
isMounted.value
|
|
47
|
+
? visibleActions.value
|
|
48
|
+
: visibleActions.value.filter(isPlainDataHeaderAction),
|
|
49
|
+
);
|
|
34
50
|
|
|
35
51
|
// The builder button keeps its fixed slot first in the toolbar, and only
|
|
36
52
|
// exists when a module (the Builder) registers the action that drives it.
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "../../utils/github-repository";
|
|
7
7
|
|
|
8
8
|
const EXTERNAL_LINK_PATTERN = /^https?:\/\//;
|
|
9
|
+
const DEFAULT_COPYRIGHT = "Antelope";
|
|
9
10
|
const GITHUB_REPOSITORY_API = `https://api.github.com/repos/${GITHUB_REPOSITORY}`;
|
|
10
11
|
const GITHUB_STARS_CACHE_KEY = "dms-github-stars";
|
|
11
12
|
// GitHub allows 60 anonymous API calls an hour per visitor: the count is
|
|
@@ -15,6 +16,17 @@ const GITHUB_STARS_CACHE_TTL_MS = 60 * 60 * 1000;
|
|
|
15
16
|
const { locale, setLocale, t } = useI18n();
|
|
16
17
|
const { uniqueLocales } = useUniqueLocales();
|
|
17
18
|
const { links } = useFooterLinks();
|
|
19
|
+
const { processI18n } = useTranslation();
|
|
20
|
+
const footer = computed(() => useDmsAppConfig().branding.footer ?? {});
|
|
21
|
+
const copyright = computed(() =>
|
|
22
|
+
footer.value.copyright === false
|
|
23
|
+
? null
|
|
24
|
+
: processI18n(footer.value.copyright ?? DEFAULT_COPYRIGHT),
|
|
25
|
+
);
|
|
26
|
+
const showsGithubStar = computed(() => footer.value.githubStar !== false);
|
|
27
|
+
const showsLocaleSwitcher = computed(
|
|
28
|
+
() => footer.value.localeSwitcher !== false,
|
|
29
|
+
);
|
|
18
30
|
|
|
19
31
|
const localesOptions: SelectItem[] = uniqueLocales.value.map((lang) => ({
|
|
20
32
|
label: lang.name,
|
|
@@ -87,6 +99,7 @@ async function loadGithubStars(): Promise<void> {
|
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
onMounted(async () => {
|
|
102
|
+
if (!showsGithubStar.value) return;
|
|
90
103
|
await loadGithubStars();
|
|
91
104
|
githubStarsSettled.value = true;
|
|
92
105
|
});
|
|
@@ -103,7 +116,7 @@ async function onLocaleChange(code: unknown) {
|
|
|
103
116
|
<footer
|
|
104
117
|
class="border-default text-dimmed flex flex-wrap items-center gap-x-[18px] gap-y-1.5 border-t px-5 py-3 text-[12.5px] max-sm:justify-center"
|
|
105
118
|
>
|
|
106
|
-
<span>©
|
|
119
|
+
<span v-if="copyright">© {{ copyright }}</span>
|
|
107
120
|
<template v-for="link in links" :key="link.id">
|
|
108
121
|
<a
|
|
109
122
|
v-if="isExternal(link.to)"
|
|
@@ -125,6 +138,7 @@ async function onLocaleChange(code: unknown) {
|
|
|
125
138
|
|
|
126
139
|
<!-- GitHub star button: icon + "Star" | live count, opens the repository. -->
|
|
127
140
|
<a
|
|
141
|
+
v-if="showsGithubStar"
|
|
128
142
|
:href="GITHUB_REPOSITORY_URL"
|
|
129
143
|
target="_blank"
|
|
130
144
|
rel="noopener noreferrer"
|
|
@@ -152,6 +166,7 @@ async function onLocaleChange(code: unknown) {
|
|
|
152
166
|
</a>
|
|
153
167
|
|
|
154
168
|
<USelect
|
|
169
|
+
v-if="showsLocaleSwitcher"
|
|
155
170
|
:model-value="locale"
|
|
156
171
|
:items="localesOptions"
|
|
157
172
|
:aria-label="t('empty_layout.footer.language')"
|
package/frontend-vue/layers/dms-layout/app/build/composables/layout/useHeaderActionControls.ts
CHANGED
|
@@ -15,6 +15,20 @@ export interface HeaderActionControls {
|
|
|
15
15
|
select: (action: HeaderAction) => void;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const ACTION_CALLABLES = ["onSelect", "isVisible", "isActive"] as const;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether the action is plain data, which a universal plugin registers on the
|
|
22
|
+
* server and the client alike, so the server markup holds its button. One
|
|
23
|
+
* carrying a callable belongs in a `.client` plugin: the server never drew
|
|
24
|
+
* it, and the header adds it once mounted rather than during hydration.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export function isPlainDataHeaderAction(action: HeaderAction): boolean {
|
|
29
|
+
return ACTION_CALLABLES.every((key) => typeof action[key] !== "function");
|
|
30
|
+
}
|
|
31
|
+
|
|
18
32
|
/**
|
|
19
33
|
* How the header reads and runs any {@link HeaderAction}: an action linked to
|
|
20
34
|
* a side panel gets its state and its click from the DMS, any other one from
|
|
@@ -9,6 +9,7 @@ interface HStackProps {
|
|
|
9
9
|
distribution?: StackDistribution;
|
|
10
10
|
spacing?: string;
|
|
11
11
|
wrap?: boolean;
|
|
12
|
+
grow?: number;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
const props = withDefaults(defineProps<HStackProps>(), {
|
|
@@ -28,6 +29,7 @@ const props = withDefaults(defineProps<HStackProps>(), {
|
|
|
28
29
|
justifyContent: distributionMap[props.distribution],
|
|
29
30
|
gap: props.spacing,
|
|
30
31
|
'--dms-stack-gap': props.spacing,
|
|
32
|
+
flexGrow: props.grow,
|
|
31
33
|
flexWrap: props.wrap ? 'wrap' : 'nowrap',
|
|
32
34
|
}"
|
|
33
35
|
class="dms-hstack w-full"
|
|
@@ -35,3 +37,17 @@ const props = withDefaults(defineProps<HStackProps>(), {
|
|
|
35
37
|
<slot />
|
|
36
38
|
</div>
|
|
37
39
|
</template>
|
|
40
|
+
|
|
41
|
+
<style>
|
|
42
|
+
/* A stack fills what holds it, except along a row, where it is one item among
|
|
43
|
+
its siblings: sibling stacks share the row (in their `grow` ratio), and in
|
|
44
|
+
a row with a Spacer they keep their content width so the Spacer can push
|
|
45
|
+
them aside. An inline `grow` wins over both. */
|
|
46
|
+
.dms-hstack > :is(.dms-hstack, .dms-vstack) {
|
|
47
|
+
width: auto;
|
|
48
|
+
flex: 1 1 0%;
|
|
49
|
+
}
|
|
50
|
+
.dms-hstack:has(> .dms-spacer) > :is(.dms-hstack, .dms-vstack) {
|
|
51
|
+
flex: 0 1 auto;
|
|
52
|
+
}
|
|
53
|
+
</style>
|
|
@@ -8,6 +8,7 @@ interface VStackProps {
|
|
|
8
8
|
alignment?: StackAlignment;
|
|
9
9
|
distribution?: StackDistribution;
|
|
10
10
|
spacing?: string;
|
|
11
|
+
grow?: number;
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
const props = withDefaults(defineProps<VStackProps>(), {
|
|
@@ -26,6 +27,7 @@ const props = withDefaults(defineProps<VStackProps>(), {
|
|
|
26
27
|
justifyContent: distributionMap[props.distribution],
|
|
27
28
|
gap: props.spacing,
|
|
28
29
|
'--dms-stack-gap': props.spacing,
|
|
30
|
+
flexGrow: props.grow,
|
|
29
31
|
}"
|
|
30
32
|
class="dms-vstack w-full"
|
|
31
33
|
>
|
|
@@ -7,7 +7,8 @@ export interface FooterLink {
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The Antelope links every footer starts with. A layer replaces one by
|
|
10
|
-
* registering an entry under the same id
|
|
10
|
+
* registering an entry under the same id, or removes it with
|
|
11
|
+
* `unregisterFooterLink`.
|
|
11
12
|
*/
|
|
12
13
|
const DEFAULT_FOOTER_LINKS: FooterLink[] = [
|
|
13
14
|
{
|
|
@@ -43,6 +44,15 @@ export function registerFooterLink(link: FooterLink): void {
|
|
|
43
44
|
links.value = next;
|
|
44
45
|
}
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Removes a link from the footer, one of the Antelope defaults
|
|
49
|
+
* (`dms:documentation`, `dms:website`) or another layer's. An id never
|
|
50
|
+
* registered is ignored.
|
|
51
|
+
*/
|
|
52
|
+
export function unregisterFooterLink(id: string): void {
|
|
53
|
+
links.value = links.value.filter((entry) => entry.id !== id);
|
|
54
|
+
}
|
|
55
|
+
|
|
46
56
|
export function useFooterLinks() {
|
|
47
57
|
const sortedLinks = computed<FooterLink[]>(() =>
|
|
48
58
|
[...links.value].sort(compareByOrder),
|
|
@@ -32,7 +32,7 @@ interface RoleEditorHandle {
|
|
|
32
32
|
|
|
33
33
|
const NAME_REQUIRED = "$page.settings.roles.error.name_required";
|
|
34
34
|
const NAME_TAKEN = "$page.settings.roles.error.name_taken";
|
|
35
|
-
const ROLE_DELETE_CONFIRM_URL = `${ROLES_API_PATH}/{id}/delete-confirm`;
|
|
35
|
+
const ROLE_DELETE_CONFIRM_URL = `${ROLES_API_PATH}/{{row.id}}/delete-confirm`;
|
|
36
36
|
|
|
37
37
|
const api = useRolesApi();
|
|
38
38
|
const { t } = useI18n();
|
|
@@ -51,7 +51,7 @@ const invitation = computed(() => {
|
|
|
51
51
|
</script>
|
|
52
52
|
|
|
53
53
|
<template>
|
|
54
|
-
<EmailLayout :title="title" :lang="locale">
|
|
54
|
+
<EmailLayout :title="title" :lang="locale" public-logo-only>
|
|
55
55
|
<EText :style="emailStyles.text">
|
|
56
56
|
{{ t("emails.admin_invite.greeting") }}
|
|
57
57
|
</EText>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "vue-i18n";
|
|
2
3
|
import EmailLayout from "./components/EmailLayout.vue";
|
|
3
4
|
import { emailStyles } from "./components/index";
|
|
4
5
|
|
|
@@ -6,22 +7,27 @@ defineProps<{
|
|
|
6
7
|
userName: string;
|
|
7
8
|
newEmail: string;
|
|
8
9
|
}>();
|
|
10
|
+
|
|
11
|
+
const { t, locale } = useI18n();
|
|
9
12
|
</script>
|
|
10
13
|
|
|
11
14
|
<template>
|
|
12
|
-
<EmailLayout
|
|
13
|
-
|
|
15
|
+
<EmailLayout
|
|
16
|
+
:title="t('emails.email_change_notice.title')"
|
|
17
|
+
:lang="locale"
|
|
18
|
+
public-logo-only
|
|
19
|
+
>
|
|
20
|
+
<EText :style="emailStyles.text">
|
|
21
|
+
{{ t("emails.common.greeting", { name: userName }) }}
|
|
22
|
+
</EText>
|
|
14
23
|
|
|
15
24
|
<EText :style="emailStyles.text">
|
|
16
|
-
|
|
17
|
-
{{ newEmail }}. The change takes effect once that address is confirmed.
|
|
25
|
+
{{ t("emails.email_change_notice.body", { email: newEmail }) }}
|
|
18
26
|
</EText>
|
|
19
27
|
|
|
20
28
|
<template #footer>
|
|
21
29
|
<EText :style="emailStyles.footer">
|
|
22
|
-
|
|
23
|
-
sessions in Security: your account keeps this address until the new
|
|
24
|
-
one is confirmed.
|
|
30
|
+
{{ t("emails.email_change_notice.footer") }}
|
|
25
31
|
</EText>
|
|
26
32
|
</template>
|
|
27
33
|
</EmailLayout>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "vue-i18n";
|
|
2
3
|
import EmailLayout from "./components/EmailLayout.vue";
|
|
3
4
|
import EmailOTP from "./components/EmailOTP.vue";
|
|
4
5
|
import { emailStyles } from "./components/index";
|
|
@@ -8,24 +9,34 @@ defineProps<{
|
|
|
8
9
|
newEmail: string;
|
|
9
10
|
code: string;
|
|
10
11
|
expiresIn: string;
|
|
12
|
+
expiresInHours: number;
|
|
11
13
|
}>();
|
|
14
|
+
|
|
15
|
+
const { t, locale } = useI18n();
|
|
12
16
|
</script>
|
|
13
17
|
|
|
14
18
|
<template>
|
|
15
|
-
<EmailLayout
|
|
16
|
-
|
|
19
|
+
<EmailLayout
|
|
20
|
+
:title="t('emails.email_change_verification.title')"
|
|
21
|
+
:lang="locale"
|
|
22
|
+
public-logo-only
|
|
23
|
+
>
|
|
24
|
+
<EText :style="emailStyles.text">
|
|
25
|
+
{{ t("emails.common.greeting", { name: userName }) }}
|
|
26
|
+
</EText>
|
|
17
27
|
|
|
18
28
|
<EText :style="emailStyles.text">
|
|
19
|
-
|
|
20
|
-
account:
|
|
29
|
+
{{ t("emails.email_change_verification.body", { email: newEmail }) }}
|
|
21
30
|
</EText>
|
|
22
31
|
|
|
23
|
-
<EmailOTP
|
|
32
|
+
<EmailOTP
|
|
33
|
+
:code="code"
|
|
34
|
+
:expires-in="t('emails.common.hours', expiresInHours)"
|
|
35
|
+
/>
|
|
24
36
|
|
|
25
37
|
<template #footer>
|
|
26
38
|
<EText :style="emailStyles.footer">
|
|
27
|
-
|
|
28
|
-
your account keeps its current address.
|
|
39
|
+
{{ t("emails.email_change_verification.footer") }}
|
|
29
40
|
</EText>
|
|
30
41
|
</template>
|
|
31
42
|
</EmailLayout>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { I18nT, useI18n } from "vue-i18n";
|
|
2
3
|
import EmailLayout from "./components/EmailLayout.vue";
|
|
3
4
|
import EmailButton from "./components/EmailButton.vue";
|
|
4
5
|
import { emailStyles } from "./components/index";
|
|
@@ -7,38 +8,48 @@ defineProps<{
|
|
|
7
8
|
userName: string;
|
|
8
9
|
downloadLink: string;
|
|
9
10
|
expiresIn: string;
|
|
11
|
+
expiresInHours: number;
|
|
10
12
|
partial: boolean;
|
|
11
13
|
failedSources: string[];
|
|
12
14
|
}>();
|
|
15
|
+
|
|
16
|
+
const { t, locale } = useI18n();
|
|
13
17
|
</script>
|
|
14
18
|
|
|
15
19
|
<template>
|
|
16
|
-
<EmailLayout
|
|
17
|
-
|
|
20
|
+
<EmailLayout
|
|
21
|
+
:title="t('emails.export_ready.title')"
|
|
22
|
+
:lang="locale"
|
|
23
|
+
public-logo-only
|
|
24
|
+
>
|
|
25
|
+
<EText :style="emailStyles.text">
|
|
26
|
+
{{ t("emails.common.greeting", { name: userName }) }}
|
|
27
|
+
</EText>
|
|
18
28
|
|
|
19
29
|
<EText :style="emailStyles.text">
|
|
20
|
-
|
|
21
|
-
archive:
|
|
30
|
+
{{ t("emails.export_ready.body") }}
|
|
22
31
|
</EText>
|
|
23
32
|
|
|
24
33
|
<EmailButton
|
|
25
34
|
:href="downloadLink"
|
|
26
|
-
:expires-in="
|
|
27
|
-
expires-label="
|
|
35
|
+
:expires-in="t('emails.common.hours', expiresInHours)"
|
|
36
|
+
:expires-label="t('emails.export_ready.expires_label')"
|
|
28
37
|
>
|
|
29
|
-
|
|
38
|
+
{{ t("emails.export_ready.button") }}
|
|
30
39
|
</EmailButton>
|
|
31
40
|
|
|
32
41
|
<EText v-if="partial" :style="emailStyles.text">
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
<I18nT keypath="emails.export_ready.partial" scope="global">
|
|
43
|
+
<template #incomplete>
|
|
44
|
+
<strong>{{ t("emails.export_ready.incomplete") }}</strong>
|
|
45
|
+
</template>
|
|
46
|
+
<template #sources>{{ failedSources.join(", ") }}</template>
|
|
47
|
+
</I18nT>
|
|
37
48
|
</EText>
|
|
38
49
|
|
|
39
50
|
<template #footer>
|
|
40
51
|
<EText :style="emailStyles.footer">
|
|
41
|
-
|
|
52
|
+
{{ t("emails.export_ready.footer") }}
|
|
42
53
|
</EText>
|
|
43
54
|
</template>
|
|
44
55
|
</EmailLayout>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { useI18n } from "vue-i18n";
|
|
2
3
|
import EmailLayout from "./components/EmailLayout.vue";
|
|
3
4
|
import EmailOTP from "./components/EmailOTP.vue";
|
|
4
5
|
import { emailStyles } from "./components/index";
|
|
@@ -7,24 +8,34 @@ defineProps<{
|
|
|
7
8
|
userName: string;
|
|
8
9
|
resetCode: string;
|
|
9
10
|
expiresIn: string;
|
|
11
|
+
expiresInHours: number;
|
|
10
12
|
}>();
|
|
13
|
+
|
|
14
|
+
const { t, locale } = useI18n();
|
|
11
15
|
</script>
|
|
12
16
|
|
|
13
17
|
<template>
|
|
14
|
-
<EmailLayout
|
|
15
|
-
|
|
18
|
+
<EmailLayout
|
|
19
|
+
:title="t('emails.reset_password.title')"
|
|
20
|
+
:lang="locale"
|
|
21
|
+
public-logo-only
|
|
22
|
+
>
|
|
23
|
+
<EText :style="emailStyles.text">
|
|
24
|
+
{{ t("emails.common.greeting", { name: userName }) }}
|
|
25
|
+
</EText>
|
|
16
26
|
|
|
17
27
|
<EText :style="emailStyles.text">
|
|
18
|
-
|
|
19
|
-
below to reset your password:
|
|
28
|
+
{{ t("emails.reset_password.body") }}
|
|
20
29
|
</EText>
|
|
21
30
|
|
|
22
|
-
<EmailOTP
|
|
31
|
+
<EmailOTP
|
|
32
|
+
:code="resetCode"
|
|
33
|
+
:expires-in="t('emails.common.hours', expiresInHours)"
|
|
34
|
+
/>
|
|
23
35
|
|
|
24
36
|
<template #footer>
|
|
25
37
|
<EText :style="emailStyles.footer">
|
|
26
|
-
|
|
27
|
-
email. Your password will remain unchanged.
|
|
38
|
+
{{ t("emails.reset_password.footer") }}
|
|
28
39
|
</EText>
|
|
29
40
|
</template>
|
|
30
41
|
</EmailLayout>
|