diff --git a/web/src/admin/admin-overview/cards/RecentEventsCard.ts b/web/src/admin/admin-overview/cards/RecentEventsCard.ts index 8d07af8d7568..0d1b744e567a 100644 --- a/web/src/admin/admin-overview/cards/RecentEventsCard.ts +++ b/web/src/admin/admin-overview/cards/RecentEventsCard.ts @@ -7,6 +7,7 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EventWithContext } from "#common/events"; +import { EntityLabel } from "#common/i18n/nouns"; import { actionToLabel } from "#common/labels"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -16,7 +17,7 @@ import { EventGeo, renderEventUser } from "#admin/events/utils"; import { Event, EventsApi } from "@goauthentik/api"; -import { msg } from "@lit/localize"; +import { msg, str } from "@lit/localize"; import { css, CSSResult, html, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; @@ -28,6 +29,11 @@ export class RecentEventsCard extends Table { public override ariaLabel = msg("Recent events"); public override label = msg("Events"); + protected override entityLabel: EntityLabel = { + singular: msg("event"), + plural: msg("events"), + }; + @property() order = "-created"; @@ -89,7 +95,7 @@ export class RecentEventsCard extends Table { return super.renderEmpty( html`${msg("No Events found.")} + >${msg(msg(str`No ${this.entityLabel.plural.toLowerCase()} found.`))}
${msg("No matching events could be found.")}
`, ); diff --git a/web/src/admin/admin-settings/AdminSettingsForm.ts b/web/src/admin/admin-settings/AdminSettingsForm.ts index 9f1bb31bc673..89fbbc7a0c1e 100644 --- a/web/src/admin/admin-settings/AdminSettingsForm.ts +++ b/web/src/admin/admin-settings/AdminSettingsForm.ts @@ -46,9 +46,8 @@ export class AdminSettingsForm extends Form { @property({ attribute: false }) public settings!: Settings; - getSuccessMessage(): string { - return msg("Successfully updated settings."); - } + protected override readonly actionName = "save"; + protected override entityLabel = msg("settings"); async send(settingsRequest: SettingsRequest): Promise { const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({ diff --git a/web/src/admin/applications/ApplicationCheckAccessForm.ts b/web/src/admin/applications/ApplicationCheckAccessForm.ts index a0738bf19918..927f13bec8b3 100644 --- a/web/src/admin/applications/ApplicationCheckAccessForm.ts +++ b/web/src/admin/applications/ApplicationCheckAccessForm.ts @@ -32,9 +32,9 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> { @property({ attribute: false }) request?: number; - getSuccessMessage(): string { - return msg("Successfully sent test-request."); - } + protected override readonly actionName = "send"; + + protected override entityLabel = msg("test-request"); async send(data: { forUser: number }): Promise { this.request = data.forUser; diff --git a/web/src/admin/applications/ApplicationForm.ts b/web/src/admin/applications/ApplicationForm.ts index 20eb072c8ade..bdb7f4fb6fa1 100644 --- a/web/src/admin/applications/ApplicationForm.ts +++ b/web/src/admin/applications/ApplicationForm.ts @@ -57,11 +57,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm { applicationRequest.backchannelProviders = this.backchannelProviders.map((p) => p.pk); diff --git a/web/src/admin/applications/ApplicationListPage.ts b/web/src/admin/applications/ApplicationListPage.ts index f9bda299184a..d25382a667fb 100644 --- a/web/src/admin/applications/ApplicationListPage.ts +++ b/web/src/admin/applications/ApplicationListPage.ts @@ -45,7 +45,15 @@ export const applicationListStyle = css` @customElement("ak-application-list") export class ApplicationListPage extends WithBrandConfig(TablePage) { protected override searchEnabled = true; - public pageTitle = msg("Applications"); + protected override entityLabel = { + singular: msg("Application"), + plural: msg("Applications"), + }; + + protected override get searchPlaceholder() { + return msg("Search for an application by name or group..."); + } + public get pageDescription() { return msg( str`External applications that use ${this.brandingTitle} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`, @@ -132,7 +140,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage) html`${item.providerObj?.verboseName || msg("-")}`, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Application")} @@ -173,18 +181,20 @@ export class ApplicationListPage extends WithBrandConfig(TablePage) - ${msg("Create")} - ${msg("Create Application")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + `; } renderToolbar(): TemplateResult { return html` ${super.renderToolbar()} { return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate(); diff --git a/web/src/admin/applications/ApplicationViewPage.ts b/web/src/admin/applications/ApplicationViewPage.ts index d51e3e215784..ef811956911f 100644 --- a/web/src/admin/applications/ApplicationViewPage.ts +++ b/web/src/admin/applications/ApplicationViewPage.ts @@ -12,6 +12,9 @@ import "#elements/buttons/SpinnerButton/ak-spinner-button"; import { DEFAULT_CONFIG } from "#common/api/config"; import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; @@ -60,6 +63,11 @@ export class ApplicationViewPage extends AKElement { //#region State + protected entityLabel: EntityLabel = { + singular: msg("Application"), + plural: msg("Applications"), + }; + @state() protected application?: Application; @@ -219,9 +227,11 @@ export class ApplicationViewPage extends AKElement {
- ${msg("Update")} + ${ActionTenseRecord.apply.present} - ${msg("Update Application")} + ${formatEditMessage(this.entityLabel)} - ${msg("Edit")} + ${formatEditMessage(this.entityLabel)}
diff --git a/web/src/admin/applications/ProviderSelectModal.ts b/web/src/admin/applications/ProviderSelectModal.ts index 4c83a3923f8e..cff9e902ea78 100644 --- a/web/src/admin/applications/ProviderSelectModal.ts +++ b/web/src/admin/applications/ProviderSelectModal.ts @@ -1,6 +1,7 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; import { TableModal } from "#elements/table/TableModal"; @@ -17,6 +18,11 @@ export class ProviderSelectModal extends TableModal { checkbox = true; checkboxChip = true; + protected override entityLabel: EntityLabel = { + singular: msg("provider"), + plural: msg("providers"), + }; + protected override searchEnabled = true; @property({ type: Boolean }) diff --git a/web/src/admin/applications/entitlements/ApplicationEntitlementForm.ts b/web/src/admin/applications/entitlements/ApplicationEntitlementForm.ts index b546be9d4673..eb477623736b 100644 --- a/web/src/admin/applications/entitlements/ApplicationEntitlementForm.ts +++ b/web/src/admin/applications/entitlements/ApplicationEntitlementForm.ts @@ -29,12 +29,7 @@ export class ApplicationEntitlementForm extends ModelForm { order = "order"; + protected override entityLabel: EntityLabel = { + singular: msg("Entitlement"), + plural: msg("Entitlements"), + }; + async apiEndpoint(): Promise> { return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsList({ ...(await this.defaultEndpointConfig()), @@ -76,8 +82,8 @@ export class ApplicationEntitlementsPage extends Table { return [ html`${item.name}`, html` - ${msg("Update")} - ${msg("Update Entitlement")} + ${this.updateEntityLabel} + ${this.editEntityLabel} { > @@ -126,12 +132,12 @@ export class ApplicationEntitlementsPage extends Table { renderToolbar(): TemplateResult { return html` - ${msg("Create")} - ${msg("Create Entitlement")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} `; } diff --git a/web/src/admin/blueprints/BlueprintForm.ts b/web/src/admin/blueprints/BlueprintForm.ts index c9281dd9dcd4..67693dd48e0b 100644 --- a/web/src/admin/blueprints/BlueprintForm.ts +++ b/web/src/admin/blueprints/BlueprintForm.ts @@ -46,11 +46,7 @@ export class BlueprintForm extends ModelForm { return inst; } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated instance.") - : msg("Successfully created instance."); - } + protected override entityLabel = msg("instance"); static styles: CSSResult[] = [...super.styles, PFContent]; diff --git a/web/src/admin/blueprints/BlueprintListPage.ts b/web/src/admin/blueprints/BlueprintListPage.ts index d7341351738a..7cf2e5c25438 100644 --- a/web/src/admin/blueprints/BlueprintListPage.ts +++ b/web/src/admin/blueprints/BlueprintListPage.ts @@ -47,7 +47,10 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string { @customElement("ak-blueprint-list") export class BlueprintListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Blueprints"); + protected override entityLabel = { + singular: msg("Blueprint Instance"), + plural: msg("Blueprint Instances"), + }; public pageDescription = msg("Automate and template configuration within authentik."); public pageIcon = "pf-icon pf-icon-blueprint"; @@ -150,7 +153,7 @@ export class BlueprintListPage extends TablePage { html``, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Blueprint")} + `; } diff --git a/web/src/admin/brands/BrandForm.ts b/web/src/admin/brands/BrandForm.ts index 12bd2908501b..59abe1e71625 100644 --- a/web/src/admin/brands/BrandForm.ts +++ b/web/src/admin/brands/BrandForm.ts @@ -41,11 +41,7 @@ export class BrandForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated brand.") - : msg("Successfully created brand."); - } + protected override entityLabel = msg("brand"); async send(data: Brand): Promise { data.attributes ??= {}; diff --git a/web/src/admin/brands/BrandListPage.ts b/web/src/admin/brands/BrandListPage.ts index a6ef20d7efd4..004429ebccb7 100644 --- a/web/src/admin/brands/BrandListPage.ts +++ b/web/src/admin/brands/BrandListPage.ts @@ -21,8 +21,14 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-brand-list") export class BrandListPage extends TablePage { protected override searchEnabled = true; - public override searchPlaceholder = msg("Search by domain or brand name..."); - public pageTitle = msg("Brands"); + protected override entityLabel = { + singular: msg("Brand"), + plural: msg("Brands"), + }; + protected get searchPlaceholder() { + return msg("Search by domain or brand name..."); + } + public pageDescription = msg("Configure visual settings and defaults for different domains."); public pageIcon = "pf-icon pf-icon-tenant"; @@ -79,7 +85,7 @@ export class BrandListPage extends TablePage { html``, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Brand")} + `; } diff --git a/web/src/admin/crypto/CertificateGenerateForm.ts b/web/src/admin/crypto/CertificateGenerateForm.ts index 4388fe9b8f35..77527e5f5abd 100644 --- a/web/src/admin/crypto/CertificateGenerateForm.ts +++ b/web/src/admin/crypto/CertificateGenerateForm.ts @@ -17,9 +17,8 @@ import { customElement } from "lit/decorators.js"; @customElement("ak-crypto-certificate-generate-form") export class CertificateKeyPairForm extends Form { - getSuccessMessage(): string { - return msg("Successfully generated certificate-key pair."); - } + protected override readonly actionName = "generate"; + protected override entityLabel = msg("certificate-key pair"); async send(data: CertificateGenerationRequest): Promise { return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsGenerateCreate({ diff --git a/web/src/admin/crypto/CertificateKeyPairForm.ts b/web/src/admin/crypto/CertificateKeyPairForm.ts index 0f6bf6556b14..6136e31ab092 100644 --- a/web/src/admin/crypto/CertificateKeyPairForm.ts +++ b/web/src/admin/crypto/CertificateKeyPairForm.ts @@ -21,11 +21,7 @@ export class CertificateKeyPairForm extends ModelForm { if (this.instance) { diff --git a/web/src/admin/crypto/CertificateKeyPairListPage.ts b/web/src/admin/crypto/CertificateKeyPairListPage.ts index 2faaee6bc30e..2312039b2dfe 100644 --- a/web/src/admin/crypto/CertificateKeyPairListPage.ts +++ b/web/src/admin/crypto/CertificateKeyPairListPage.ts @@ -33,7 +33,10 @@ export class CertificateKeyPairListPage extends TablePage { clearOnRefresh = true; protected override searchEnabled = true; - public pageTitle = msg("Certificate-Key Pairs"); + protected override entityLabel = { + singular: msg("Certificate-Key Pair"), + plural: msg("Certificate-Key Pairs"), + }; public pageDescription = msg( "Import certificates of external providers or create certificates to sign requests with.", ); @@ -114,7 +117,7 @@ export class CertificateKeyPairListPage extends TablePage { html` ${item.certExpiry?.toLocaleString()} `, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Certificate-Key Pair")} diff --git a/web/src/admin/enterprise/EnterpriseLicenseForm.ts b/web/src/admin/enterprise/EnterpriseLicenseForm.ts index f9fb59b3fe0d..321e0616db4e 100644 --- a/web/src/admin/enterprise/EnterpriseLicenseForm.ts +++ b/web/src/admin/enterprise/EnterpriseLicenseForm.ts @@ -25,11 +25,7 @@ export class EnterpriseLicenseForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated license.") - : msg("Successfully created license."); - } + protected override entityLabel = msg("license"); async load(): Promise { this.installID = ( diff --git a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts index 51967a6cd29c..bc914ceb1092 100644 --- a/web/src/admin/enterprise/EnterpriseLicenseListPage.ts +++ b/web/src/admin/enterprise/EnterpriseLicenseListPage.ts @@ -41,7 +41,10 @@ export class EnterpriseLicenseListPage extends TablePage { clearOnRefresh = true; protected override searchEnabled = true; - public pageTitle = msg("Licenses"); + protected override entityLabel = { + singular: msg("License"), + plural: msg("Licenses"), + }; public pageDescription = msg("Manage enterprise licenses"); public pageIcon = "pf-icon pf-icon-key"; @@ -221,7 +224,7 @@ export class EnterpriseLicenseListPage extends TablePage { html` ${item.expiry?.toLocaleString()} `, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update License")} diff --git a/web/src/admin/events/EventListPage.ts b/web/src/admin/events/EventListPage.ts index fc71f84bc789..97dd986be0c7 100644 --- a/web/src/admin/events/EventListPage.ts +++ b/web/src/admin/events/EventListPage.ts @@ -28,7 +28,15 @@ export class EventListPage extends WithLicenseSummary(TablePage) { expandable = true; supportsQL = true; - public pageTitle = msg("Event Log"); + protected override entityLabel = { + singular: msg("Event Log"), + plural: msg("Event Log"), + }; + + protected override get searchPlaceholder() { + return msg("Search for an event by action or user..."); + } + public pageDescription = ""; public pageIcon = "pf-icon pf-icon-catalog"; diff --git a/web/src/admin/events/RuleForm.ts b/web/src/admin/events/RuleForm.ts index 145cf29826d9..53bd9f066eea 100644 --- a/web/src/admin/events/RuleForm.ts +++ b/web/src/admin/events/RuleForm.ts @@ -41,11 +41,7 @@ export class RuleForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated rule.") - : msg("Successfully created rule."); - } + protected override entityLabel = msg("rule"); async send(data: NotificationRule): Promise { if (this.instance) { diff --git a/web/src/admin/events/RuleListPage.ts b/web/src/admin/events/RuleListPage.ts index 49ced01e5fc9..eceb7f299710 100644 --- a/web/src/admin/events/RuleListPage.ts +++ b/web/src/admin/events/RuleListPage.ts @@ -33,7 +33,10 @@ export class RuleListPage extends TablePage { clearOnRefresh = true; protected override searchEnabled = true; - public pageTitle = msg("Notification Rules"); + protected override entityLabel = { + singular: msg("Notification Rule"), + plural: msg("Notification Rules"), + }; public pageDescription = msg( "Send notifications whenever a specific Event is created and matched by policies.", ); @@ -89,7 +92,7 @@ export class RuleListPage extends TablePage { : msg("-")}`, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Notification Rule")} + `; } diff --git a/web/src/admin/events/TransportForm.ts b/web/src/admin/events/TransportForm.ts index 3ff1a5e4815d..441e72d59c6a 100644 --- a/web/src/admin/events/TransportForm.ts +++ b/web/src/admin/events/TransportForm.ts @@ -47,11 +47,7 @@ export class TransportForm extends ModelForm { @property({ type: Boolean }) showEmail = false; - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated transport.") - : msg("Successfully created transport."); - } + protected override entityLabel = msg("transport"); async send(data: NotificationTransport): Promise { if (this.instance) { diff --git a/web/src/admin/events/TransportListPage.ts b/web/src/admin/events/TransportListPage.ts index 173daf7bbebc..9df084b3aaaa 100644 --- a/web/src/admin/events/TransportListPage.ts +++ b/web/src/admin/events/TransportListPage.ts @@ -27,7 +27,10 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-event-transport-list") export class TransportListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Notification Transports"); + protected override entityLabel = { + singular: msg("Notification Transport"), + plural: msg("Notification Transports"), + }; public pageDescription = msg( "Define how notifications are sent to users, like Email or Webhook.", ); @@ -80,7 +83,7 @@ export class TransportListPage extends TablePage { html`${item.modeVerbose}`, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Notification Transport")} @@ -135,10 +138,12 @@ export class TransportListPage extends TablePage { renderObjectCreate(): TemplateResult { return html` - ${msg("Create")} - ${msg("Create Notification Transport")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + `; } diff --git a/web/src/admin/flows/BoundStagesList.ts b/web/src/admin/flows/BoundStagesList.ts index a04b45a0a49b..f387e0285ed9 100644 --- a/web/src/admin/flows/BoundStagesList.ts +++ b/web/src/admin/flows/BoundStagesList.ts @@ -8,6 +8,7 @@ import "#elements/forms/ModalForm"; import "#elements/forms/ProxyForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -31,6 +32,11 @@ export class BoundStagesList extends Table { order = "order"; + protected override entityLabel: EntityLabel = { + singular: msg("Stage Binding"), + plural: msg("Stage Bindings"), + }; + @property() target?: string; @@ -86,7 +92,7 @@ export class BoundStagesList extends Table { html`${item.stageObj?.name}`, html`${item.stageObj?.verboseName}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.stageObj?.verboseName}`)} { > - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Stage binding")} @@ -140,8 +146,8 @@ export class BoundStagesList extends Table { bindingTarget=${ifDefined(this.target)} > - ${msg("Create")} - ${msg("Create Stage binding")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} + ${msg("Import")} ${msg("Import Flow")} - + `; } @@ -148,8 +163,8 @@ export class FlowListPage extends TablePage { return html` ${super.renderToolbar()} { return new FlowsApi(DEFAULT_CONFIG).flowsInstancesCacheClearCreate(); diff --git a/web/src/admin/flows/FlowViewPage.ts b/web/src/admin/flows/FlowViewPage.ts index 453b810abfa8..db0b8040dd48 100644 --- a/web/src/admin/flows/FlowViewPage.ts +++ b/web/src/admin/flows/FlowViewPage.ts @@ -129,7 +129,7 @@ export class FlowViewPage extends AKElement {
- ${msg("Update")} + ${msg("Update")} ${msg("Update Flow")} diff --git a/web/src/admin/flows/StageBindingForm.ts b/web/src/admin/flows/StageBindingForm.ts index 04836a4bb963..6dbd8aa9e650 100644 --- a/web/src/admin/flows/StageBindingForm.ts +++ b/web/src/admin/flows/StageBindingForm.ts @@ -40,12 +40,7 @@ export class StageBindingForm extends ModelForm { @state() defaultOrder = 0; - getSuccessMessage(): string { - if (this.instance?.pk) { - return msg("Successfully updated binding."); - } - return msg("Successfully created binding."); - } + protected override entityLabel = msg("binding"); send(data: FlowStageBinding): Promise { if (this.instance?.pk) { diff --git a/web/src/admin/groups/GroupForm.ts b/web/src/admin/groups/GroupForm.ts index 6e8bcd3e0f4d..018fb701d606 100644 --- a/web/src/admin/groups/GroupForm.ts +++ b/web/src/admin/groups/GroupForm.ts @@ -48,11 +48,7 @@ export class GroupForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated group.") - : msg("Successfully created group."); - } + protected override entityLabel = msg("group"); async send(data: Group): Promise { data.attributes ??= {}; diff --git a/web/src/admin/groups/GroupListPage.ts b/web/src/admin/groups/GroupListPage.ts index 921e340a0bde..3e87f5815b03 100644 --- a/web/src/admin/groups/GroupListPage.ts +++ b/web/src/admin/groups/GroupListPage.ts @@ -6,6 +6,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; import { TablePage } from "#elements/table/TablePage"; @@ -22,9 +23,15 @@ export class GroupListPage extends TablePage { checkbox = true; clearOnRefresh = true; protected override searchEnabled = true; - public searchPlaceholder = msg("Search for a group by name…"); - public searchLabel = msg("Group Search"); - public pageTitle = msg("Groups"); + protected override entityLabel: EntityLabel = { + singular: msg("Group"), + plural: msg("Groups"), + }; + + protected override get searchPlaceholder() { + return msg("Search for a group by name..."); + } + public pageDescription = msg( "Group users together and give them permissions based on the membership.", ); @@ -82,11 +89,11 @@ export class GroupListPage extends TablePage { html``, html`
- ${msg("Update")} - ${msg("Update Group")} + ${this.updateEntityLabel} + ${this.editEntityLabel} @@ -98,10 +105,12 @@ export class GroupListPage extends TablePage { renderObjectCreate(): TemplateResult { return html` - ${msg("Create Group")} - ${msg("New Group")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + `; } diff --git a/web/src/admin/groups/GroupViewPage.ts b/web/src/admin/groups/GroupViewPage.ts index ecf572b6b058..d505c29b13f2 100644 --- a/web/src/admin/groups/GroupViewPage.ts +++ b/web/src/admin/groups/GroupViewPage.ts @@ -11,6 +11,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -36,6 +39,11 @@ import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; @customElement("ak-group-view") export class GroupViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Group"), + plural: msg("Groups"), + }; + @property({ type: String }) set groupId(id: string) { new CoreApi(DEFAULT_CONFIG) @@ -144,12 +152,14 @@ export class GroupViewPage extends AKElement {
diff --git a/web/src/admin/groups/MemberSelectModal.ts b/web/src/admin/groups/MemberSelectModal.ts index a97e4f98cb12..346197fdb0ab 100644 --- a/web/src/admin/groups/MemberSelectModal.ts +++ b/web/src/admin/groups/MemberSelectModal.ts @@ -2,6 +2,7 @@ import "#components/ak-status-label"; import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, TableColumn, Timestamp } from "#elements/table/Table"; import { TableModal } from "#elements/table/TableModal"; @@ -22,8 +23,15 @@ type UserListRequestFilter = Partial>; @customElement("ak-group-member-select-table") export class MemberSelectTable extends TableModal { - public override searchPlaceholder = msg("Search for users by username or display name..."); - public override searchLabel = msg("Search Users"); + protected override entityLabel: EntityLabel = { + singular: msg("user"), + plural: msg("users"), + }; + + protected override get searchPlaceholder() { + return msg("Search for users by username or display name..."); + } + public override label = msg("Select Users"); static styles = [ ...super.styles, diff --git a/web/src/admin/groups/RelatedGroupList.ts b/web/src/admin/groups/RelatedGroupList.ts index 3afd08963b1d..82a312444a53 100644 --- a/web/src/admin/groups/RelatedGroupList.ts +++ b/web/src/admin/groups/RelatedGroupList.ts @@ -8,6 +8,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { Form } from "#elements/forms/Form"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -28,9 +29,9 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> { @state() groupsToAdd: Group[] = []; - getSuccessMessage(): string { - return msg("Successfully added user to group(s)."); - } + protected override readonly actionName = "add"; + + protected override entityLabel = msg("user to group(s)"); async send(data: { groups: string[] }): Promise { await Promise.all( @@ -90,6 +91,11 @@ export class RelatedGroupList extends Table { clearOnRefresh = true; protected override searchEnabled = true; + protected override entityLabel: EntityLabel = { + singular: msg("Group"), + plural: msg("Groups"), + }; + @property() order = "name"; @@ -143,8 +149,8 @@ export class RelatedGroupList extends Table { html`${item.parentName || msg("-")}`, html``, html` - ${msg("Update")} - ${msg("Update Group")} + ${this.updateEntityLabel} + ${this.editEntityLabel} + `; } diff --git a/web/src/admin/outposts/ServiceConnectionDockerForm.ts b/web/src/admin/outposts/ServiceConnectionDockerForm.ts index 90d6b289f0cd..13e470815436 100644 --- a/web/src/admin/outposts/ServiceConnectionDockerForm.ts +++ b/web/src/admin/outposts/ServiceConnectionDockerForm.ts @@ -21,11 +21,7 @@ export class ServiceConnectionDockerForm extends ModelForm { if (this.instance) { diff --git a/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts b/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts index 02c34290d7f7..8347e7572f39 100644 --- a/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts +++ b/web/src/admin/outposts/ServiceConnectionKubernetesForm.ts @@ -26,11 +26,7 @@ export class ServiceConnectionKubernetesForm extends ModelForm< }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated integration.") - : msg("Successfully created integration."); - } + protected override entityLabel = msg("integration"); async send(data: KubernetesServiceConnection): Promise { if (this.instance) { diff --git a/web/src/admin/outposts/ServiceConnectionListPage.ts b/web/src/admin/outposts/ServiceConnectionListPage.ts index d0796957bc18..a10e8626f1e7 100644 --- a/web/src/admin/outposts/ServiceConnectionListPage.ts +++ b/web/src/admin/outposts/ServiceConnectionListPage.ts @@ -28,7 +28,10 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-outpost-service-connection-list") export class OutpostServiceConnectionListPage extends TablePage { - public pageTitle = msg("Outpost integrations"); + protected override entityLabel = { + singular: msg("Outpost integration"), + plural: msg("Outpost integrations"), + }; public pageDescription = msg( "Outpost integrations define how authentik connects to external platforms to manage and deploy Outposts.", ); @@ -83,7 +86,7 @@ export class OutpostServiceConnectionListPage extends TablePage${msg("Unhealthy")}`}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.verboseName}`)} extends ModelForm { - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated policy.") - : msg("Successfully created policy."); - } + protected override entityLabel = msg("policy"); } diff --git a/web/src/admin/policies/BoundPoliciesList.ts b/web/src/admin/policies/BoundPoliciesList.ts index e6c32a22372f..6424e272b542 100644 --- a/web/src/admin/policies/BoundPoliciesList.ts +++ b/web/src/admin/policies/BoundPoliciesList.ts @@ -11,6 +11,9 @@ import "#elements/forms/ProxyForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { PFSize } from "#common/enums"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -55,6 +58,11 @@ export class BoundPoliciesList extends Table { order = "order"; + protected entityLabel: EntityLabel = { + singular: msg("Policy Binding"), + plural: msg("Policy Bindings"), + }; + static get styles(): CSSResult[] { return super.styles.concat(PFSpacing); } @@ -107,7 +115,7 @@ export class BoundPoliciesList extends Table { getObjectEditButton(item: PolicyBinding): SlottedTemplateResult { if (item.policy) { return html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.policyObj?.name}`)} { `; } else if (item.group) { return html` - ${msg("Update")} - ${msg("Update Group")} + ${ActionTenseRecord.apply.present} + ${formatEditMessage(msg("Group"))} `; } else if (item.user) { return html` - ${msg("Update")} - ${msg("Update User")} + ${ActionTenseRecord.apply.present} + ${formatEditMessage(msg("User"))} `; } @@ -182,8 +190,8 @@ export class BoundPoliciesList extends Table { html`${item.timeout}`, html` ${this.getObjectEditButton(item)} - ${msg("Update")} - ${msg("Update Binding")} + ${this.updateEntityLabel} + ${this.editEntityLabel} { > { bindingTarget=${ifDefined(this.target)} > - ${msg("Create")} - ${msg("Create Binding")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} { >` : nothing} - ${msg("Create")} - ${msg("Create Binding")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} { @state() defaultOrder = 0; - getSuccessMessage(): string { - if (this.instance?.pk) { - return msg("Successfully updated binding."); - } - return msg("Successfully created binding."); - } + protected override entityLabel = msg("policy binding"); static styles: CSSResult[] = [...super.styles, PFContent]; diff --git a/web/src/admin/policies/PolicyListPage.ts b/web/src/admin/policies/PolicyListPage.ts index c2129461d20c..d0e9dd4d4009 100644 --- a/web/src/admin/policies/PolicyListPage.ts +++ b/web/src/admin/policies/PolicyListPage.ts @@ -31,7 +31,10 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-policy-list") export class PolicyListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Policies"); + protected override entityLabel = { + singular: msg("Policy"), + plural: msg("Policies"), + }; public pageDescription = msg( "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages.", ); @@ -66,7 +69,7 @@ export class PolicyListPage extends TablePage { `}`, html`${item.verboseName}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.verboseName}`)} { renderToolbar(): TemplateResult { return html` ${super.renderToolbar()} { return new PoliciesApi(DEFAULT_CONFIG).policiesAllCacheClearCreate(); diff --git a/web/src/admin/policies/PolicyTestForm.ts b/web/src/admin/policies/PolicyTestForm.ts index 56514062c996..e71a99bb3e29 100644 --- a/web/src/admin/policies/PolicyTestForm.ts +++ b/web/src/admin/policies/PolicyTestForm.ts @@ -38,9 +38,9 @@ export class PolicyTestForm extends Form { @property({ attribute: false }) request?: PolicyTestRequest; - getSuccessMessage(): string { - return msg("Successfully sent test-request."); - } + protected override readonly actionName = "send"; + + protected override entityLabel = msg("test-request"); async send(data: PolicyTestRequest): Promise { this.request = data; diff --git a/web/src/admin/policies/PolicyWizard.ts b/web/src/admin/policies/PolicyWizard.ts index a225ae117459..16e2ce5f9e7d 100644 --- a/web/src/admin/policies/PolicyWizard.ts +++ b/web/src/admin/policies/PolicyWizard.ts @@ -12,6 +12,7 @@ import "#elements/wizard/TypeCreateWizardPage"; import "#elements/wizard/Wizard"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatNewMessage } from "#common/i18n/actions"; import { AKElement } from "#elements/Base"; import { FormWizardPage } from "#elements/wizard/FormWizardPage"; @@ -34,7 +35,7 @@ export class PolicyWizard extends AKElement { static styles: CSSResult[] = [PFBase, PFButton]; @property() - createText = msg("Create"); + createText = formatNewMessage(msg("Policy")); @property({ type: Boolean }) showBindingPage = false; diff --git a/web/src/admin/policies/reputation/ReputationListPage.ts b/web/src/admin/policies/reputation/ReputationListPage.ts index e05d656cf383..a410fb5d48c5 100644 --- a/web/src/admin/policies/reputation/ReputationListPage.ts +++ b/web/src/admin/policies/reputation/ReputationListPage.ts @@ -25,7 +25,10 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-policy-reputation-list") export class ReputationListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Reputation scores"); + protected override entityLabel = { + singular: msg("Reputation score"), + plural: msg("Reputation scores"), + }; public pageDescription = msg( "Reputation for IP and user identifiers. Scores are decreased for each failed login and increased for each successful login.", ); diff --git a/web/src/admin/property-mappings/BasePropertyMappingForm.ts b/web/src/admin/property-mappings/BasePropertyMappingForm.ts index bd12ca4a3aab..e327b38372a0 100644 --- a/web/src/admin/property-mappings/BasePropertyMappingForm.ts +++ b/web/src/admin/property-mappings/BasePropertyMappingForm.ts @@ -19,11 +19,7 @@ export abstract class BasePropertyMappingForm extends > { protected docLink: string | URL = "/add-secure-apps/providers/property-mappings/expression"; - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated mapping.") - : msg("Successfully created mapping."); - } + protected override entityLabel = msg("mapping"); renderExtraFields(): SlottedTemplateResult { return nothing; diff --git a/web/src/admin/property-mappings/PropertyMappingListPage.ts b/web/src/admin/property-mappings/PropertyMappingListPage.ts index 16f247576d7b..53afbef6fca5 100644 --- a/web/src/admin/property-mappings/PropertyMappingListPage.ts +++ b/web/src/admin/property-mappings/PropertyMappingListPage.ts @@ -22,6 +22,7 @@ import "#elements/forms/ProxyForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { getURLParam, updateURLParams } from "#elements/router/RouteMatch"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; @@ -38,13 +39,17 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-property-mapping-list") export class PropertyMappingListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Property Mappings"); public pageDescription = msg("Control how authentik exposes and interprets information."); public pageIcon = "pf-icon pf-icon-blueprint"; checkbox = true; clearOnRefresh = true; + protected override entityLabel: EntityLabel = { + singular: msg("Property Mapping"), + plural: msg("Property Mappings"), + }; + @property() order = "name"; @@ -91,7 +96,7 @@ export class PropertyMappingListPage extends TablePage { html`${item.name}`, html`${item.verboseName}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.verboseName}`)} { @property({ attribute: false }) request?: PropertyMappingTestRequest; - getSuccessMessage(): string { - return msg("Successfully sent test-request."); - } + protected override readonly actionName = "send"; + + protected override entityLabel = msg("test-request"); async send(data: PropertyMappingTestRequest): Promise { this.request = data; diff --git a/web/src/admin/property-mappings/PropertyMappingWizard.ts b/web/src/admin/property-mappings/PropertyMappingWizard.ts index a6599d2a53b8..896c5f46601b 100644 --- a/web/src/admin/property-mappings/PropertyMappingWizard.ts +++ b/web/src/admin/property-mappings/PropertyMappingWizard.ts @@ -20,6 +20,7 @@ import "#elements/wizard/TypeCreateWizardPage"; import "#elements/wizard/Wizard"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatNewMessage } from "#common/i18n/actions"; import { AKElement } from "#elements/Base"; import type { Wizard } from "#elements/wizard/Wizard"; @@ -80,7 +81,9 @@ export class PropertyMappingWizard extends AKElement { `; })} - + `; } diff --git a/web/src/admin/providers/BaseProviderForm.ts b/web/src/admin/providers/BaseProviderForm.ts index 3283ee01463f..ce500db11936 100644 --- a/web/src/admin/providers/BaseProviderForm.ts +++ b/web/src/admin/providers/BaseProviderForm.ts @@ -7,11 +7,7 @@ import { APIMessage } from "#elements/messages/Message"; import { msg } from "@lit/localize"; export abstract class BaseProviderForm extends ModelForm { - public override getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated provider.") - : msg("Successfully created provider."); - } + protected override entityLabel = msg("provider"); protected override formatAPIErrorMessage(error: APIError): APIMessage { return { diff --git a/web/src/admin/providers/ProviderListPage.ts b/web/src/admin/providers/ProviderListPage.ts index 753c2668ab72..88a49519f8cf 100644 --- a/web/src/admin/providers/ProviderListPage.ts +++ b/web/src/admin/providers/ProviderListPage.ts @@ -17,6 +17,7 @@ import "#elements/forms/ProxyForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; import { TablePage } from "#elements/table/TablePage"; @@ -32,7 +33,14 @@ import { customElement, property } from "lit/decorators.js"; export class ProviderListPage extends TablePage { protected override searchEnabled = true; - override pageTitle = msg("Providers"); + protected override entityLabel = { + singular: msg("Provider"), + plural: msg("Providers"), + }; + + protected override get searchPlaceholder() { + return msg("Search for a provider by name or application..."); + } public pageDescription = msg( "Provide support for protocols like SAML and OAuth to assigned applications.", @@ -46,9 +54,6 @@ export class ProviderListPage extends TablePage { @property() public order = "name"; - public searchLabel = msg("Provider Search"); - public searchPlaceholder = msg("Search for providers…"); - override async apiEndpoint(): Promise> { return new ProvidersApi(DEFAULT_CONFIG).providersAllList( await this.defaultEndpointConfig(), @@ -113,7 +118,7 @@ export class ProviderListPage extends TablePage { html`${item.verboseName}`, html`
- ${msg("Update")} + ${ActionTenseRecord.apply.present} ${msg(str`Update ${item.verboseName}`)} - ${msg("Create")} + ${formatNewMessage(msg("Provider"))} `; diff --git a/web/src/admin/providers/RelatedApplicationButton.ts b/web/src/admin/providers/RelatedApplicationButton.ts index 203cc7fe0f3e..95906f1e5f3b 100644 --- a/web/src/admin/providers/RelatedApplicationButton.ts +++ b/web/src/admin/providers/RelatedApplicationButton.ts @@ -2,6 +2,9 @@ import "#admin/applications/ApplicationForm"; import "#elements/Spinner"; import "#elements/forms/ModalForm"; +import { formatCreateMessage, formatNewMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; + import { AKElement } from "#elements/Base"; import { Provider } from "@goauthentik/api"; @@ -17,6 +20,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; export class RelatedApplicationButton extends AKElement { static styles: CSSResult[] = [PFBase, PFButton]; + protected entityLabel: EntityLabel = { + singular: msg("Application"), + plural: msg("Applications"), + }; + @property({ attribute: false }) provider?: Provider; @@ -37,10 +45,13 @@ export class RelatedApplicationButton extends AKElement { `; } return html` - ${msg("Create")} - ${msg("Create Application")} + ${formatCreateMessage(this.entityLabel)} + ${formatNewMessage(this.entityLabel)} + - + `; } } diff --git a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderGroupList.ts b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderGroupList.ts index 3290ae1e8402..c715684187c4 100644 --- a/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderGroupList.ts +++ b/web/src/admin/providers/google_workspace/GoogleWorkspaceProviderGroupList.ts @@ -3,6 +3,7 @@ import "#elements/forms/ModalForm"; import "#elements/sync/SyncObjectForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -25,6 +26,11 @@ export class GoogleWorkspaceProviderGroupList extends Table diff --git a/web/src/admin/providers/ldap/LDAPProviderViewPage.ts b/web/src/admin/providers/ldap/LDAPProviderViewPage.ts index 1ccdb428d8a1..2608445c1c4a 100644 --- a/web/src/admin/providers/ldap/LDAPProviderViewPage.ts +++ b/web/src/admin/providers/ldap/LDAPProviderViewPage.ts @@ -9,6 +9,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { me } from "#common/users"; import { AKElement } from "#elements/Base"; @@ -40,6 +43,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-provider-ldap-view") export class LDAPProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("LDAP Provider"), + plural: msg("LDAP Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -136,14 +144,11 @@ export class LDAPProviderViewPage extends AKElement { if (!this.provider) { return nothing; } - return html` - ${ - this.provider?.outpostSet.length < 1 - ? html`
- ${msg("Warning: Provider is not used by any Outpost.")} -
` - : nothing - } + return html` ${this.provider?.outpostSet.length < 1 + ? html`
+ ${msg("Warning: Provider is not used by any Outpost.")} +
` + : nothing}
@@ -188,24 +193,20 @@ export class LDAPProviderViewPage extends AKElement {
-
- ${msg("How to connect")} -
+
${msg("How to connect")}
-

- ${msg("Connect to the LDAP Server on port 389:")} -

+

${msg("Connect to the LDAP Server on port 389:")}

  • ${msg("Check the IP of the Kubernetes service, or")}
  • ${msg("The Host IP of the docker host")}
  • @@ -216,7 +217,7 @@ export class LDAPProviderViewPage extends AKElement { ${msg("Bind DN")}
    ${msg("Search base")}
-
-
`; +
`; } } diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderGroupList.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderGroupList.ts index 6023f98052f8..07855a453ca7 100644 --- a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderGroupList.ts +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderGroupList.ts @@ -3,6 +3,7 @@ import "#elements/forms/ModalForm"; import "#elements/sync/SyncObjectForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -27,6 +28,11 @@ export class MicrosoftEntraProviderGroupList extends Table ${msg("Sync")} diff --git a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderUserList.ts b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderUserList.ts index e8116755e919..948c844bbb72 100644 --- a/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderUserList.ts +++ b/web/src/admin/providers/microsoft_entra/MicrosoftEntraProviderUserList.ts @@ -3,6 +3,7 @@ import "#elements/forms/ModalForm"; import "#elements/sync/SyncObjectForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -25,6 +26,11 @@ export class MicrosoftEntraProviderUserList extends Table diff --git a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts index 1be455e23283..0ce4d4ccf5de 100644 --- a/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts +++ b/web/src/admin/providers/oauth2/OAuth2ProviderViewPage.ts @@ -11,6 +11,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -64,6 +67,11 @@ export function TypeToLabel(type?: ClientTypeEnum): string { @customElement("ak-provider-oauth2-view") export class OAuth2ProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("OAuth2 Provider"), + plural: msg("OAuth2 Providers"), + }; + @property({ type: Number }) set providerID(value: number) { new ProvidersApi(DEFAULT_CONFIG) @@ -298,15 +306,15 @@ export class OAuth2ProviderViewPage extends AKElement {
diff --git a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts index 1b174f2fcd23..f2e846a94252 100644 --- a/web/src/admin/providers/proxy/ProxyProviderViewPage.ts +++ b/web/src/admin/providers/proxy/ProxyProviderViewPage.ts @@ -11,6 +11,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import type { Replacer } from "#elements/ak-mdx/index"; import { AKElement } from "#elements/Base"; @@ -78,6 +81,11 @@ export function isForward(mode: ProxyMode): boolean { @customElement("ak-provider-proxy-view") export class ProxyProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Proxy Provider"), + plural: msg("Proxy Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -381,15 +389,15 @@ export class ProxyProviderViewPage extends AKElement {
diff --git a/web/src/admin/providers/rac/ConnectionTokenList.ts b/web/src/admin/providers/rac/ConnectionTokenList.ts index 79d63fc655c1..a35037a930c9 100644 --- a/web/src/admin/providers/rac/ConnectionTokenList.ts +++ b/web/src/admin/providers/rac/ConnectionTokenList.ts @@ -4,6 +4,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -23,6 +24,11 @@ export class ConnectionTokenListPage extends Table { protected override searchEnabled = true; + protected override entityLabel: EntityLabel = { + singular: msg("connection token"), + plural: msg("connection tokens"), + }; + @property() order = "name"; diff --git a/web/src/admin/providers/rac/EndpointForm.ts b/web/src/admin/providers/rac/EndpointForm.ts index 7ca2da5bb7db..056bf6b6d021 100644 --- a/web/src/admin/providers/rac/EndpointForm.ts +++ b/web/src/admin/providers/rac/EndpointForm.ts @@ -30,11 +30,7 @@ export class EndpointForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated endpoint.") - : msg("Successfully created endpoint."); - } + protected override entityLabel = msg("endpoint"); async send(data: Endpoint): Promise { data.authMode = EndpointAuthModeEnum.Prompt; diff --git a/web/src/admin/providers/rac/EndpointList.ts b/web/src/admin/providers/rac/EndpointList.ts index a998e38e8b77..561ef6b2a379 100644 --- a/web/src/admin/providers/rac/EndpointList.ts +++ b/web/src/admin/providers/rac/EndpointList.ts @@ -7,6 +7,9 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatCreateMessage, formatEditMessage, formatNewMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -30,6 +33,11 @@ export class EndpointListPage extends Table { checkbox = true; clearOnRefresh = true; + protected override entityLabel: EntityLabel = { + singular: msg("Endpoint"), + plural: msg("Endpoints"), + }; + protected override searchEnabled = true; @property() @@ -88,8 +96,8 @@ export class EndpointListPage extends Table { html`${item.host}`, html`
- ${msg("Update")} - ${msg("Update Endpoint")} + ${ActionTenseRecord.apply.present} + ${formatEditMessage(this.entityLabel)} + `; } diff --git a/web/src/admin/providers/rac/RACProviderForm.ts b/web/src/admin/providers/rac/RACProviderForm.ts index 1077d2959841..533cff810ae0 100644 --- a/web/src/admin/providers/rac/RACProviderForm.ts +++ b/web/src/admin/providers/rac/RACProviderForm.ts @@ -31,12 +31,7 @@ export class RACProviderFormPage extends ModelForm { }); } - getSuccessMessage(): string { - if (this.instance) { - return msg("Successfully updated provider."); - } - return msg("Successfully created provider."); - } + protected override entityLabel = msg("RAC provider"); async send(data: RACProvider): Promise { if (this.instance) { diff --git a/web/src/admin/providers/rac/RACProviderViewPage.ts b/web/src/admin/providers/rac/RACProviderViewPage.ts index 5e188ff678a3..4ad9341fb254 100644 --- a/web/src/admin/providers/rac/RACProviderViewPage.ts +++ b/web/src/admin/providers/rac/RACProviderViewPage.ts @@ -13,6 +13,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -41,6 +44,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-provider-rac-view") export class RACProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("RAC Provider"), + plural: msg("RAC Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -189,12 +197,12 @@ export class RACProviderViewPage extends AKElement {
diff --git a/web/src/admin/providers/radius/RadiusProviderViewPage.ts b/web/src/admin/providers/radius/RadiusProviderViewPage.ts index e22414e934a8..e8d2a66e5292 100644 --- a/web/src/admin/providers/radius/RadiusProviderViewPage.ts +++ b/web/src/admin/providers/radius/RadiusProviderViewPage.ts @@ -9,6 +9,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -35,6 +38,11 @@ import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; @customElement("ak-provider-radius-view") export class RadiusProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("RADIUS Provider"), + plural: msg("RADIUS Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -139,7 +147,9 @@ export class RadiusProviderViewPage extends AKElement { diff --git a/web/src/admin/providers/saml/SAMLProviderImportForm.ts b/web/src/admin/providers/saml/SAMLProviderImportForm.ts index bca552db7e23..c128e1149657 100644 --- a/web/src/admin/providers/saml/SAMLProviderImportForm.ts +++ b/web/src/admin/providers/saml/SAMLProviderImportForm.ts @@ -15,9 +15,9 @@ import { customElement } from "lit/decorators.js"; @customElement("ak-provider-saml-import-form") export class SAMLProviderImportForm extends Form { - getSuccessMessage(): string { - return msg("Successfully imported provider."); - } + protected override readonly actionName = "$import"; + + protected override entityLabel = msg("provider"); async send(data: SAMLProvider): Promise { const file = this.files().get("metadata"); diff --git a/web/src/admin/providers/saml/SAMLProviderViewPage.ts b/web/src/admin/providers/saml/SAMLProviderViewPage.ts index 62ea0b386c04..3b96e363b9bc 100644 --- a/web/src/admin/providers/saml/SAMLProviderViewPage.ts +++ b/web/src/admin/providers/saml/SAMLProviderViewPage.ts @@ -11,6 +11,9 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { MessageLevel } from "#common/messages"; import { AKElement } from "#elements/Base"; @@ -59,6 +62,11 @@ interface SAMLPreviewAttribute { @customElement("ak-provider-saml-view") export class SAMLProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("SAML Provider"), + plural: msg("SAML Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -363,12 +371,12 @@ export class SAMLProviderViewPage extends AKElement { diff --git a/web/src/admin/providers/scim/SCIMProviderGroupList.ts b/web/src/admin/providers/scim/SCIMProviderGroupList.ts index e3ef2b41916c..a395beadf1c2 100644 --- a/web/src/admin/providers/scim/SCIMProviderGroupList.ts +++ b/web/src/admin/providers/scim/SCIMProviderGroupList.ts @@ -3,6 +3,7 @@ import "#elements/forms/ModalForm"; import "#elements/sync/SyncObjectForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -23,6 +24,11 @@ export class SCIMProviderGroupList extends Table { @property({ type: Number }) providerId?: number; + protected override entityLabel: EntityLabel = { + singular: msg("SCIM group"), + plural: msg("SCIM groups"), + }; + protected override searchEnabled = true; expandable = true; diff --git a/web/src/admin/providers/scim/SCIMProviderUserList.ts b/web/src/admin/providers/scim/SCIMProviderUserList.ts index 536f57f2b639..f593b8aacf86 100644 --- a/web/src/admin/providers/scim/SCIMProviderUserList.ts +++ b/web/src/admin/providers/scim/SCIMProviderUserList.ts @@ -3,6 +3,7 @@ import "#elements/forms/ModalForm"; import "#elements/sync/SyncObjectForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -23,6 +24,11 @@ export class SCIMProviderUserList extends Table { @property({ type: Number }) providerId?: number; + protected override entityLabel: EntityLabel = { + singular: msg("SCIM user"), + plural: msg("SCIM users"), + }; + protected override searchEnabled = true; expandable = true; diff --git a/web/src/admin/providers/scim/SCIMProviderViewPage.ts b/web/src/admin/providers/scim/SCIMProviderViewPage.ts index f6a9595e7cb7..5430028b4a7b 100644 --- a/web/src/admin/providers/scim/SCIMProviderViewPage.ts +++ b/web/src/admin/providers/scim/SCIMProviderViewPage.ts @@ -15,6 +15,9 @@ import "#elements/tasks/TaskList"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -47,6 +50,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-provider-scim-view") export class SCIMProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("SCIM Provider"), + plural: msg("SCIM Providers"), + }; + @property({ type: Number }) providerID?: number; @@ -236,12 +244,12 @@ export class SCIMProviderViewPage extends AKElement { diff --git a/web/src/admin/providers/ssf/SSFProviderViewPage.ts b/web/src/admin/providers/ssf/SSFProviderViewPage.ts index aa32aa24a74c..97bdb533db63 100644 --- a/web/src/admin/providers/ssf/SSFProviderViewPage.ts +++ b/web/src/admin/providers/ssf/SSFProviderViewPage.ts @@ -11,6 +11,9 @@ import "#elements/tasks/TaskList"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -40,6 +43,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-provider-ssf-view") export class SSFProviderViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("SSF Provider"), + plural: msg("SSF Providers"), + }; + @property({ type: Number }) set providerID(value: number) { new ProvidersApi(DEFAULT_CONFIG) @@ -169,12 +177,12 @@ export class SSFProviderViewPage extends AKElement { diff --git a/web/src/admin/providers/ssf/StreamTable.ts b/web/src/admin/providers/ssf/StreamTable.ts index 9f70241d2fd0..1027f391ea98 100644 --- a/web/src/admin/providers/ssf/StreamTable.ts +++ b/web/src/admin/providers/ssf/StreamTable.ts @@ -5,6 +5,7 @@ import "#elements/forms/ProxyForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -17,6 +18,11 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-provider-ssf-stream-list") export class SSFProviderStreamList extends Table { + protected override entityLabel: EntityLabel = { + singular: msg("SSF stream"), + plural: msg("SSF streams"), + }; + protected override searchEnabled = true; checkbox = true; clearOnRefresh = true; diff --git a/web/src/admin/rbac/InitialPermissionsForm.ts b/web/src/admin/rbac/InitialPermissionsForm.ts index 223cf7be0c2e..3660d3d1f4a3 100644 --- a/web/src/admin/rbac/InitialPermissionsForm.ts +++ b/web/src/admin/rbac/InitialPermissionsForm.ts @@ -37,11 +37,7 @@ export class InitialPermissionsForm extends ModelForm { if (this.instance?.pk) { diff --git a/web/src/admin/rbac/InitialPermissionsListPage.ts b/web/src/admin/rbac/InitialPermissionsListPage.ts index 623243bf510a..643ae1be0e60 100644 --- a/web/src/admin/rbac/InitialPermissionsListPage.ts +++ b/web/src/admin/rbac/InitialPermissionsListPage.ts @@ -5,6 +5,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; import { TablePage } from "#elements/table/TablePage"; @@ -23,7 +24,12 @@ export class InitialPermissionsListPage extends TablePage { checkbox = true; clearOnRefresh = true; protected override searchEnabled = true; - public pageTitle = msg("Initial Permissions"); + + protected override entityLabel: EntityLabel = { + singular: msg("Initial Permission"), + plural: msg("Initial Permissions"), + }; + public pageDescription = msg("Set initial permissions for newly created objects."); public pageIcon = "fa fa-lock"; @@ -75,12 +81,12 @@ export class InitialPermissionsListPage extends TablePage { html`${item.name}`, html`
- ${msg("Update")} - ${msg("Update Initial Permissions")} + ${this.updateEntityLabel} + ${this.editEntityLabel} @@ -92,10 +98,12 @@ export class InitialPermissionsListPage extends TablePage { renderObjectCreate(): TemplateResult { return html` - ${msg("Create")} - ${msg("Create Initial Permissions")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + `; } diff --git a/web/src/admin/rbac/PermissionSelectModal.ts b/web/src/admin/rbac/PermissionSelectModal.ts index b05e36ca434e..1f49b761691a 100644 --- a/web/src/admin/rbac/PermissionSelectModal.ts +++ b/web/src/admin/rbac/PermissionSelectModal.ts @@ -1,6 +1,7 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { groupBy } from "#common/utils"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; @@ -20,6 +21,11 @@ export class PermissionSelectModal extends TableModal { checkbox = true; checkboxChip = true; + protected override entityLabel: EntityLabel = { + singular: msg("permission"), + plural: msg("permissions"), + }; + protected override searchEnabled = true; @property() diff --git a/web/src/admin/rbac/RoleObjectPermissionForm.ts b/web/src/admin/rbac/RoleObjectPermissionForm.ts index 7de2ef3f7a1e..f4da5f3dc632 100644 --- a/web/src/admin/rbac/RoleObjectPermissionForm.ts +++ b/web/src/admin/rbac/RoleObjectPermissionForm.ts @@ -4,6 +4,7 @@ import "#elements/forms/Radio"; import "#elements/forms/SearchSelect/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionName } from "#common/i18n/verbs"; import { ModelForm } from "#elements/forms/ModelForm"; import { SlottedTemplateResult } from "#elements/types"; @@ -51,10 +52,12 @@ export class RoleObjectPermissionForm extends ModelForm throw new Error("Method not implemented."); } - getSuccessMessage(): string { - return msg("Successfully assigned permission."); + protected override get actionName(): ActionName { + return "assign"; } + protected override entityLabel = msg("permission"); + send(data: RoleAssignData): Promise { return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({ uuid: data.role, diff --git a/web/src/admin/rbac/RoleObjectPermissionTable.ts b/web/src/admin/rbac/RoleObjectPermissionTable.ts index 3bec2eda613d..462827e70ff5 100644 --- a/web/src/admin/rbac/RoleObjectPermissionTable.ts +++ b/web/src/admin/rbac/RoleObjectPermissionTable.ts @@ -4,6 +4,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -34,6 +35,11 @@ export class RoleAssignedObjectPermissionTable extends Table> { const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesList({ ...(await this.defaultEndpointConfig()), diff --git a/web/src/admin/rbac/UserObjectPermissionForm.ts b/web/src/admin/rbac/UserObjectPermissionForm.ts index 1218586661d8..586c11109283 100644 --- a/web/src/admin/rbac/UserObjectPermissionForm.ts +++ b/web/src/admin/rbac/UserObjectPermissionForm.ts @@ -4,6 +4,7 @@ import "#elements/forms/Radio"; import "#elements/forms/SearchSelect/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionName } from "#common/i18n/verbs"; import { ModelForm } from "#elements/forms/ModelForm"; import { SlottedTemplateResult } from "#elements/types"; @@ -52,10 +53,12 @@ export class UserObjectPermissionForm extends ModelForm throw new Error("Method not implemented."); } - getSuccessMessage(): string { - return msg("Successfully assigned permission."); + protected override get actionName(): ActionName { + return "assign"; } + protected override entityLabel = msg("permission"); + send(data: UserAssignData): Promise { return new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({ id: data.user, diff --git a/web/src/admin/rbac/UserObjectPermissionTable.ts b/web/src/admin/rbac/UserObjectPermissionTable.ts index d6aec44d8904..2660ef2d63e6 100644 --- a/web/src/admin/rbac/UserObjectPermissionTable.ts +++ b/web/src/admin/rbac/UserObjectPermissionTable.ts @@ -4,6 +4,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -34,6 +35,11 @@ export class UserAssignedObjectPermissionTable extends Table> { const perms = await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersList({ ...(await this.defaultEndpointConfig()), diff --git a/web/src/admin/roles/RoleAssignedGlobalPermissionsTable.ts b/web/src/admin/roles/RoleAssignedGlobalPermissionsTable.ts index ab543d4f8b71..9ba7cd33bb3d 100644 --- a/web/src/admin/roles/RoleAssignedGlobalPermissionsTable.ts +++ b/web/src/admin/roles/RoleAssignedGlobalPermissionsTable.ts @@ -2,6 +2,7 @@ import "#admin/roles/RolePermissionForm"; import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { groupBy } from "#common/utils"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -19,6 +20,11 @@ export class RoleAssignedGlobalPermissionsTable extends Table { @property() roleUuid?: string; + protected override entityLabel: EntityLabel = { + singular: msg("permission"), + plural: msg("permissions"), + }; + protected override searchEnabled = true; checkbox = true; diff --git a/web/src/admin/roles/RoleAssignedObjectPermissionTable.ts b/web/src/admin/roles/RoleAssignedObjectPermissionTable.ts index af5a4a546841..53ec95153b49 100644 --- a/web/src/admin/roles/RoleAssignedObjectPermissionTable.ts +++ b/web/src/admin/roles/RoleAssignedObjectPermissionTable.ts @@ -2,6 +2,7 @@ import "#elements/forms/DeleteBulkForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { groupBy } from "#common/utils"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -18,6 +19,11 @@ export class RoleAssignedObjectPermissionTable extends Table { @@ -22,11 +23,7 @@ export class RoleForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated role.") - : msg("Successfully created role."); - } + protected override entityLabel = msg("role"); async send(data: Role): Promise { if (this.instance?.pk) { @@ -41,14 +38,13 @@ export class RoleForm extends ModelForm { } renderForm(): TemplateResult { - return html` - - `; + return html``; } } diff --git a/web/src/admin/roles/RoleListPage.ts b/web/src/admin/roles/RoleListPage.ts index 7cd24a9c88f0..7aad773f0162 100644 --- a/web/src/admin/roles/RoleListPage.ts +++ b/web/src/admin/roles/RoleListPage.ts @@ -5,6 +5,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; import { TablePage } from "#elements/table/TablePage"; @@ -23,7 +24,11 @@ export class RoleListPage extends TablePage { checkbox = true; clearOnRefresh = true; protected override searchEnabled = true; - public pageTitle = msg("Roles"); + protected override entityLabel: EntityLabel = { + singular: msg("Role"), + plural: msg("Roles"), + }; + public pageDescription = msg( "Manage roles which grant permissions to objects within authentik.", ); @@ -75,7 +80,7 @@ export class RoleListPage extends TablePage { html`${item.name}`, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Role")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} + + `; } diff --git a/web/src/admin/roles/RolePermissionForm.ts b/web/src/admin/roles/RolePermissionForm.ts index e6dc000e796b..8c4b7d1c9dc9 100644 --- a/web/src/admin/roles/RolePermissionForm.ts +++ b/web/src/admin/roles/RolePermissionForm.ts @@ -7,6 +7,7 @@ import "#elements/forms/Radio"; import "#elements/forms/SearchSelect/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionName } from "#common/i18n/verbs"; import { ModelForm } from "#elements/forms/ModelForm"; @@ -34,10 +35,12 @@ export class RolePermissionForm extends ModelForm throw new Error("Method not implemented."); } - getSuccessMessage(): string { - return msg("Successfully assigned permission."); + protected override get actionName(): ActionName { + return "assign"; } + protected override entityLabel = msg("permission"); + async send(data: RolePermissionAssign) { await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByRolesAssign({ uuid: this.roleUuid || "", diff --git a/web/src/admin/roles/RoleViewPage.ts b/web/src/admin/roles/RoleViewPage.ts index 45113ccfdc39..96209d8b31f2 100644 --- a/web/src/admin/roles/RoleViewPage.ts +++ b/web/src/admin/roles/RoleViewPage.ts @@ -8,6 +8,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; @@ -31,6 +34,11 @@ import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; @customElement("ak-role-view") export class RoleViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Role"), + plural: msg("Roles"), + }; + @property({ type: String }) set roleId(id: string) { new RbacApi(DEFAULT_CONFIG) @@ -76,10 +84,12 @@ export class RoleViewPage extends AKElement { renderUpdateControl(role: Role) { return html`
- ${msg("Update")} - ${msg("Update Role")} - - + ${ActionTenseRecord.apply.present} + ${formatEditMessage(this.entityLabel)} + +
`; } @@ -107,7 +117,7 @@ export class RoleViewPage extends AKElement {
${renderDescriptionList([ [msg("Name"), this._role.name], - [msg("Edit"), this.renderUpdateControl(this._role)], + [msg("Actions"), this.renderUpdateControl(this._role)], ])}
diff --git a/web/src/admin/sources/BaseSourceForm.ts b/web/src/admin/sources/BaseSourceForm.ts index 5fa6b1e8ca72..c677d358fae7 100644 --- a/web/src/admin/sources/BaseSourceForm.ts +++ b/web/src/admin/sources/BaseSourceForm.ts @@ -3,9 +3,5 @@ import { ModelForm } from "#elements/forms/ModelForm"; import { msg } from "@lit/localize"; export abstract class BaseSourceForm extends ModelForm { - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated source.") - : msg("Successfully created source."); - } + protected override entityLabel = msg("source"); } diff --git a/web/src/admin/sources/SourceListPage.ts b/web/src/admin/sources/SourceListPage.ts index ce2260670d03..668713c23226 100644 --- a/web/src/admin/sources/SourceListPage.ts +++ b/web/src/admin/sources/SourceListPage.ts @@ -10,6 +10,7 @@ import "#elements/forms/ProxyForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { PFColor } from "#elements/Label"; import { PaginatedResponse, TableColumn } from "#elements/table/Table"; @@ -25,7 +26,10 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-source-list") export class SourceListPage extends TablePage { - public pageTitle = msg("Federation and Social login"); + protected override entityLabel = { + singular: msg("Federation and Social login"), + plural: msg("Federation and Social login"), + }; public pageDescription = msg( "Sources of identities, which can either be synced into authentik's database, or can be used by users to authenticate and enroll themselves.", ); @@ -89,7 +93,7 @@ export class SourceListPage extends TablePage { `, html`${item.verboseName}`, html` - ${msg("Update")} + ${ActionTenseRecord.apply.present} ${msg(str`Update ${item.verboseName}`)} { > diff --git a/web/src/admin/sources/SourceViewPage.ts b/web/src/admin/sources/SourceViewPage.ts index 2c20a0b93c2c..9bfa968f3216 100644 --- a/web/src/admin/sources/SourceViewPage.ts +++ b/web/src/admin/sources/SourceViewPage.ts @@ -9,6 +9,7 @@ import "#elements/EmptyState"; import "#elements/buttons/SpinnerButton/ak-spinner-button"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { AKElement } from "#elements/Base"; @@ -16,11 +17,17 @@ import { setPageDetails } from "#components/ak-page-navbar"; import { Source, SourcesApi } from "@goauthentik/api"; +import { msg } from "@lit/localize"; import { html, PropertyValues, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; @customElement("ak-source-view") export class SourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Source"), + plural: msg("Sources"), + }; + @property({ type: String }) set sourceSlug(slug: string) { new SourcesApi(DEFAULT_CONFIG) diff --git a/web/src/admin/sources/SourceWizard.ts b/web/src/admin/sources/SourceWizard.ts index 197881beb3f2..69c8f0aa1a41 100644 --- a/web/src/admin/sources/SourceWizard.ts +++ b/web/src/admin/sources/SourceWizard.ts @@ -10,6 +10,7 @@ import "#elements/wizard/FormWizardPage"; import "#elements/wizard/Wizard"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatNewMessage } from "#common/i18n/actions"; import { AKElement } from "#elements/Base"; import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage"; @@ -77,7 +78,9 @@ export class SourceWizard extends AKElement { `; })} - + `; } diff --git a/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts b/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts index 2ad76c60c3eb..d3ae8afccefc 100644 --- a/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts +++ b/web/src/admin/sources/kerberos/KerberosSourceViewPage.ts @@ -12,6 +12,9 @@ import "#elements/sync/SyncStatusCard"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage, formatNewMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -41,6 +44,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-kerberos-view") export class KerberosSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Kerberos Source"), + plural: msg("Kerberos Sources"), + }; + @property({ type: String }) set sourceSlug(slug: string) { new SourcesApi(DEFAULT_CONFIG) @@ -130,15 +138,15 @@ export class KerberosSourceViewPage extends AKElement {
diff --git a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts index 39d377ddad54..a78ef841ec14 100644 --- a/web/src/admin/sources/ldap/LDAPSourceViewPage.ts +++ b/web/src/admin/sources/ldap/LDAPSourceViewPage.ts @@ -12,6 +12,9 @@ import "#elements/tasks/ScheduleList"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -38,6 +41,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-ldap-view") export class LDAPSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("LDAP Source"), + plural: msg("LDAP Sources"), + }; + @property({ type: String }) set sourceSlug(slug: string) { new SourcesApi(DEFAULT_CONFIG) @@ -134,15 +142,17 @@ export class LDAPSourceViewPage extends AKElement { diff --git a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts index 928489b0d220..133fcd916783 100644 --- a/web/src/admin/sources/oauth/OAuthSourceViewPage.ts +++ b/web/src/admin/sources/oauth/OAuthSourceViewPage.ts @@ -10,6 +10,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -76,6 +79,11 @@ export function ProviderToLabel(provider?: ProviderTypeEnum): string { @customElement("ak-source-oauth-view") export class OAuthSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("OAuth Source"), + plural: msg("OAuth Sources"), + }; + @property({ type: String }) set sourceSlug(value: string) { new SourcesApi(DEFAULT_CONFIG) @@ -205,15 +213,17 @@ export class OAuthSourceViewPage extends AKElement { diff --git a/web/src/admin/sources/plex/PlexSourceViewPage.ts b/web/src/admin/sources/plex/PlexSourceViewPage.ts index 917c0c1fff13..2e9e5d6d4019 100644 --- a/web/src/admin/sources/plex/PlexSourceViewPage.ts +++ b/web/src/admin/sources/plex/PlexSourceViewPage.ts @@ -9,6 +9,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -35,6 +38,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-plex-view") export class PlexSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Plex Source"), + plural: msg("Plex Sources"), + }; + @property({ type: String }) set sourceSlug(value: string) { new SourcesApi(DEFAULT_CONFIG) @@ -101,15 +109,17 @@ export class PlexSourceViewPage extends AKElement { diff --git a/web/src/admin/sources/saml/SAMLSourceViewPage.ts b/web/src/admin/sources/saml/SAMLSourceViewPage.ts index 1ecfc466e2df..dc69f7de9419 100644 --- a/web/src/admin/sources/saml/SAMLSourceViewPage.ts +++ b/web/src/admin/sources/saml/SAMLSourceViewPage.ts @@ -9,6 +9,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { CodeMirrorMode } from "#elements/CodeMirror"; @@ -38,6 +41,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-saml-view") export class SAMLSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("SAML Source"), + plural: msg("SAML Sources"), + }; + @property({ type: String }) set sourceSlug(slug: string) { new SourcesApi(DEFAULT_CONFIG) @@ -143,15 +151,17 @@ export class SAMLSourceViewPage extends AKElement { diff --git a/web/src/admin/sources/scim/SCIMSourceGroups.ts b/web/src/admin/sources/scim/SCIMSourceGroups.ts index 28af4a2dd22a..6ac044d76dff 100644 --- a/web/src/admin/sources/scim/SCIMSourceGroups.ts +++ b/web/src/admin/sources/scim/SCIMSourceGroups.ts @@ -1,4 +1,5 @@ import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -15,6 +16,12 @@ export class SCIMSourceGroupList extends Table { sourceSlug?: string; expandable = true; + + protected override entityLabel: EntityLabel = { + singular: msg("group"), + plural: msg("groups"), + }; + protected override searchEnabled = true; async apiEndpoint(): Promise> { diff --git a/web/src/admin/sources/scim/SCIMSourceUsers.ts b/web/src/admin/sources/scim/SCIMSourceUsers.ts index 43d2c926645b..ae3bc7c0284a 100644 --- a/web/src/admin/sources/scim/SCIMSourceUsers.ts +++ b/web/src/admin/sources/scim/SCIMSourceUsers.ts @@ -1,4 +1,5 @@ import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -15,6 +16,12 @@ export class SCIMSourceUserList extends Table { sourceSlug?: string; expandable = true; + + protected override entityLabel: EntityLabel = { + singular: msg("user"), + plural: msg("users"), + }; + protected override searchEnabled = true; async apiEndpoint(): Promise> { diff --git a/web/src/admin/sources/scim/SCIMSourceViewPage.ts b/web/src/admin/sources/scim/SCIMSourceViewPage.ts index ced97cc244eb..b34d8d04184b 100644 --- a/web/src/admin/sources/scim/SCIMSourceViewPage.ts +++ b/web/src/admin/sources/scim/SCIMSourceViewPage.ts @@ -11,6 +11,9 @@ import "#elements/forms/ModalForm"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; import { SlottedTemplateResult } from "#elements/types"; @@ -37,6 +40,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-scim-view") export class SCIMSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("SCIM Source"), + plural: msg("SCIM Sources"), + }; + @property({ type: String }) set sourceSlug(value: string) { new SourcesApi(DEFAULT_CONFIG) @@ -118,15 +126,17 @@ export class SCIMSourceViewPage extends AKElement { diff --git a/web/src/admin/sources/telegram/TelegramSourceViewPage.ts b/web/src/admin/sources/telegram/TelegramSourceViewPage.ts index 43876ed7e3f4..68250214ac13 100644 --- a/web/src/admin/sources/telegram/TelegramSourceViewPage.ts +++ b/web/src/admin/sources/telegram/TelegramSourceViewPage.ts @@ -1,6 +1,9 @@ import "#admin/sources/telegram/TelegramSourceForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { AKElement } from "#elements/Base"; @@ -26,6 +29,11 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css"; @customElement("ak-source-telegram-view") export class TelegramSourceViewPage extends AKElement { + protected entityLabel: EntityLabel = { + singular: msg("Telegram Source"), + plural: msg("Telegram Sources"), + }; + @property({ type: String }) set sourceSlug(value: string) { new SourcesApi(DEFAULT_CONFIG) @@ -86,15 +94,15 @@ export class TelegramSourceViewPage extends AKElement { diff --git a/web/src/admin/stages/BaseStageForm.ts b/web/src/admin/stages/BaseStageForm.ts index 1c11692e6def..c9cdddc8c0c1 100644 --- a/web/src/admin/stages/BaseStageForm.ts +++ b/web/src/admin/stages/BaseStageForm.ts @@ -3,9 +3,5 @@ import { ModelForm } from "#elements/forms/ModelForm"; import { msg } from "@lit/localize"; export abstract class BaseStageForm extends ModelForm { - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated stage.") - : msg("Successfully created stage."); - } + protected override entityLabel = msg("stage"); } diff --git a/web/src/admin/stages/StageListPage.ts b/web/src/admin/stages/StageListPage.ts index d8ed3232bae7..d4817715456a 100644 --- a/web/src/admin/stages/StageListPage.ts +++ b/web/src/admin/stages/StageListPage.ts @@ -45,13 +45,20 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-stage-list") export class StageListPage extends TablePage { - public pageTitle = msg("Stages"); + protected override entityLabel = { + singular: msg("Stages"), + plural: msg("Stages"), + }; public pageDescription = msg( "Stages are single steps of a Flow that a user is guided through. A stage can only be executed from within a flow.", ); public pageIcon = "pf-icon pf-icon-plugged"; protected override searchEnabled = true; + protected override get searchPlaceholder(): string { + return msg("Search by stage name..."); + } + checkbox = true; clearOnRefresh = true; @@ -125,7 +132,7 @@ export class StageListPage extends TablePage { `, html`
- ${msg("Update")} + ${this.updateEntityLabel} ${msg(str`Update ${item.verboseName}`)} { const importData = data as unknown as AuthenticatorDuoStageManualDeviceImportRequest; return new StagesApi(DEFAULT_CONFIG).stagesAuthenticatorDuoImportDeviceManualCreate({ diff --git a/web/src/admin/stages/invitation/InvitationForm.ts b/web/src/admin/stages/invitation/InvitationForm.ts index 5c503368efa1..1dc667d81b68 100644 --- a/web/src/admin/stages/invitation/InvitationForm.ts +++ b/web/src/admin/stages/invitation/InvitationForm.ts @@ -25,11 +25,7 @@ export class InvitationForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated invitation.") - : msg("Successfully created invitation."); - } + protected override entityLabel = msg("invitation"); async send(data: Invitation): Promise { if (this.instance) { diff --git a/web/src/admin/stages/invitation/InvitationListPage.ts b/web/src/admin/stages/invitation/InvitationListPage.ts index 6e0c3928fffa..132928ae39e0 100644 --- a/web/src/admin/stages/invitation/InvitationListPage.ts +++ b/web/src/admin/stages/invitation/InvitationListPage.ts @@ -34,7 +34,10 @@ export class InvitationListPage extends TablePage { expandable = true; protected override searchEnabled = true; - public pageTitle = msg("Invitations"); + protected override entityLabel = { + singular: msg("Invitation"), + plural: msg("Invitations"), + }; public pageDescription = msg( "Create Invitation Links to enroll Users, and optionally force specific attributes of their account.", ); @@ -122,7 +125,7 @@ export class InvitationListPage extends TablePage { html`${item.createdBy?.username}`, html`${item.expires?.toLocaleString() || msg("-")}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Invitation")} + `; } diff --git a/web/src/admin/stages/prompt/PromptForm.ts b/web/src/admin/stages/prompt/PromptForm.ts index e7c02ec4021a..9350c99312dd 100644 --- a/web/src/admin/stages/prompt/PromptForm.ts +++ b/web/src/admin/stages/prompt/PromptForm.ts @@ -97,11 +97,7 @@ export class PromptForm extends ModelForm { }); } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated prompt.") - : msg("Successfully created prompt."); - } + protected override entityLabel = msg("prompt"); static styles: CSSResult[] = [...super.styles, PFGrid, PFTitle]; diff --git a/web/src/admin/stages/prompt/PromptListPage.ts b/web/src/admin/stages/prompt/PromptListPage.ts index 76fe85dc3861..2851992da30b 100644 --- a/web/src/admin/stages/prompt/PromptListPage.ts +++ b/web/src/admin/stages/prompt/PromptListPage.ts @@ -22,7 +22,10 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-stage-prompt-list") export class PromptListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Prompts"); + protected override entityLabel = { + singular: msg("Prompt"), + plural: msg("Prompts"), + }; public pageDescription = msg("Single Prompts that can be used for Prompt Stages."); public pageIcon = "pf-icon pf-icon-plugged"; @@ -79,7 +82,7 @@ export class PromptListPage extends TablePage { return html`
  • ${stage.name}
  • `; })}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Prompt")} + `; } diff --git a/web/src/admin/stages/prompt/PromptStageForm.ts b/web/src/admin/stages/prompt/PromptStageForm.ts index 27e83f77b570..807558bcaf0b 100644 --- a/web/src/admin/stages/prompt/PromptStageForm.ts +++ b/web/src/admin/stages/prompt/PromptStageForm.ts @@ -25,6 +25,8 @@ import { ifDefined } from "lit/directives/if-defined.js"; @customElement("ak-stage-prompt-form") export class PromptStageForm extends BaseStageForm { + protected override entityLabel = msg("Prompt Stage"); + loadInstance(pk: string): Promise { return new StagesApi(DEFAULT_CONFIG).stagesPromptStagesRetrieve({ stageUuid: pk, @@ -68,15 +70,15 @@ export class PromptStageForm extends BaseStageForm { > ${this.instance ? html` - ${msg("Create")} - ${msg("Create Prompt")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} ` : nothing} diff --git a/web/src/admin/tokens/TokenForm.ts b/web/src/admin/tokens/TokenForm.ts index 5c8d41bea739..71cda19247cb 100644 --- a/web/src/admin/tokens/TokenForm.ts +++ b/web/src/admin/tokens/TokenForm.ts @@ -27,11 +27,7 @@ export class TokenForm extends ModelForm { return token; } - getSuccessMessage(): string { - return this.instance - ? msg("Successfully updated token.") - : msg("Successfully created token."); - } + protected override entityLabel = msg("token"); async send(data: Token): Promise { if (this.instance?.identifier) { diff --git a/web/src/admin/tokens/TokenListPage.ts b/web/src/admin/tokens/TokenListPage.ts index 9734fcf7c66f..54020d30d470 100644 --- a/web/src/admin/tokens/TokenListPage.ts +++ b/web/src/admin/tokens/TokenListPage.ts @@ -28,7 +28,10 @@ import { customElement, property } from "lit/decorators.js"; @customElement("ak-token-list") export class TokenListPage extends TablePage { protected override searchEnabled = true; - public pageTitle = msg("Tokens"); + protected override entityLabel = { + singular: msg("Token"), + plural: msg("Tokens"), + }; public pageDescription = msg( "Tokens are used throughout authentik for Email validation stages, Recovery keys and API access.", ); @@ -85,10 +88,12 @@ export class TokenListPage extends TablePage { renderObjectCreate(): TemplateResult { return html` - ${msg("Create")} - ${msg("Create Token")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + `; } @@ -106,7 +111,7 @@ export class TokenListPage extends TablePage { html` ${!item.managed ? html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Token")} diff --git a/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts b/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts index f04e9867b652..08db5b81662f 100644 --- a/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts +++ b/web/src/admin/users/UserAssignedGlobalPermissionsTable.ts @@ -4,6 +4,7 @@ import "#elements/forms/ModalForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { groupBy } from "#common/utils"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -24,6 +25,11 @@ export class UserAssignedGlobalPermissionsTable extends Table { checkbox = true; clearOnRefresh = true; + protected override entityLabel: EntityLabel = { + singular: msg("permission"), + plural: msg("permissions"), + }; + async apiEndpoint(): Promise> { return new RbacApi(DEFAULT_CONFIG).rbacPermissionsList({ ...(await this.defaultEndpointConfig()), diff --git a/web/src/admin/users/UserAssignedObjectPermissionsTable.ts b/web/src/admin/users/UserAssignedObjectPermissionsTable.ts index 89a047d3515d..6d08a2de1325 100644 --- a/web/src/admin/users/UserAssignedObjectPermissionsTable.ts +++ b/web/src/admin/users/UserAssignedObjectPermissionsTable.ts @@ -2,6 +2,7 @@ import "#elements/forms/DeleteBulkForm"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { groupBy } from "#common/utils"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -21,6 +22,11 @@ export class UserAssignedObjectPermissionsTable extends Table> { return new RbacApi(DEFAULT_CONFIG).rbacPermissionsUsersList({ ...(await this.defaultEndpointConfig()), diff --git a/web/src/admin/users/UserDevicesTable.ts b/web/src/admin/users/UserDevicesTable.ts index b1618ba5a394..e4e8f19287b7 100644 --- a/web/src/admin/users/UserDevicesTable.ts +++ b/web/src/admin/users/UserDevicesTable.ts @@ -1,6 +1,7 @@ import "#elements/forms/DeleteBulkForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { deviceTypeName } from "#common/labels"; import { SentryIgnoredError } from "#common/sentry/index"; @@ -21,6 +22,11 @@ export class UserDeviceTable extends Table { checkbox = true; clearOnRefresh = true; + protected override entityLabel: EntityLabel = { + singular: msg("device"), + plural: msg("devices"), + }; + async apiEndpoint(): Promise> { return new AuthenticatorsApi(DEFAULT_CONFIG) .authenticatorsAdminAllList({ diff --git a/web/src/admin/users/UserForm.ts b/web/src/admin/users/UserForm.ts index bd54d78e75fe..3ab85762cc5f 100644 --- a/web/src/admin/users/UserForm.ts +++ b/web/src/admin/users/UserForm.ts @@ -7,10 +7,13 @@ import "#components/ak-radio-input"; import "#components/ak-switch-input"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { formatSuccessMessage } from "#common/i18n/actions"; +import { MessageLevel } from "#common/messages"; import { CodeMirrorMode } from "#elements/CodeMirror"; import { ModelForm } from "#elements/forms/ModelForm"; import { RadioOption } from "#elements/forms/Radio"; +import { APIMessage } from "#elements/messages/Message"; import { CoreApi, Group, User, UserTypeEnum } from "@goauthentik/api"; @@ -73,14 +76,20 @@ export class UserForm extends ModelForm { }); } - getSuccessMessage(): string { - if (this.instance) { - return msg("Successfully updated user."); - } + protected override formatAPISuccessMessage(): APIMessage | null { if (this.group) { - return msg(str`Successfully created user and added to group ${this.group.name}`); + return { + level: MessageLevel.success, + message: formatSuccessMessage( + msg(str`user and added to group ${this.group.name}`), + this.instance, + ), + }; } - return msg("Successfully created user."); + return { + level: MessageLevel.success, + message: formatSuccessMessage(msg("user"), this.instance), + }; } async send(data: User): Promise { diff --git a/web/src/admin/users/UserImpersonateForm.ts b/web/src/admin/users/UserImpersonateForm.ts index 09f9f40f48ea..e59b948b09ce 100644 --- a/web/src/admin/users/UserImpersonateForm.ts +++ b/web/src/admin/users/UserImpersonateForm.ts @@ -1,6 +1,7 @@ import "#components/ak-text-input"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionName } from "#common/i18n/verbs"; import { MessageLevel } from "#common/messages"; import { Form } from "#elements/forms/Form"; @@ -30,6 +31,8 @@ export class UserImpersonateForm extends Form { } } + protected override readonly actionName?: ActionName = "impersonate"; + protected override formatAPISuccessMessage(): APIMessage | null { return { level: MessageLevel.success, diff --git a/web/src/admin/users/UserListPage.ts b/web/src/admin/users/UserListPage.ts index 350d2d1406ca..820eec30532f 100644 --- a/web/src/admin/users/UserListPage.ts +++ b/web/src/admin/users/UserListPage.ts @@ -91,10 +91,14 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa supportsQL = true; protected override searchEnabled = true; - public override searchPlaceholder = msg("Search by username, email, etc..."); - public override searchLabel = msg("User Search"); + public override get searchPlaceholder() { + return msg("Search by username, email, etc..."); + } - public pageTitle = msg("Users"); + protected override entityLabel = { + singular: msg("User"), + plural: msg("Users"), + }; public pageDescription = ""; public pageIcon = "pf-icon pf-icon-user"; @@ -259,11 +263,11 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa html`${userTypeToLabel(item.type)}`, html`
    - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update User")} @@ -350,7 +354,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa .instancePk=${item.pk} > ${this.brand.flowRecovery @@ -359,7 +363,7 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa class="pf-m-secondary" .apiRequest=${() => requestRecoveryLink(item)} > - ${msg("Create recovery link")} + ${msg("Create Recovery Link")} ${item.email ? renderRecoveryEmailRequest(item) @@ -383,10 +387,12 @@ export class UserListPage extends WithBrandConfig(WithCapabilitiesConfig(TablePa renderObjectCreate(): TemplateResult { return html` - ${msg("Create User")} - ${msg("New User")} + ${this.createEntityLabel} + ${this.newEntityActionLabel} - + ${msg("Create Service Account")} diff --git a/web/src/admin/users/UserPasswordForm.ts b/web/src/admin/users/UserPasswordForm.ts index d8ab3a428ddf..4d94340dd535 100644 --- a/web/src/admin/users/UserPasswordForm.ts +++ b/web/src/admin/users/UserPasswordForm.ts @@ -43,9 +43,8 @@ export class UserPasswordForm extends Form { //#endregion - public override getSuccessMessage(): string { - return msg("Successfully updated password."); - } + protected override readonly actionName = "save"; + protected override entityLabel = msg("password"); public override async send(data: UserPasswordSetRequest): Promise { return new CoreApi(DEFAULT_CONFIG).coreUsersSetPasswordCreate({ diff --git a/web/src/admin/users/UserPermissionForm.ts b/web/src/admin/users/UserPermissionForm.ts index 1e6d1193904e..8fcca1c91c42 100644 --- a/web/src/admin/users/UserPermissionForm.ts +++ b/web/src/admin/users/UserPermissionForm.ts @@ -7,6 +7,7 @@ import "#elements/forms/Radio"; import "#elements/forms/SearchSelect/index"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { ActionName } from "#common/i18n/verbs"; import { ModelForm } from "#elements/forms/ModelForm"; @@ -34,10 +35,12 @@ export class UserPermissionForm extends ModelForm throw new Error("Method not implemented."); } - getSuccessMessage(): string { - return msg("Successfully assigned permission."); + protected override get actionName(): ActionName { + return "assign"; } + protected override entityLabel = msg("permission"); + async send(data: UserPermissionAssign) { await new RbacApi(DEFAULT_CONFIG).rbacPermissionsAssignedByUsersAssign({ id: this.userId || 0, diff --git a/web/src/admin/users/UserResetEmailForm.ts b/web/src/admin/users/UserResetEmailForm.ts index 4bc559e35722..9e50bc549fbb 100644 --- a/web/src/admin/users/UserResetEmailForm.ts +++ b/web/src/admin/users/UserResetEmailForm.ts @@ -24,9 +24,9 @@ export class UserResetEmailForm extends Form { data.id = this.user.pk; diff --git a/web/src/admin/users/UserViewPage.ts b/web/src/admin/users/UserViewPage.ts index 3abebeef7fef..aa8aa5e633a8 100644 --- a/web/src/admin/users/UserViewPage.ts +++ b/web/src/admin/users/UserViewPage.ts @@ -27,6 +27,9 @@ import "./UserDevicesTable.js"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; import { PFSize } from "#common/enums"; +import { formatEditMessage } from "#common/i18n/actions"; +import { EntityLabel } from "#common/i18n/nouns"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { userTypeToLabel } from "#common/labels"; import { me } from "#common/users"; @@ -65,6 +68,11 @@ import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css"; @customElement("ak-user-view") export class UserViewPage extends WithCapabilitiesConfig(AKElement) { + protected entityLabel: EntityLabel = { + singular: msg("User"), + plural: msg("Users"), + }; + @property({ type: Number }) set userId(id: number) { me().then((me) => { @@ -162,11 +170,11 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) { return html`
    - ${msg("Update")} - ${msg("Update User")} + ${ActionTenseRecord.apply.present} + ${formatEditMessage(this.entityLabel)} ; + +export type ActionTenseRecord = typeof ActionTenseRecord; + +export type ActionName = keyof ActionTenseRecord; diff --git a/web/src/common/strings.ts b/web/src/common/strings.ts index b9287ada8cd8..f45f8a21a0a9 100644 --- a/web/src/common/strings.ts +++ b/web/src/common/strings.ts @@ -2,12 +2,7 @@ * @file String utilities. */ -import { msg } from "@lit/localize"; - -const truncationEllipsis = msg("...", { - desc: "Truncation ellipsis", - id: "ellipsis", -}); +import { truncationEllipsis } from "#common/i18n/punctuation"; /** * Truncate a string based on character count. diff --git a/web/src/components/events/ObjectChangelog.ts b/web/src/components/events/ObjectChangelog.ts index f61a91dec4d4..c5006b753f27 100644 --- a/web/src/components/events/ObjectChangelog.ts +++ b/web/src/components/events/ObjectChangelog.ts @@ -6,6 +6,7 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EventWithContext } from "#common/events"; +import { EntityLabel } from "#common/i18n/nouns"; import { actionToLabel } from "#common/labels"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; @@ -15,7 +16,7 @@ import { EventGeo, renderEventUser } from "#admin/events/utils"; import { Event, EventsApi } from "@goauthentik/api"; -import { msg } from "@lit/localize"; +import { msg, str } from "@lit/localize"; import { html, PropertyValues, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; @@ -23,6 +24,11 @@ import { customElement, property } from "lit/decorators.js"; export class ObjectChangelog extends Table { expandable = true; + protected override entityLabel: EntityLabel = { + singular: msg("event"), + plural: msg("events"), + }; + @property() order = "-created"; @@ -89,7 +95,7 @@ export class ObjectChangelog extends Table { renderEmpty(): TemplateResult { return super.renderEmpty( html`${msg("No Events found.")} + >${msg(msg(str`No ${this.entityLabel.plural.toLowerCase()} found.`))}
    ${msg("No matching events could be found.")}
    `, ); diff --git a/web/src/components/events/UserEvents.ts b/web/src/components/events/UserEvents.ts index 549fc2b00364..58167f29df6f 100644 --- a/web/src/components/events/UserEvents.ts +++ b/web/src/components/events/UserEvents.ts @@ -6,6 +6,7 @@ import "#elements/buttons/SpinnerButton/index"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EventWithContext } from "#common/events"; +import { EntityLabel } from "#common/i18n/nouns"; import { actionToLabel } from "#common/labels"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; @@ -15,7 +16,7 @@ import { renderEventUser } from "#admin/events/utils"; import { Event, EventsApi } from "@goauthentik/api"; -import { msg } from "@lit/localize"; +import { msg, str } from "@lit/localize"; import { html, TemplateResult } from "lit"; import { customElement, property } from "lit/decorators.js"; @@ -23,6 +24,11 @@ import { customElement, property } from "lit/decorators.js"; export class UserEvents extends Table { expandable = true; + protected override entityLabel: EntityLabel = { + singular: msg("event"), + plural: msg("events"), + }; + @property() order = "-created"; @@ -63,7 +69,7 @@ export class UserEvents extends Table { renderEmpty(): TemplateResult { return super.renderEmpty( html`${msg("No Events found.")} + >${msg(str`No ${this.entityLabel.plural.toLowerCase()} found.`)}
    ${msg("No matching events could be found.")}
    `, ); diff --git a/web/src/elements/events/LogViewer.ts b/web/src/elements/events/LogViewer.ts index 02f1fa6271e7..afcfced15b87 100644 --- a/web/src/elements/events/LogViewer.ts +++ b/web/src/elements/events/LogViewer.ts @@ -1,6 +1,7 @@ import "#components/ak-status-label"; import "#elements/EmptyState"; +import { EntityLabel } from "#common/i18n/nouns"; import { formatElapsedTime } from "#common/temporal"; import { PaginatedResponse, Table, TableColumn } from "#elements/table/Table"; @@ -22,6 +23,11 @@ export class LogViewer extends Table { expandable = true; paginated = false; + protected override entityLabel: EntityLabel = { + singular: msg("log message"), + plural: msg("log messages"), + }; + static styles: CSSResult[] = [...super.styles, PFDescriptionList]; async apiEndpoint(): Promise> { diff --git a/web/src/elements/forms/DeleteBulkForm.ts b/web/src/elements/forms/DeleteBulkForm.ts index bbd0800ba992..c296f9a16303 100644 --- a/web/src/elements/forms/DeleteBulkForm.ts +++ b/web/src/elements/forms/DeleteBulkForm.ts @@ -2,6 +2,7 @@ import "#elements/buttons/SpinnerButton/index"; import { EVENT_REFRESH } from "#common/constants"; import { PFSize } from "#common/enums"; +import { EntityLabel } from "#common/i18n/nouns"; import { MessageLevel } from "#common/messages"; import { ModalButton } from "#elements/buttons/ModalButton"; @@ -24,6 +25,11 @@ type BulkDeleteMetadata = { key: string; value: string }[]; export class DeleteObjectsTable extends Table { paginated = false; + protected override entityLabel: EntityLabel = { + singular: msg("object"), + plural: msg("objects"), + }; + @property({ attribute: false }) objects: T[] = []; diff --git a/web/src/elements/forms/Form.ts b/web/src/elements/forms/Form.ts index d9f3d6f763f9..7d5e31e1db55 100644 --- a/web/src/elements/forms/Form.ts +++ b/web/src/elements/forms/Form.ts @@ -5,6 +5,14 @@ import { pluckErrorDetail, pluckFallbackFieldErrors, } from "#common/errors/network"; +import { + formatCreateMessage, + formatEditMessage, + formatNewMessage, + formatSuccessActionMessage, +} from "#common/i18n/actions"; +import { DefaultEntityLabel } from "#common/i18n/nouns"; +import { ActionName, ActionTenseRecord } from "#common/i18n/verbs"; import { MessageLevel } from "#common/messages"; import { dateToUTC } from "#common/temporal"; @@ -221,7 +229,7 @@ export abstract class Form> extends AKElement { //#region Properties - @property({ type: String }) + @property({ type: String, attribute: "success-message" }) public successMessage?: string; @property({ type: String }) @@ -266,19 +274,52 @@ export abstract class Form> extends AKElement { } /** - * An overridable method for returning a success message after a successful submission. - * - * @deprecated Use `formatAPISuccessMessage` instead. + * The label for the type of entity being listed. + */ + protected entityLabel: string = DefaultEntityLabel.singular; + + /** + * The label for the action performed when submitting the form. + * @abstract + */ + protected abstract actionName?: ActionName; + + /** + * Label for opening a "new entity" modal or page. + */ + protected get newEntityActionLabel(): string { + return formatNewMessage(this.entityLabel); + } + + /** + * Label for a "create entity" button. + */ + protected get createEntityLabel(): string { + return formatCreateMessage(this.entityLabel); + } + + /** + * Label for an "edit entity" button. */ - protected getSuccessMessage(): string | undefined { - return this.successMessage; + protected get editEntityLabel(): string { + return formatEditMessage(this.entityLabel); + } + + /** + * Label for an "Apply" button. + */ + protected get updateEntityLabel(): string { + return ActionTenseRecord.apply.present; } /** * An overridable method for returning a formatted message after a successful submission. */ protected formatAPISuccessMessage(response: unknown): APIMessage | null { - const message = this.getSuccessMessage(); + const actionName: ActionName = this.actionName || "create"; + const action = ActionTenseRecord[actionName].past; + + const message = formatSuccessActionMessage(action, this.entityLabel); if (!message) return null; diff --git a/web/src/elements/forms/ModelForm.ts b/web/src/elements/forms/ModelForm.ts index 025a315d00c6..185295b2ebbf 100644 --- a/web/src/elements/forms/ModelForm.ts +++ b/web/src/elements/forms/ModelForm.ts @@ -1,6 +1,7 @@ import "#elements/EmptyState"; import { EVENT_REFRESH } from "#common/constants"; +import { ActionName } from "#common/i18n/verbs"; import { Form } from "#elements/forms/Form"; import { SlottedTemplateResult } from "#elements/types"; @@ -21,6 +22,10 @@ export abstract class ModelForm extends Form */ protected abstract loadInstance(pk: PKT): Promise; + protected get actionName(): ActionName { + return this.instance ? "update" : "create"; + } + /** * An overridable method for loading any data, beyond the instance. * diff --git a/web/src/elements/forms/ProxyForm.ts b/web/src/elements/forms/ProxyForm.ts index 3b92a083d2cf..047f6e85230e 100644 --- a/web/src/elements/forms/ProxyForm.ts +++ b/web/src/elements/forms/ProxyForm.ts @@ -40,10 +40,6 @@ export abstract class ProxyForm extends Form { this.innerElement?.reset(); } - public override getSuccessMessage(): string { - return this.innerElement?.getSuccessMessage() || ""; - } - public override async requestUpdate(name?: PropertyKey, oldValue?: unknown): Promise { const result = super.requestUpdate(name, oldValue); diff --git a/web/src/elements/oauth/UserAccessTokenList.ts b/web/src/elements/oauth/UserAccessTokenList.ts index bef7f983506e..b580f3ce0515 100644 --- a/web/src/elements/oauth/UserAccessTokenList.ts +++ b/web/src/elements/oauth/UserAccessTokenList.ts @@ -4,6 +4,7 @@ import "#elements/chips/ChipGroup"; import "#elements/forms/DeleteBulkForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -20,6 +21,11 @@ import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css"; export class UserOAuthAccessTokenList extends Table { expandable = true; + protected override entityLabel: EntityLabel = { + singular: msg("access token"), + plural: msg("access tokens"), + }; + @property({ type: Number }) userId?: number; diff --git a/web/src/elements/oauth/UserRefreshTokenList.ts b/web/src/elements/oauth/UserRefreshTokenList.ts index a90ac8d38879..e1576bb6f788 100644 --- a/web/src/elements/oauth/UserRefreshTokenList.ts +++ b/web/src/elements/oauth/UserRefreshTokenList.ts @@ -4,6 +4,7 @@ import "#elements/chips/ChipGroup"; import "#elements/forms/DeleteBulkForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -20,6 +21,11 @@ import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css"; export class UserOAuthRefreshTokenList extends Table { expandable = true; + protected override entityLabel: EntityLabel = { + singular: msg("refresh token"), + plural: msg("refresh tokens"), + }; + @property({ type: Number }) userId?: number; diff --git a/web/src/elements/sync/SyncObjectForm.ts b/web/src/elements/sync/SyncObjectForm.ts index c2b8d9b94e6d..31b10d118ea9 100644 --- a/web/src/elements/sync/SyncObjectForm.ts +++ b/web/src/elements/sync/SyncObjectForm.ts @@ -44,9 +44,9 @@ export class SyncObjectForm extends Form { return Promise.reject(); }; - getSuccessMessage(): string { - return msg("Successfully triggered sync."); - } + protected override readonly actionName = "trigger"; + + protected override entityLabel = msg("sync"); async send(data: SyncObjectRequest): Promise { data.syncObjectModel = this.model; diff --git a/web/src/elements/table/Table.ts b/web/src/elements/table/Table.ts index 0cefaef2153f..ad27318c4e6a 100644 --- a/web/src/elements/table/Table.ts +++ b/web/src/elements/table/Table.ts @@ -11,6 +11,10 @@ import { renderTableColumn, TableColumn } from "./TableColumn.js"; import { EVENT_REFRESH } from "#common/constants"; import { APIError, parseAPIResponseError, pluckErrorDetail } from "#common/errors/network"; +import { formatCreateMessage, formatEditMessage, formatNewMessage } from "#common/i18n/actions"; +import { DefaultEntityLabel, EntityLabel } from "#common/i18n/nouns"; +import { truncationEllipsis } from "#common/i18n/punctuation"; +import { ActionTenseRecord } from "#common/i18n/verbs"; import { uiConfig } from "#common/ui/config"; import { GroupResult } from "#common/utils"; @@ -28,7 +32,6 @@ import { msg, str } from "@lit/localize"; import { css, CSSResult, html, nothing, PropertyValues, TemplateResult } from "lit"; import { property, state } from "lit/decorators.js"; import { classMap } from "lit/directives/class-map.js"; -import { ifDefined } from "lit/directives/if-defined.js"; import { createRef, ref } from "lit/directives/ref.js"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; @@ -268,14 +271,63 @@ export abstract class Table @state() protected lastRefreshedAt: Date | null = null; + /** + * The label for the type of entity being listed. + * + * Typically used in the empty state. + */ + protected abstract entityLabel: EntityLabel; + + protected get qlAvailable(): boolean { + return this.supportsQL && this.hasEnterpriseLicense; + } + + protected get searchPlaceholder(): string { + if (this.qlAvailable) { + return msg("Search by ") + truncationEllipsis; + } else if (this.entityLabel !== DefaultEntityLabel) { + return msg(str`Search ${this.entityLabel.plural.toLowerCase()}`) + truncationEllipsis; + } + + return msg("Search") + truncationEllipsis; + } + + /** + * Label for opening a "new entity" modal or page. + */ + protected get newEntityActionLabel(): string { + return formatNewMessage(this.entityLabel); + } + + /** + * Label for a "create entity" button. + */ + protected get createEntityLabel(): string { + return formatCreateMessage(this.entityLabel); + } + + /** + * Label for an "edit entity" button. + */ + protected get editEntityLabel(): string { + return formatEditMessage(this.entityLabel); + } + + /** + * Label for an "Apply" button. + */ + protected get updateEntityLabel(): string { + return ActionTenseRecord.apply.present; + } + #pageParam = `${this.tagName.toLowerCase()}-page`; #searchParam = `${this.tagName.toLowerCase()}-search`; + //#region Properties + @property({ type: Boolean }) public supportsQL: boolean = false; - //#region Properties - @property({ type: String }) public toolbarLabel: string | null = null; @@ -357,12 +409,6 @@ export abstract class Table @property({ type: Boolean }) public expandable = false; - @property({ attribute: false }) - public searchLabel?: string; - - @property({ attribute: false }) - public searchPlaceholder?: string; - //#endregion //#region Lifecycle @@ -492,7 +538,11 @@ export abstract class Table
    ${inner ?? html`${msg("No objects found.")} + >${msg( + str`No ${this.entityLabel.plural.toLowerCase()} found.`, + )}
    ${this.renderObjectCreate()}
    `}
    @@ -519,7 +569,7 @@ export abstract class Table if (!this.error) return nothing; return html`${msg("Failed to fetch objects.")} + >${msg(str`Failed to fetch ${this.entityLabel.plural}.`)}
    ${pluckErrorDetail(this.error)}
    `; } @@ -839,14 +889,14 @@ export abstract class Table return nothing; } - const isQL = this.supportsQL && this.hasEnterpriseLicense; + const searchLabel = msg(str`${this.entityLabel.singular} search`); return html` extends Table { `, ]; - //#region Abstract properties - /** * The title of the page. - * @abstract */ - public abstract pageTitle: string; + public get pageTitle(): string { + return this.entityLabel.plural; + } + + //#region Abstract properties /** * The description of the page. @@ -115,7 +116,7 @@ export abstract class TablePage extends Table { ${inner ? inner : html`${msg("No objects found.")} + >${msg(str`No ${this.entityLabel.plural.toLowerCase()} found.`)}
    ${this.searchEnabled ? this.renderEmptyClearSearch() : nothing}
    diff --git a/web/src/elements/tasks/ScheduleForm.ts b/web/src/elements/tasks/ScheduleForm.ts index 954e16fbe266..25b14adf4bc9 100644 --- a/web/src/elements/tasks/ScheduleForm.ts +++ b/web/src/elements/tasks/ScheduleForm.ts @@ -6,8 +6,10 @@ import "#elements/forms/ModalForm"; import "#elements/forms/ProxyForm"; import { DEFAULT_CONFIG } from "#common/api/config"; +import { MessageLevel } from "#common/messages"; import { ModelForm } from "#elements/forms/ModelForm"; +import { APIMessage } from "#elements/messages/Message"; import { Schedule, TasksApi } from "@goauthentik/api"; @@ -24,11 +26,15 @@ export class ScheduleForm extends ModelForm { }); } - getSuccessMessage(): string { + protected override formatAPISuccessMessage(): APIMessage | null { if (!this.instance) { - return ""; + return null; } - return msg("Successfully updated schedule."); + + return { + level: MessageLevel.success, + message: msg("Successfully updated schedule."), + }; } async send(data: Schedule): Promise { diff --git a/web/src/elements/tasks/ScheduleList.ts b/web/src/elements/tasks/ScheduleList.ts index 3c933ca6c3e0..efba5b6af36b 100644 --- a/web/src/elements/tasks/ScheduleList.ts +++ b/web/src/elements/tasks/ScheduleList.ts @@ -9,6 +9,7 @@ import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { DEFAULT_CONFIG } from "#common/api/config"; import { EVENT_REFRESH } from "#common/constants"; +import { EntityLabel } from "#common/i18n/nouns"; import { PaginatedResponse, Table, TableColumn, Timestamp } from "#elements/table/Table"; import { SlottedTemplateResult } from "#elements/types"; @@ -26,6 +27,11 @@ export class ScheduleList extends Table { expandable = true; clearOnRefresh = true; + protected override entityLabel: EntityLabel = { + singular: msg("schedule"), + plural: msg("schedules"), + }; + protected override searchEnabled = true; @property() @@ -136,7 +142,7 @@ export class ScheduleList extends Table { - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Schedule")} - ${msg("Create")} - ${msg("Create App password")} + ${formatCreateMessage(msg("App Password"))} + ${formatNewMessage(msg("App Password"))} ${super.renderToolbar()} @@ -151,7 +158,7 @@ export class UserTokenList extends Table { html`${item.identifier}`, html` - ${msg("Update")} + ${this.updateEntityLabel} ${msg("Update Token")}