From 69942ec2431a6f3cb0486e676dae20d7a0644c57 Mon Sep 17 00:00:00 2001 From: giancarlo Date: Wed, 10 Apr 2024 21:23:41 +0800 Subject: [PATCH] Refactor language switcher and enhance site routing Renamed 'LanguageDropdownSwitcher' to 'LanguageSelector' for better representation of the component's functionality. Removed unnecessary dependencies and optimized function declarations. Updated site routing to include new pages like 'contact', 'terms-of-service', and 'privacy-policy'. Also made adjustments for multi-language support, providing better user experience. --- .../(legal)/cookie-policy/page.tsx | 15 ++++++++-- .../(legal)/privacy-policy/page.tsx | 15 ++++++++-- .../(legal)/terms-of-service/page.tsx | 15 ++++++++-- .../(marketing)/_components/site-footer.tsx | 2 +- apps/web/app/(marketing)/contact/page.tsx | 25 +++++++++++++++++ apps/web/app/server-sitemap.xml/route.ts | 9 +++++- apps/web/app/update-password/page.tsx | 9 ++++++ apps/web/public/locales/en/account.json | 12 +++----- apps/web/public/locales/en/auth.json | 1 + apps/web/public/locales/en/marketing.json | 2 +- .../account-settings-container.tsx | 28 +++++++++++++++++++ packages/ui/package.json | 3 +- ...own-switcher.tsx => language-selector.tsx} | 15 ++++------ 13 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 apps/web/app/(marketing)/contact/page.tsx rename packages/ui/src/makerkit/{language-dropdown-switcher.tsx => language-selector.tsx} (87%) diff --git a/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx b/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx index fe40b667b..f7257871f 100644 --- a/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx +++ b/apps/web/app/(marketing)/(legal)/cookie-policy/page.tsx @@ -1,12 +1,23 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; -function CookiePolicyPage() { +export async function generateMetadata() { + const { t } = await createI18nServerInstance(); + + return { + title: t('marketing.cookiePolicy'), + }; +} + +async function CookiePolicyPage() { + const { t } = await createI18nServerInstance(); + return (
diff --git a/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx b/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx index d8f3d94f5..3fbff2c0e 100644 --- a/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx +++ b/apps/web/app/(marketing)/(legal)/privacy-policy/page.tsx @@ -1,11 +1,22 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; -function PrivacyPolicyPage() { +export async function generateMetadata() { + const { t } = await createI18nServerInstance(); + + return { + title: t('marketing.privacyPolicy'), + }; +} + +async function PrivacyPolicyPage() { + const { t } = await createI18nServerInstance(); + return (
- +
); diff --git a/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx b/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx index 4993eaede..d81cfdbe9 100644 --- a/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx +++ b/apps/web/app/(marketing)/(legal)/terms-of-service/page.tsx @@ -1,11 +1,22 @@ import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; -function TermsOfServicePage() { +export async function generateMetadata() { + const { t } = await createI18nServerInstance(); + + return { + title: t('marketing.termsOfService'), + }; +} + +async function TermsOfServicePage() { + const { t } = await createI18nServerInstance(); + return (
- +
); diff --git a/apps/web/app/(marketing)/_components/site-footer.tsx b/apps/web/app/(marketing)/_components/site-footer.tsx index a6df3ab6d..279cedaec 100644 --- a/apps/web/app/(marketing)/_components/site-footer.tsx +++ b/apps/web/app/(marketing)/_components/site-footer.tsx @@ -90,7 +90,7 @@ export function SiteFooter() { - + diff --git a/apps/web/app/(marketing)/contact/page.tsx b/apps/web/app/(marketing)/contact/page.tsx new file mode 100644 index 000000000..bfb60c67c --- /dev/null +++ b/apps/web/app/(marketing)/contact/page.tsx @@ -0,0 +1,25 @@ +import { SitePageHeader } from '~/(marketing)/_components/site-page-header'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; +import { withI18n } from '~/lib/i18n/with-i18n'; + +export async function generateMetadata() { + const { t } = await createI18nServerInstance(); + + return { + title: t('marketing.contact'), + }; +} + +async function ContactPage() { + const { t } = await createI18nServerInstance(); + + return ( +
+
+ +
+
+ ); +} + +export default withI18n(ContactPage); diff --git a/apps/web/app/server-sitemap.xml/route.ts b/apps/web/app/server-sitemap.xml/route.ts index ed6756b41..b4e3ecb3b 100644 --- a/apps/web/app/server-sitemap.xml/route.ts +++ b/apps/web/app/server-sitemap.xml/route.ts @@ -24,7 +24,14 @@ export async function GET() { } function getSiteUrls() { - const urls = ['/', 'faq', 'pricing']; + const urls = [ + '/', + '/faq', + '/pricing', + '/contact', + '/terms-of-service', + '/privacy-policy', + ]; return urls.map((url) => { return { diff --git a/apps/web/app/update-password/page.tsx b/apps/web/app/update-password/page.tsx index 59f99188d..af47c17a4 100644 --- a/apps/web/app/update-password/page.tsx +++ b/apps/web/app/update-password/page.tsx @@ -7,8 +7,17 @@ import { getSupabaseServerComponentClient } from '@kit/supabase/server-component import { AppLogo } from '~/components/app-logo'; import pathsConfig from '~/config/paths.config'; +import { createI18nServerInstance } from '~/lib/i18n/i18n.server'; import { withI18n } from '~/lib/i18n/with-i18n'; +export const generateMetadata = async () => { + const { t } = await createI18nServerInstance(); + + return { + title: t('auth.updatePassword'), + }; +}; + async function PasswordResetPage() { const client = getSupabaseServerComponentClient(); const auth = await requireUser(client); diff --git a/apps/web/public/locales/en/account.json b/apps/web/public/locales/en/account.json index 7380ca916..cd64b82a6 100644 --- a/apps/web/public/locales/en/account.json +++ b/apps/web/public/locales/en/account.json @@ -5,18 +5,12 @@ "emailTabTabSubheading": "Update your email address", "passwordTab": "Password", "passwordTabSubheading": "Update your password", - "manageConnectedAccounts": "Connected Accounts", - "manageConnectedAccountsSubheading": "Manage your connected accounts", - "connectedAccounts": "Connected Accounts", "homePage": "Home", "billingTab": "Billing", "settingsTab": "Settings", "authenticationTab": "Authentication", "multiFactorAuth": "Multi-Factor Authentication", - "multiFactorAuthDescription": "Set up Multi-Factor Authentication method to further secure your account", - "connectedAccountsSubheading": "Below are the accounts linked to your profile", - "availableProviders": "Available Providers", - "availableProvidersSubheading": "Click on the providers below to link your profile to the provider", + "multiFactorAuthDescription": "Set up Multi-Factor Authentication method to further secure your account", "updateProfileSuccess": "Profile successfully updated", "updateProfileError": "Encountered an error. Please try again", "updatePasswordSuccess": "Password update request successful", @@ -141,5 +135,7 @@ "deleteProfileConfirmationInputLabel": "Type DELETE to confirm", "deleteAccountErrorHeading": "Sorry, we couldn't delete your account", "needsReauthentication": "Reauthentication Required", - "needsReauthenticationDescription": "You need to reauthenticate to change your password. Please sign out and sign in again to change your password." + "needsReauthenticationDescription": "You need to reauthenticate to change your password. Please sign out and sign in again to change your password.", + "language": "Language", + "languageDescription": "Choose your preferred language" } diff --git a/apps/web/public/locales/en/auth.json b/apps/web/public/locales/en/auth.json index 1c002aae6..6a25d5626 100644 --- a/apps/web/public/locales/en/auth.json +++ b/apps/web/public/locales/en/auth.json @@ -4,6 +4,7 @@ "signInHeading": "Sign in to your account", "signIn": "Sign In", "getStarted": "Get started", + "updatePassword": "Update Password", "signOut": "Sign out", "signingIn": "Signing in...", "signingUp": "Signing up...", diff --git a/apps/web/public/locales/en/marketing.json b/apps/web/public/locales/en/marketing.json index 03a472806..31edb3330 100644 --- a/apps/web/public/locales/en/marketing.json +++ b/apps/web/public/locales/en/marketing.json @@ -14,7 +14,7 @@ "about": "About", "product": "Product", "legal": "Legal", - "tos": "Terms of Service", + "termsOfService": "Terms of Service", "cookiePolicy": "Cookie Policy", "privacyPolicy": "Privacy Policy" } diff --git a/packages/features/accounts/src/components/personal-account-settings/account-settings-container.tsx b/packages/features/accounts/src/components/personal-account-settings/account-settings-container.tsx index c1d3f8c92..63658a7f0 100644 --- a/packages/features/accounts/src/components/personal-account-settings/account-settings-container.tsx +++ b/packages/features/accounts/src/components/personal-account-settings/account-settings-container.tsx @@ -1,5 +1,7 @@ 'use client'; +import { useTranslation } from 'react-i18next'; + import { Card, CardContent, @@ -8,6 +10,7 @@ import { CardTitle, } from '@kit/ui/card'; import { If } from '@kit/ui/if'; +import { LanguageSelector } from '@kit/ui/language-selector'; import { Trans } from '@kit/ui/trans'; import { AccountDangerZone } from './account-danger-zone'; @@ -28,6 +31,8 @@ export function PersonalAccountSettingsContainer( }; }>, ) { + const supportsLanguageSelection = useSupportMultiLanguage(); + return (
@@ -62,6 +67,24 @@ export function PersonalAccountSettingsContainer( + + + + + + + + + + + + + + + + + + @@ -130,3 +153,8 @@ export function PersonalAccountSettingsContainer(
); } + +function useSupportMultiLanguage() { + const { i18n } = useTranslation(); + return i18n.options.supportedLngs && i18n.options.supportedLngs.length > 1; +} diff --git a/packages/ui/package.json b/packages/ui/package.json index 197dbf47e..0f4864603 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -124,7 +124,8 @@ "./loading-overlay": "./src/makerkit/loading-overlay.tsx", "./profile-avatar": "./src/makerkit/profile-avatar.tsx", "./mode-toggle": "./src/makerkit/mode-toggle.tsx", - "./enhanced-data-table": "./src/makerkit/data-table.tsx" + "./enhanced-data-table": "./src/makerkit/data-table.tsx", + "./language-selector": "./src/makerkit/language-selector.tsx" }, "typesVersions": { "*": { diff --git a/packages/ui/src/makerkit/language-dropdown-switcher.tsx b/packages/ui/src/makerkit/language-selector.tsx similarity index 87% rename from packages/ui/src/makerkit/language-dropdown-switcher.tsx rename to packages/ui/src/makerkit/language-selector.tsx index 275a88457..848833cc1 100644 --- a/packages/ui/src/makerkit/language-dropdown-switcher.tsx +++ b/packages/ui/src/makerkit/language-selector.tsx @@ -2,8 +2,6 @@ import { useCallback, useMemo, useState } from 'react'; -import { useRouter } from 'next/navigation'; - import { useTranslation } from 'react-i18next'; import { @@ -14,11 +12,12 @@ import { SelectValue, } from '../shadcn/select'; -export const LanguageDropdownSwitcher: React.FC<{ +export function LanguageSelector({ + onChange, +}: { onChange?: (locale: string) => unknown; -}> = ({ onChange }) => { +}) { const { i18n } = useTranslation(); - const router = useRouter(); const { language: currentLanguage, options } = i18n; @@ -43,10 +42,8 @@ export const LanguageDropdownSwitcher: React.FC<{ } await i18n.changeLanguage(locale); - - return router.refresh(); }, - [i18n, onChange, router], + [i18n, onChange], ); return ( @@ -73,7 +70,7 @@ export const LanguageDropdownSwitcher: React.FC<{ ); -}; +} function capitalize(lang: string) { return lang.slice(0, 1).toUpperCase() + lang.slice(1);