diff --git a/application/account-management/WebApp/federated-modules/common/AcceptInvitationDialog.tsx b/application/account-management/WebApp/federated-modules/common/AcceptInvitationDialog.tsx index 73ceb5589a..6c0844d0e7 100644 --- a/application/account-management/WebApp/federated-modules/common/AcceptInvitationDialog.tsx +++ b/application/account-management/WebApp/federated-modules/common/AcceptInvitationDialog.tsx @@ -54,7 +54,7 @@ export function AcceptInvitationDialog({ return ( - + onOpenChange(false)} className="absolute top-2 right-2 h-10 w-10 cursor-pointer p-2 hover:bg-muted" diff --git a/application/account-management/WebApp/federated-modules/common/SupportDialog.tsx b/application/account-management/WebApp/federated-modules/common/SupportDialog.tsx index 4507bd9146..47f0970adc 100644 --- a/application/account-management/WebApp/federated-modules/common/SupportDialog.tsx +++ b/application/account-management/WebApp/federated-modules/common/SupportDialog.tsx @@ -16,7 +16,7 @@ export function SupportDialog({ children }: Readonly) { {children} - + {({ close }) => ( <> diff --git a/application/account-management/WebApp/federated-modules/common/UserProfileModal.tsx b/application/account-management/WebApp/federated-modules/common/UserProfileModal.tsx index 3604092bf3..ae02fb7454 100644 --- a/application/account-management/WebApp/federated-modules/common/UserProfileModal.tsx +++ b/application/account-management/WebApp/federated-modules/common/UserProfileModal.tsx @@ -135,7 +135,10 @@ export default function UserProfileModal({ isOpen, onOpenChange }: Readonly ) : ( - + Update your profile picture and personal details here.}> @@ -237,6 +240,7 @@ export default function UserProfileModal({ isOpen, onOpenChange }: Readonly diff --git a/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx b/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx index 1002258f90..b41ddf5f12 100644 --- a/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx +++ b/application/account-management/WebApp/routes/(index)/-components/FooterSection.tsx @@ -12,7 +12,7 @@ export function FooterSection() {
Technology that has your back.
- + {/* Button component is used to display a call to action */}
diff --git a/application/account-management/WebApp/routes/admin/account/index.tsx b/application/account-management/WebApp/routes/admin/account/index.tsx index f7e48ef195..b0576ae943 100644 --- a/application/account-management/WebApp/routes/admin/account/index.tsx +++ b/application/account-management/WebApp/routes/admin/account/index.tsx @@ -335,6 +335,7 @@ export function AccountSettings() { isDisabled={updateCurrentTenantMutation.isPending} isReadOnly={!isOwner} label={t`Account name`} + tooltip={isOwner ? t`The name of your account, shown to users and in email notifications` : undefined} description={!isOwner ? t`Only account owners can modify the account name` : undefined} validationBehavior="aria" /> diff --git a/application/account-management/WebApp/routes/admin/users/-components/InviteUserDialog.tsx b/application/account-management/WebApp/routes/admin/users/-components/InviteUserDialog.tsx index 85a05227ca..145d212bb5 100644 --- a/application/account-management/WebApp/routes/admin/users/-components/InviteUserDialog.tsx +++ b/application/account-management/WebApp/routes/admin/users/-components/InviteUserDialog.tsx @@ -34,7 +34,7 @@ export default function InviteUserDialog({ isOpen, onOpenChange }: Readonly - + onOpenChange(false)} className="absolute top-2 right-2 h-10 w-10 cursor-pointer p-2 hover:bg-muted" diff --git a/application/account-management/WebApp/routes/admin/users/-components/TenantNameRequiredDialog.tsx b/application/account-management/WebApp/routes/admin/users/-components/TenantNameRequiredDialog.tsx index 13e11c6213..797e8a5450 100644 --- a/application/account-management/WebApp/routes/admin/users/-components/TenantNameRequiredDialog.tsx +++ b/application/account-management/WebApp/routes/admin/users/-components/TenantNameRequiredDialog.tsx @@ -16,7 +16,7 @@ interface TenantNameRequiredDialogProps { export function TenantNameRequiredDialog({ isOpen, onOpenChange }: Readonly) { return ( - + {({ close }) => ( <> diff --git a/application/account-management/WebApp/routes/signup/index.tsx b/application/account-management/WebApp/routes/signup/index.tsx index 16ae14b658..b8a710b6bd 100644 --- a/application/account-management/WebApp/routes/signup/index.tsx +++ b/application/account-management/WebApp/routes/signup/index.tsx @@ -93,6 +93,7 @@ export function StartSignupForm() { name="region" selectedKey="europe" label={t`Region`} + tooltip={t`Data storage location for privacy and compliance. This cannot be changed later.`} description={t`This is the region where your data is stored`} isRequired={true} className="flex w-full flex-col" diff --git a/application/account-management/WebApp/rsbuild.config.ts b/application/account-management/WebApp/rsbuild.config.ts index 8f27782645..b94eccd039 100644 --- a/application/account-management/WebApp/rsbuild.config.ts +++ b/application/account-management/WebApp/rsbuild.config.ts @@ -36,8 +36,7 @@ export default defineConfig({ "./FederatedTopMenu": "./federated-modules/topMenu/FederatedTopMenu.tsx", "./AuthSyncModal": "./federated-modules/common/AuthSyncModal.tsx", "./translations/en-US": "./shared/translations/locale/en-US.ts", - "./translations/da-DK": "./shared/translations/locale/da-DK.ts", - "./translations/nl-NL": "./shared/translations/locale/nl-NL.ts" + "./translations/da-DK": "./shared/translations/locale/da-DK.ts" } }) ] diff --git a/application/account-management/WebApp/shared/translations/locale/da-DK.po b/application/account-management/WebApp/shared/translations/locale/da-DK.po index b8ec297e31..22a5e0fa6c 100644 --- a/application/account-management/WebApp/shared/translations/locale/da-DK.po +++ b/application/account-management/WebApp/shared/translations/locale/da-DK.po @@ -176,6 +176,9 @@ msgstr "Mørk" msgid "Dashboard" msgstr "Dashboard" +msgid "Data storage location for privacy and compliance. This cannot be changed later." +msgstr "Datalagringsplacering for privatliv og compliance. Dette kan ikke ændres senere." + msgid "Decline" msgstr "Afslå" @@ -457,6 +460,9 @@ msgstr "System" msgid "Terms of use" msgstr "Brugsvilkår" +msgid "The name of your account, shown to users and in email notifications" +msgstr "Navnet på din konto, vist til brugere og i e-mail-notifikationer" + msgid "Theme" msgstr "Tema" @@ -579,6 +585,9 @@ msgstr "Din konto blev skiftet i en anden browsertab." msgid "Your account was switched to <0>{newTenantName} in another browser tab." msgstr "Din konto blev skiftet til <0>{newTenantName} i en anden browsertab." +msgid "Your professional title or role" +msgstr "Din professionelle titel eller rolle" + msgid "Your team needs to know who's inviting them. Add an account name to get started." msgstr "Dit team skal vide, hvem der inviterer dem. Tilføj et kontonavn for at komme i gang." diff --git a/application/account-management/WebApp/shared/translations/locale/en-US.po b/application/account-management/WebApp/shared/translations/locale/en-US.po index 9d61f5c1c0..ce640c089f 100644 --- a/application/account-management/WebApp/shared/translations/locale/en-US.po +++ b/application/account-management/WebApp/shared/translations/locale/en-US.po @@ -176,6 +176,9 @@ msgstr "Dark" msgid "Dashboard" msgstr "Dashboard" +msgid "Data storage location for privacy and compliance. This cannot be changed later." +msgstr "Data storage location for privacy and compliance. This cannot be changed later." + msgid "Decline" msgstr "Decline" @@ -457,6 +460,9 @@ msgstr "System" msgid "Terms of use" msgstr "Terms of use" +msgid "The name of your account, shown to users and in email notifications" +msgstr "The name of your account, shown to users and in email notifications" + msgid "Theme" msgstr "Theme" @@ -579,6 +585,9 @@ msgstr "Your account was switched in another browser tab." msgid "Your account was switched to <0>{newTenantName} in another browser tab." msgstr "Your account was switched to <0>{newTenantName} in another browser tab." +msgid "Your professional title or role" +msgstr "Your professional title or role" + msgid "Your team needs to know who's inviting them. Add an account name to get started." msgstr "Your team needs to know who's inviting them. Add an account name to get started." diff --git a/application/account-management/WebApp/shared/translations/locale/nl-NL.po b/application/account-management/WebApp/shared/translations/locale/nl-NL.po deleted file mode 100644 index 746ec23c4a..0000000000 --- a/application/account-management/WebApp/shared/translations/locale/nl-NL.po +++ /dev/null @@ -1,592 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-07 23:40+0200\n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: nl-NL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"X-Generator: @lingui/cli\n" - -#. placeholder {0}: users.length -msgid "{0} users deleted successfully" -msgstr "{0} gebruikers succesvol verwijderd" - -msgid "A different user logged in from another browser tab." -msgstr "Een andere gebruiker heeft ingelogd vanuit een ander browsertabblad." - -msgid "A new verification code has been sent to your email." -msgstr "Een nieuwe verificatiecode is naar je e-mail verzonden." - -msgid "Accept invitation" -msgstr "Uitnodiging accepteren" - -msgid "Accepting..." -msgstr "Accepteren..." - -msgid "Account" -msgstr "Account" - -msgid "Account information" -msgstr "Accountinformatie" - -msgid "Account name" -msgstr "Accountnaam" - -msgid "Account name updated successfully" -msgstr "Accountnaam succesvol bijgewerkt" - -msgid "Account settings" -msgstr "Accountinstellingen" - -msgid "Account switched" -msgstr "Account gewisseld" - -msgid "Active" -msgstr "Actief" - -msgid "Active users" -msgstr "Actieve gebruikers" - -msgid "Active users in the past 30 days" -msgstr "Actieve gebruikers in de afgelopen 30 dagen" - -msgid "Add more in the Users menu" -msgstr "Nieuwe toevoegen via het Gebruikers menu" - -msgid "Add profile picture" -msgstr "Profielfoto toevoegen" - -msgid "Add your account name" -msgstr "Voeg je accountnaam toe" - -msgid "Admin" -msgstr "Beheerder" - -msgid "All users" -msgstr "Alle gebruikers" - -msgid "An email with login instructions will be sent to the user." -msgstr "Een e-mail met inloginstructies zal naar de gebruiker worden gestuurd." - -#. placeholder {0}: error.message -msgid "An error occurred while processing your request. {0}" -msgstr "Er is een fout opgetreden bij het verwerken van uw verzoek. {0}" - -msgid "An unexpected state occurred. Please reload the page." -msgstr "Er is een onverwachte fout opgetreden. Herlaad de pagina." - -msgid "Any role" -msgstr "Elke rol" - -msgid "Any status" -msgstr "Elke status" - -#. placeholder {0}: users.length -msgid "Are you sure you want to delete <0>{0} users?" -msgstr "Weet je zeker dat je <0>{0} gebruikers wilt verwijderen?" - -msgid "Are you sure you want to delete <0>{userDisplayName}?" -msgstr "Weet je zeker dat je <0>{userDisplayName} wilt verwijderen?" - -msgid "Authentication is shared across all tabs." -msgstr "Authenticatie wordt gedeeld over alle tabbladen." - -msgid "Authentication is shared across all tabs. To use multiple accounts simultaneously, please use different browsers." -msgstr "Authenticatie wordt gedeeld over alle tabbladen. Gebruik verschillende browsers om meerdere accounts tegelijk te gebruiken." - -msgid "Back Office" -msgstr "Backoffice" - -msgid "Back to login" -msgstr "Terug naar inloggen" - -msgid "Back to signup" -msgstr "Terug naar aanmelding" - -msgid "By continuing, you accept our policies" -msgstr "Door te gaan, accepteer je onze voorwaarden" - -msgid "Can't find your code?" -msgstr "Kun je je code niet vinden?" - -msgid "Cancel" -msgstr "Annuleren" - -msgid "Change language" -msgstr "Taal wijzigen" - -msgid "Change logo" -msgstr "Logo wijzigen" - -msgid "Change profile picture" -msgstr "Profielfoto wijzigen" - -msgid "Change role" -msgstr "Rol wijzigen" - -msgid "Change theme" -msgstr "Thema wijzigen" - -msgid "Change user role" -msgstr "Gebruikersrol wijzigen" - -#. placeholder {0}: `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim() || user.email -msgid "Change user role for {0}" -msgstr "Gebruikersrol wijzigen voor {0}" - -msgid "Check your spam folder." -msgstr "Controleer je spammap." - -msgid "Clear" -msgstr "Wissen" - -msgid "Clear filters" -msgstr "Filters wissen" - -msgid "Close" -msgstr "Sluiten" - -msgid "Close user profile" -msgstr "Gebruikersprofiel sluiten" - -msgid "Contact support" -msgstr "Ondersteuning contacteren" - -msgid "Continue" -msgstr "Verder" - -msgid "Create your account" -msgstr "Maak je account aan" - -msgid "Created" -msgstr "Aangemaakt" - -msgid "Danger zone" -msgstr "Gevaarzone" - -msgid "Dark" -msgstr "Donker" - -msgid "Dashboard" -msgstr "Dashboard" - -msgid "Decline" -msgstr "Weigeren" - -msgid "Declining..." -msgstr "Weigeren..." - -msgid "Delete" -msgstr "Verwijderen" - -#. placeholder {0}: selectedUsers.length -msgid "Delete {0} users" -msgstr "Verwijder {0} gebruikers" - -msgid "Delete account" -msgstr "Account verwijderen" - -msgid "Delete user" -msgstr "Gebruiker verwijderen" - -msgid "Delete users" -msgstr "Gebruikers verwijderen" - -msgid "Delete your account and all data. This action is irreversible—proceed with caution." -msgstr "Verwijder je account en alle gegevens. Deze actie is onomkeerbaar—ga zorgvuldig te werk." - -msgid "Different user logged in" -msgstr "Andere gebruiker ingelogd" - -msgid "Do you already have an account?" -msgstr "Heb je al een account?" - -msgid "Don't have an account? <0>Create one" -msgstr "Heb je geen account? <0>Maak er één aan" - -msgid "E.g. Alex" -msgstr "Bijv. Alex" - -msgid "E.g. Software engineer" -msgstr "Bijv. Software engineer" - -msgid "E.g. Taylor" -msgstr "Bijv. Taylor" - -msgid "Edit" -msgstr "Bewerken" - -msgid "Edit profile" -msgstr "Profiel bewerken" - -msgid "Email" -msgstr "E-mailadres" - -msgid "Enter your email below to log in" -msgstr "Voer je e-mailadres hieronder in om in te loggen" - -msgid "Enter your verification code" -msgstr "Voer je verificatiecode in" - -msgid "Error: Something went wrong!" -msgstr "Fout: Er is iets misgegaan!" - -msgid "Europe" -msgstr "Europa" - -msgid "Feel free to reach out with any questions or issues you may have." -msgstr "Voel je vrij om contact op te nemen met vragen of problemen die je hebt." - -msgid "Fetching data..." -msgstr "Gegevens ophalen..." - -msgid "Filters" -msgstr "Filters" - -msgid "First name" -msgstr "Voornaam" - -msgid "Go to account settings" -msgstr "Ga naar accountinstellingen" - -msgid "Help your team recognize your invites" -msgstr "Help je team je uitnodigingen te herkennen" - -msgid "Here's your overview of what's happening." -msgstr "Hier is je overzicht van wat er gebeurt." - -msgid "Hi! Welcome back" -msgstr "Hallo! Welkom terug" - -msgid "Home" -msgstr "Home" - -msgid "Image must be smaller than 1 MB." -msgstr "Afbeelding moet kleiner zijn dan 1 MB." - -msgid "Invitation pending" -msgstr "Uitnodiging in behandeling" - -msgid "Invite user" -msgstr "Gebruiker uitnodigen" - -msgid "Invited users" -msgstr "Uitgenodigde gebruikers" - -msgid "Language" -msgstr "Taal" - -msgid "Last name" -msgstr "Achternaam" - -msgid "Light" -msgstr "Licht" - -msgid "Log in" -msgstr "Inloggen" - -msgid "Log out" -msgstr "Uitloggen" - -msgid "Logged out" -msgstr "Uitgelogd" - -msgid "Login verification code" -msgstr "Verificatiecode voor inloggen" - -msgid "Logo" -msgstr "Logo" - -msgid "Logo removed successfully" -msgstr "Logo succesvol verwijderd" - -msgid "Logo uploaded successfully" -msgstr "Logo succesvol geüpload" - -msgid "Manage your account here." -msgstr "Beheer je account hier." - -msgid "Manage your users and permissions here." -msgstr "Beheer je gebruikers en rechten hier." - -msgid "Member" -msgstr "Lid" - -msgid "Modified" -msgstr "Gewijzigd" - -msgid "Modified date" -msgstr "Gewijzigde datum" - -msgid "Name" -msgstr "Naam" - -msgid "Navigation" -msgstr "Navigatie" - -msgid "Need help? Our support team is here to assist you." -msgstr "Hulp nodig? Ons ondersteuningsteam staat voor je klaar." - -msgid "Next" -msgstr "Volgende" - -msgid "OK" -msgstr "OK" - -msgid "Only account owners can modify the account name" -msgstr "Alleen accounteigenaren kunnen de accountnaam wijzigen" - -msgid "Open navigation menu" -msgstr "Navigatiemenu openen" - -msgid "Organization" -msgstr "Organisatie" - -msgid "Our support team will assist you with the account deletion process and ensure all your data is properly removed." -msgstr "Ons ondersteuningsteam helpt je met het verwijderen van je account en al je gegevens." - -msgid "Owner" -msgstr "Eigenaar" - -msgid "Pending" -msgstr "In behandeling" - -msgid "PlatformPlatform" -msgstr "PlatformPlatform" - -msgid "Please check your email for a verification code sent to <0>{email}" -msgstr "Controleer je e-mail voor een verificatiecode verzonden naar <0>{email}" - -msgid "Please select a JPEG, PNG, GIF, or WebP image." -msgstr "Selecteer een JPEG-, PNG-, GIF- of WebP-afbeelding." - -msgid "Please select a JPEG, PNG, GIF, WebP, or SVG image." -msgstr "Selecteer een JPEG-, PNG-, GIF-, WebP- of SVG-afbeelding." - -msgid "Powered by" -msgstr "Powered by" - -msgid "Preview avatar" -msgstr "Voorbeeld avatar" - -msgid "Previous" -msgstr "Vorige" - -msgid "Privacy policies" -msgstr "Privacybeleid" - -msgid "Profile picture" -msgstr "Profielfoto" - -msgid "Profile updated successfully" -msgstr "Profiel succesvol bijgewerkt" - -msgid "Region" -msgstr "Regio" - -msgid "Reload" -msgstr "Herladen" - -msgid "Remove logo" -msgstr "Logo verwijderen" - -msgid "Remove profile picture" -msgstr "Profielfoto verwijderen" - -msgid "Request a new code" -msgstr "Vraag een nieuwe code aan" - -msgid "Role" -msgstr "Rol" - -msgid "Save changes" -msgstr "Wijzigingen opslaan" - -msgid "Saving..." -msgstr "Opslaan..." - -msgid "Screenshots of the dashboard project with desktop and mobile versions" -msgstr "Schermafbeeldingen van het dashboardproject met desktop en mobiele versies" - -msgid "Search" -msgstr "Zoeken" - -#. placeholder {0}: user ? `${user.firstName ?? ""} ${user.lastName ?? ""}`.trim() || user.email : "" -msgid "Select a new role for <0>{0}" -msgstr "Selecteer een nieuwe rol voor <0>{0}" - -msgid "Select Account" -msgstr "Selecteer account" - -msgid "Select dates" -msgstr "Selecteer datums" - -msgid "Send invite" -msgstr "Uitnodiging verzenden" - -msgid "Sending verification code..." -msgstr "Verificatiecode verzenden..." - -msgid "Settings" -msgstr "Instellingen" - -msgid "Show filters" -msgstr "Filters tonen" - -msgid "Sign up in seconds to start building on PlatformPlatform – just like thousands of others." -msgstr "Meld je aan en begin direct met bouwen op PlatformPlatform – net als duizenden anderen." - -msgid "Signup verification code" -msgstr "Verificatiecode voor aanmelding" - -msgid "Success" -msgstr "Succes" - -msgid "Switching account..." -msgstr "Account wisselen..." - -msgid "System" -msgstr "Systeem" - -msgid "Terms of use" -msgstr "Gebruiksvoorwaarden" - -msgid "Theme" -msgstr "Thema" - -msgid "This is the region where your data is stored" -msgstr "Dit is de regio waar je gegevens zijn opgeslagen" - -msgid "Title" -msgstr "Titel" - -msgid "To delete your account, please contact our support team." -msgstr "Neem contact op met ons ondersteuningsteam om je account te verwijderen." - -msgid "Toggle sidebar" -msgstr "Zijbalk wisselen" - -msgid "Total users" -msgstr "Totale gebruikers" - -msgid "Try again" -msgstr "Probeer opnieuw" - -msgid "Unknown state" -msgstr "Onbekende status" - -msgid "Unnamed account" -msgstr "Naamloos account" - -msgid "Update your profile picture and personal details here." -msgstr "Werk hier je profielfoto en persoonlijke gegevens bij." - -msgid "Upload logo" -msgstr "Logo uploaden" - -msgid "Upload profile picture" -msgstr "Profielfoto uploaden" - -msgid "User actions" -msgstr "Gebruikersacties" - -msgid "User data updated" -msgstr "Gebruikersgegevens bijgewerkt" - -msgid "User deleted successfully: {userDisplayName}" -msgstr "Gebruiker succesvol verwijderd: {userDisplayName}" - -msgid "User invited successfully" -msgstr "Gebruiker succesvol uitgenodigd" - -msgid "User not in current view" -msgstr "Gebruiker niet in huidige weergave" - -msgid "User profile" -msgstr "Gebruikersprofiel" - -msgid "User profile menu" -msgstr "Gebruikersprofielmenu" - -msgid "User role" -msgstr "Gebruikersrol" - -msgid "User role updated successfully for {userDisplayName}" -msgstr "Gebruikersrol succesvol bijgewerkt voor {userDisplayName}" - -msgid "User status" -msgstr "Gebruikersstatus" - -msgid "user@email.com" -msgstr "gebruiker@email.com" - -msgid "Users" -msgstr "Gebruikers" - -msgid "Users who haven't confirmed their email" -msgstr "Gebruikers die hun e-mail niet hebben bevestigd" - -msgid "Verification code sent" -msgstr "Verificatiecode verzonden" - -msgid "Verified" -msgstr "Geverifieerd" - -msgid "Verify" -msgstr "Verifiëren" - -msgid "Verifying..." -msgstr "Verifiëren..." - -msgid "View active users" -msgstr "Actieve gebruikers bekijken" - -msgid "View invited users" -msgstr "Uitgenodigde gebruikers bekijken" - -msgid "View profile" -msgstr "Profiel bekijken" - -msgid "View users" -msgstr "Gebruikers bekijken" - -msgid "Welcome home" -msgstr "Welkom home" - -#. placeholder {0}: userInfo.firstName -msgid "Welcome home, {0}" -msgstr "Welkom home, {0}" - -msgid "When you accept this invitation, your profile information (name, title, and avatar) from your current account will be copied to the new account." -msgstr "Wanneer je deze uitnodiging accepteert, worden je profielgegevens (naam, titel en avatar) van je huidige account gekopieerd naar het nieuwe account." - -#. placeholder {0}: tenant.tenantName -msgid "You have been invited to join <0>{0}." -msgstr "Je bent uitgenodigd om lid te worden van <0>{0}." - -msgid "You were logged out from another browser tab." -msgstr "Je bent uitgelogd vanuit een ander browsertabblad." - -msgid "Your account was switched in another browser tab." -msgstr "Je account is gewisseld in een ander browsertabblad." - -msgid "Your account was switched to <0>{newTenantName} in another browser tab." -msgstr "Je account is gewisseld naar <0>{newTenantName} in een ander browsertabblad." - -msgid "Your team needs to know who's inviting them. Add an account name to get started." -msgstr "Je team moet weten wie hen uitnodigt. Voeg een accountnaam toe om te beginnen." - -msgid "Your verification code has expired" -msgstr "Je verificatiecode is verlopen" - -msgid "Your verification code is valid for {expiresInString}" -msgstr "Je verificatiecode is geldig voor {expiresInString}" - -msgid "yourname@example.com" -msgstr "jouwnaam@voorbeeld.com" diff --git a/application/back-office/WebApp/shared/translations/locale/nl-NL.po b/application/back-office/WebApp/shared/translations/locale/nl-NL.po deleted file mode 100644 index c43b1e885e..0000000000 --- a/application/back-office/WebApp/shared/translations/locale/nl-NL.po +++ /dev/null @@ -1,23 +0,0 @@ -msgid "" -msgstr "" -"Project-Id-Version: \n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-10-07 23:40+0200\n" -"PO-Revision-Date: \n" -"Last-Translator: \n" -"Language-Team: \n" -"Language: nl-NL\n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: \n" -"X-Generator: @lingui/cli\n" - -msgid "Home" -msgstr "Home" - -msgid "Manage tenants, view system data, see exceptions, and perform various tasks for operational and support teams." -msgstr "Beheer huurders, bekijk systeemgegevens, zie uitzonderingen en voer diverse taken uit voor operationele en ondersteuningsteams." - -msgid "Welcome to the Back Office" -msgstr "Welkom bij de Backoffice" diff --git a/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs b/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs index 84a3067012..c8619302b7 100644 --- a/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs +++ b/application/shared-kernel/SharedKernel/SinglePageApp/SinglePageAppConfiguration.cs @@ -13,7 +13,7 @@ public class SinglePageAppConfiguration public const string CdnUrlKey = "CDN_URL"; private const string PublicKeyPrefix = "PUBLIC_"; private const string ApplicationVersionKey = "APPLICATION_VERSION"; - public static readonly string[] SupportedLocalizations = ["en-US", "da-DK", "nl-NL"]; + public static readonly string[] SupportedLocalizations = ["en-US", "da-DK"]; public static readonly string BuildRootPath = GetWebAppDistRoot("WebApp", "dist"); diff --git a/application/shared-webapp/build/module-federation-types/account-management.d.ts b/application/shared-webapp/build/module-federation-types/account-management.d.ts index 9bb40d7dff..0854957588 100644 --- a/application/shared-webapp/build/module-federation-types/account-management.d.ts +++ b/application/shared-webapp/build/module-federation-types/account-management.d.ts @@ -17,7 +17,3 @@ declare module "account-management/translations/da-DK" { import type { Messages } from "@lingui/core"; export const messages: Messages; } -declare module "account-management/translations/nl-NL" { - import type { Messages } from "@lingui/core"; - export const messages: Messages; -} diff --git a/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx b/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx new file mode 100644 index 0000000000..51dd65fd85 --- /dev/null +++ b/application/shared-webapp/infrastructure/translations/LocaleSwitcher.tsx @@ -0,0 +1,51 @@ +import { useLingui } from "@lingui/react"; +import type { Key } from "@react-types/shared"; +import { Button } from "@repo/ui/components/Button"; +import { Menu, MenuItem, MenuTrigger } from "@repo/ui/components/Menu"; +import { CheckIcon, GlobeIcon } from "lucide-react"; +import { use, useMemo } from "react"; +import { preferredLocaleKey } from "./constants"; +import { type Locale, translationContext } from "./TranslationContext"; + +export function LocaleSwitcher({ "aria-label": ariaLabel }: Readonly<{ "aria-label": string }>) { + const { setLocale, getLocaleInfo, locales } = use(translationContext); + const { i18n } = useLingui(); + + const items = useMemo( + () => + locales.map((locale) => ({ + id: locale, + label: getLocaleInfo(locale).label + })), + [locales, getLocaleInfo] + ); + + const handleLocaleChange = (key: Key) => { + const locale = key.toString() as Locale; + if (locale !== currentLocale) { + setLocale(locale).then(() => { + localStorage.setItem(preferredLocaleKey, locale); + }); + } + }; + + const currentLocale = i18n.locale as Locale; + + return ( + + + + {items.map((item) => ( + +
+ {item.label} + {item.id === currentLocale && } +
+
+ ))} +
+
+ ); +} diff --git a/application/shared-webapp/infrastructure/translations/i18n.config.json b/application/shared-webapp/infrastructure/translations/i18n.config.json index 1465dc4754..81be396968 100644 --- a/application/shared-webapp/infrastructure/translations/i18n.config.json +++ b/application/shared-webapp/infrastructure/translations/i18n.config.json @@ -10,11 +10,5 @@ "label": "Dansk", "territory": "DK", "rtl": false - }, - "nl-NL": { - "locale": "nl-NL", - "label": "Nederlands", - "territory": "NL", - "rtl": false } } diff --git a/application/shared-webapp/ui/components/AlertDialog.tsx b/application/shared-webapp/ui/components/AlertDialog.tsx index 3058e7132f..7ce615227c 100644 --- a/application/shared-webapp/ui/components/AlertDialog.tsx +++ b/application/shared-webapp/ui/components/AlertDialog.tsx @@ -2,6 +2,7 @@ import { AlertCircleIcon, InfoIcon } from "lucide-react"; import { type ReactNode, useId } from "react"; import { chain } from "react-aria"; import type { DialogProps } from "react-aria-components"; +import { twMerge } from "tailwind-merge"; import { tv } from "tailwind-variants"; import { Button } from "./Button"; /** @@ -45,7 +46,12 @@ export function AlertDialog({ }: Readonly) { const contentId = useId(); return ( - + {({ close }) => ( <> {title} diff --git a/application/shared-webapp/ui/components/ComboBox.tsx b/application/shared-webapp/ui/components/ComboBox.tsx index e5094ec198..4478f2fc8c 100644 --- a/application/shared-webapp/ui/components/ComboBox.tsx +++ b/application/shared-webapp/ui/components/ComboBox.tsx @@ -25,6 +25,7 @@ export interface ComboBoxProps extends Omit string); + tooltip?: string; isOpen?: boolean; placeholder?: string; children: React.ReactNode | ((item: T) => React.ReactNode); @@ -34,6 +35,7 @@ export function ComboBox({ label, description, errorMessage, + tooltip, isOpen, children, items, @@ -41,7 +43,7 @@ export function ComboBox({ }: Readonly>) { return ( - {label && } + {label && } + {tooltip} + + + + ); } diff --git a/application/shared-webapp/ui/components/Link.tsx b/application/shared-webapp/ui/components/Link.tsx index 02af4af46b..bebe9d9ad3 100644 --- a/application/shared-webapp/ui/components/Link.tsx +++ b/application/shared-webapp/ui/components/Link.tsx @@ -7,7 +7,7 @@ import { tv } from "tailwind-variants"; import { focusRing } from "./focusRing"; interface LinkProps extends AriaLinkProps { - variant?: "primary" | "secondary" | "destructive" | "ghost"; + variant?: "primary" | "secondary" | "destructive" | "ghost" | "button"; underline?: boolean | "hover"; size?: "md" | "sm" | "lg"; } @@ -20,7 +20,8 @@ const styles = tv({ primary: "text-primary hover:text-primary/90", secondary: "text-secondary-foreground hover:text-secondary-foreground/90", destructive: "text-destructive hover:text-destructive/90", - ghost: "text-accent-foreground hover:bg-hover-background hover:text-accent-foreground/90" + ghost: "text-accent-foreground hover:bg-hover-background hover:text-accent-foreground/90", + button: "hover:opacity-90" }, underline: { true: "underline disabled:no-underline", diff --git a/application/shared-webapp/ui/components/Modal.tsx b/application/shared-webapp/ui/components/Modal.tsx index 7495fd7455..dc2c343aeb 100644 --- a/application/shared-webapp/ui/components/Modal.tsx +++ b/application/shared-webapp/ui/components/Modal.tsx @@ -43,7 +43,7 @@ const overlayStyles = tv({ }); const modalStyles = tv({ - base: "flex w-full flex-col overflow-hidden bg-popover bg-clip-padding text-left align-middle text-foreground shadow-2xl dark:backdrop-blur-2xl dark:backdrop-saturate-200 forced-colors:bg-[Canvas]", + base: "flex flex-col overflow-hidden bg-popover bg-clip-padding text-left align-middle text-foreground shadow-2xl dark:backdrop-blur-2xl dark:backdrop-saturate-200 forced-colors:bg-[Canvas]", variants: { isEntering: { true: "zoom-in-105 animate-in duration-200 ease-out" @@ -52,8 +52,8 @@ const modalStyles = tv({ true: "zoom-out-95 animate-out duration-200 ease-in" }, isFullScreenMobile: { - true: "max-sm:h-full max-sm:max-h-full max-sm:rounded-none max-sm:border-0 sm:max-h-[calc(100vh-2rem)] sm:w-fit sm:rounded-lg sm:border sm:border-border", - false: "max-h-[calc(100vh-2rem)] rounded-lg border border-border sm:w-fit" + true: "max-sm:h-full max-sm:max-h-full max-sm:w-full max-sm:rounded-none max-sm:border-0 sm:max-h-[calc(100vh-2rem)] sm:rounded-lg sm:border sm:border-border", + false: "max-h-[calc(100vh-2rem)] rounded-lg border border-border" } }, defaultVariants: { @@ -74,7 +74,7 @@ export function Modal({ position, fullSize, blur, zIndex, ...props }: Readonly overlayStyles({ position, fullSize, blur, zIndex, ...renderProps })} > - + {props.children} ); } diff --git a/application/shared-webapp/ui/components/MultiSelect.tsx b/application/shared-webapp/ui/components/MultiSelect.tsx new file mode 100644 index 0000000000..96c699ed71 --- /dev/null +++ b/application/shared-webapp/ui/components/MultiSelect.tsx @@ -0,0 +1,208 @@ +/** + * MultiSelect component for multiple selection dropdowns + * Based on the Select component but optimized for multiple selection + */ +import { ChevronDown, XIcon } from "lucide-react"; +import type React from "react"; +import { useContext, useEffect, useRef, useState } from "react"; +import { + Button, + DialogTrigger, + FormValidationContext, + type Key, + ListBox, + type ListBoxItemProps, + type ListBoxProps, + type Selection, + type ValidationResult +} from "react-aria-components"; +import { tv } from "tailwind-variants"; +import { Description } from "./Description"; +import { DropdownItem, DropdownSection, type DropdownSectionProps } from "./Dropdown"; +import { FieldError } from "./FieldError"; +import { focusRing } from "./focusRing"; +import { Label } from "./Label"; +import { Popover } from "./Popover"; + +const buttonStyles = tv({ + extend: focusRing, + base: "flex h-10 w-full min-w-[150px] cursor-default items-center gap-4 rounded-md border border-input bg-input-background py-2 pr-2 pl-3 text-start text-foreground transition", + variants: { + isInvalid: { + true: "border-destructive group-invalid:border-destructive forced-colors:group-invalid:border-[Mark]" + }, + isDisabled: { + false: "pressed:bg-active-background pressed:text-accent-foreground hover:bg-hover-background", + true: "opacity-50 forced-colors:border-[GrayText] forced-colors:text-[GrayText]" + } + } +}); + +export type MultiSelectItemShape = { + id: Key; + label: string; +}; + +export interface MultiSelectProps extends Omit, "children"> { + label?: string; + description?: string; + errorMessage?: string | ((validation: ValidationResult) => string); + tooltip?: string; + items: Iterable; + selectedKeys: Selection; + onSelectionChange: (keys: Selection) => void; + children: React.ReactNode | ((item: T) => React.ReactNode); + className?: string; + placeholder?: string; + isReadOnly?: boolean; + name?: string; // for validation context +} + +export function MultiSelect({ + items, + selectedKeys, + onSelectionChange, + children, + label, + description, + errorMessage, + tooltip, + className, + placeholder = "Select options...", + isReadOnly = false, + name, + ...listBoxProps +}: Readonly>) { + const errors = useContext(FormValidationContext); + const isInvalid = Boolean(name != null && name in errors ? errors?.[name] : undefined); + + const buttonRef = useRef(null); + const [popoverWidth, setPopoverWidth] = useState(); + + useEffect(() => { + if (!buttonRef.current) { + return; + } + const observer = new globalThis.ResizeObserver(() => { + if (buttonRef.current) { + setPopoverWidth(buttonRef.current.offsetWidth); + } + }); + observer.observe(buttonRef.current); + setPopoverWidth(buttonRef.current.offsetWidth); + return () => observer.disconnect(); + }, []); + + const itemsArr = items ? Array.from(items) : []; + const hasItems = itemsArr.length > 0; + + return ( +
+ + {label && } + + {description && {description}} + {errorMessage} + {hasItems && ( + + + {children} + + + )} + +
+ ); +} + +export function MultiSelectItem(props: Readonly) { + // Only wrap string children in the span for truncation + return ( + + {typeof props.children === "string" ? ( + {props.children} + ) : ( + props.children + )} + + ); +} + +export function MultiSelectSection(props: Readonly>) { + return ; +} + +interface MultiSelectValueDisplayProps { + items: Iterable; + selectedKeys: Selection; + onRemove: (key: Key) => void; + placeholder?: string; +} + +export function MultiSelectValueDisplay({ + items, + selectedKeys, + onRemove, + placeholder +}: Readonly>) { + const itemsArr = Array.isArray(items) ? items : Array.from(items); + const selectedItems = itemsArr.filter((item) => (selectedKeys instanceof Set ? selectedKeys.has(item.id) : false)); + + if (selectedItems.length === 0) { + return {placeholder}; + } + + return ( +
+ + {selectedItems[0].label} + { + e.stopPropagation(); + onRemove(selectedItems[0].id); + }} + aria-label="Remove" + /> + + {selectedItems.length > 1 && ( + +{selectedItems.length - 1} + )} +
+ ); +} diff --git a/application/shared-webapp/ui/components/NumberField.tsx b/application/shared-webapp/ui/components/NumberField.tsx index 5f51a06df5..80d4f11606 100644 --- a/application/shared-webapp/ui/components/NumberField.tsx +++ b/application/shared-webapp/ui/components/NumberField.tsx @@ -22,12 +22,13 @@ export interface NumberFieldProps extends AriaNumberFieldProps { label?: string; description?: string; errorMessage?: string | ((validation: ValidationResult) => string); + tooltip?: string; } -export function NumberField({ label, description, errorMessage, ...props }: Readonly) { +export function NumberField({ label, description, errorMessage, tooltip, ...props }: Readonly) { return ( - {label && } + {label && } {(renderProps) => ( <> diff --git a/application/shared-webapp/ui/components/OneTimeCodeInput.tsx b/application/shared-webapp/ui/components/OneTimeCodeInput.tsx index 4d0bb3cbf4..43b7c39aeb 100644 --- a/application/shared-webapp/ui/components/OneTimeCodeInput.tsx +++ b/application/shared-webapp/ui/components/OneTimeCodeInput.tsx @@ -24,6 +24,7 @@ export const OneTimeCodeInput = forwardRef(new Array(length).fill("")); + const [isLocked, setIsLocked] = useState(false); const id = useId(); const digitRefs = useMemo(() => new Array(length).fill(id).map((id, i) => `${id}_${i}`), [id, length]); const inputValue = digits.join(""); @@ -52,6 +53,7 @@ export const OneTimeCodeInput = forwardRef ({ reset: () => { setDigits(new Array(length).fill("")); + setIsLocked(false); setFocus(0); }, focus: () => { @@ -63,15 +65,23 @@ export const OneTimeCodeInput = forwardRef { + const onChangeHandler = (value: string, i: number, _isPaste: boolean = false): void => { let newDigits: string[]; if (value.length > 1) { // If the user pastes more than one digit const pastedDigits = value.substring(0, length).split(""); - newDigits = [...pastedDigits]; + // Pad with empty strings to maintain the full length + newDigits = new Array(length).fill(""); + pastedDigits.forEach((digit, index) => { + newDigits[index] = digit; + }); setDigits(newDigits); setFocus(pastedDigits.length); + // Only lock if the pasted code is complete + if (pastedDigits.length === length) { + setIsLocked(true); + } } else { newDigits = [...digits]; newDigits[i] = value; @@ -96,7 +106,7 @@ export const OneTimeCodeInput = forwardRef onChangeHandler(value, i)} autoFocus={i === inputValue.length && autoFocus} - disabled={disabled} + disabled={disabled || isLocked} /> ))} diff --git a/application/shared-webapp/ui/components/SearchField.tsx b/application/shared-webapp/ui/components/SearchField.tsx index 7224c1d0a4..f5742e2405 100644 --- a/application/shared-webapp/ui/components/SearchField.tsx +++ b/application/shared-webapp/ui/components/SearchField.tsx @@ -20,15 +20,23 @@ export interface SearchFieldProps extends AriaSearchFieldProps { description?: string; placeholder?: string; errorMessage?: string | ((validation: ValidationResult) => string); + tooltip?: string; } -export function SearchField({ label, description, errorMessage, placeholder, ...props }: Readonly) { +export function SearchField({ + label, + description, + errorMessage, + tooltip, + placeholder, + ...props +}: Readonly) { return ( - {label && } + {label && } extends Omit, label?: string; description?: string; errorMessage?: string | ((validation: ValidationResult) => string); + tooltip?: string; items?: Iterable; children: React.ReactNode | ((item: T) => React.ReactNode); } @@ -56,6 +57,7 @@ export function Select({ label, description, errorMessage, + tooltip, children, items, className, @@ -66,7 +68,7 @@ export function Select({ return ( - {label && } + {label && } + {tooltipText} + + ); +} + +function getTooltipText(themeMode: ThemeMode, resolvedThemeMode: SystemThemeMode): string { + if (resolvedThemeMode === SystemThemeMode.Dark) { + return themeMode === ThemeMode.System ? "System mode (dark)" : "Dark mode"; + } + return themeMode === ThemeMode.System ? "System mode (light)" : "Light mode"; +} + +function ThemeModeIcon({ themeMode, resolvedThemeMode }: { themeMode: ThemeMode; resolvedThemeMode: SystemThemeMode }) { + if (resolvedThemeMode === SystemThemeMode.Dark) { + return themeMode === ThemeMode.System ? : ; + } + return themeMode === ThemeMode.System ? : ; +} diff --git a/developer-cli/Commands/TranslateCommand.cs b/developer-cli/Commands/TranslateCommand.cs index fde591575f..20373fee56 100644 --- a/developer-cli/Commands/TranslateCommand.cs +++ b/developer-cli/Commands/TranslateCommand.cs @@ -19,7 +19,7 @@ public TranslateCommand() : base( ) { AddOption(new Option(["--self-contained-system", "-s"], "Translate only files in a specific self-contained system")); - AddOption(new Option(["--language", "-l"], "Translate only files for a specific language (e.g. da-DK, nl-NL)")); + AddOption(new Option(["--language", "-l"], "Translate only files for a specific language (e.g. da-DK)")); Handler = CommandHandler.Create(Execute); }