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.
This commit is contained in:
@@ -1,12 +1,23 @@
|
|||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className={'container mx-auto'}>
|
<div className={'container mx-auto'}>
|
||||||
<SitePageHeader
|
<SitePageHeader
|
||||||
title={`Cookie Policy`}
|
title={t(`marketing.cookiePolicy`)}
|
||||||
subtitle={`This is the cookie policy page. It's a great place to put information about the cookies your site uses.`}
|
subtitle={`This is the cookie policy page. It's a great place to put information about the cookies your site uses.`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
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 (
|
return (
|
||||||
<div className={'mt-8'}>
|
<div className={'mt-8'}>
|
||||||
<div className={'container mx-auto'}>
|
<div className={'container mx-auto'}>
|
||||||
<SitePageHeader title={`Privacy Policy`} subtitle={``} />
|
<SitePageHeader title={t('marketing.privacyPolicy')} subtitle={``} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,11 +1,22 @@
|
|||||||
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
import { SitePageHeader } from '~/(marketing)/_components/site-page-header';
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
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 (
|
return (
|
||||||
<div className={'mt-8'}>
|
<div className={'mt-8'}>
|
||||||
<div className={'container mx-auto'}>
|
<div className={'container mx-auto'}>
|
||||||
<SitePageHeader title={`Terms of Service`} subtitle={``} />
|
<SitePageHeader title={t(`marketing:termsOfService`)} subtitle={``} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function SiteFooter() {
|
|||||||
<FooterSectionList>
|
<FooterSectionList>
|
||||||
<FooterLink>
|
<FooterLink>
|
||||||
<Link href={'/terms-of-service'}>
|
<Link href={'/terms-of-service'}>
|
||||||
<Trans i18nKey={'marketing:tos'} />
|
<Trans i18nKey={'marketing:termsOfService'} />
|
||||||
</Link>
|
</Link>
|
||||||
</FooterLink>
|
</FooterLink>
|
||||||
<FooterLink>
|
<FooterLink>
|
||||||
|
|||||||
25
apps/web/app/(marketing)/contact/page.tsx
Normal file
25
apps/web/app/(marketing)/contact/page.tsx
Normal file
@@ -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 (
|
||||||
|
<div className={'mt-8'}>
|
||||||
|
<div className={'container mx-auto'}>
|
||||||
|
<SitePageHeader title={t(`marketing:contact`)} subtitle={``} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withI18n(ContactPage);
|
||||||
@@ -24,7 +24,14 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getSiteUrls() {
|
function getSiteUrls() {
|
||||||
const urls = ['/', 'faq', 'pricing'];
|
const urls = [
|
||||||
|
'/',
|
||||||
|
'/faq',
|
||||||
|
'/pricing',
|
||||||
|
'/contact',
|
||||||
|
'/terms-of-service',
|
||||||
|
'/privacy-policy',
|
||||||
|
];
|
||||||
|
|
||||||
return urls.map((url) => {
|
return urls.map((url) => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -7,8 +7,17 @@ import { getSupabaseServerComponentClient } from '@kit/supabase/server-component
|
|||||||
|
|
||||||
import { AppLogo } from '~/components/app-logo';
|
import { AppLogo } from '~/components/app-logo';
|
||||||
import pathsConfig from '~/config/paths.config';
|
import pathsConfig from '~/config/paths.config';
|
||||||
|
import { createI18nServerInstance } from '~/lib/i18n/i18n.server';
|
||||||
import { withI18n } from '~/lib/i18n/with-i18n';
|
import { withI18n } from '~/lib/i18n/with-i18n';
|
||||||
|
|
||||||
|
export const generateMetadata = async () => {
|
||||||
|
const { t } = await createI18nServerInstance();
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: t('auth.updatePassword'),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
async function PasswordResetPage() {
|
async function PasswordResetPage() {
|
||||||
const client = getSupabaseServerComponentClient();
|
const client = getSupabaseServerComponentClient();
|
||||||
const auth = await requireUser(client);
|
const auth = await requireUser(client);
|
||||||
|
|||||||
@@ -5,18 +5,12 @@
|
|||||||
"emailTabTabSubheading": "Update your email address",
|
"emailTabTabSubheading": "Update your email address",
|
||||||
"passwordTab": "Password",
|
"passwordTab": "Password",
|
||||||
"passwordTabSubheading": "Update your password",
|
"passwordTabSubheading": "Update your password",
|
||||||
"manageConnectedAccounts": "Connected Accounts",
|
|
||||||
"manageConnectedAccountsSubheading": "Manage your connected accounts",
|
|
||||||
"connectedAccounts": "Connected Accounts",
|
|
||||||
"homePage": "Home",
|
"homePage": "Home",
|
||||||
"billingTab": "Billing",
|
"billingTab": "Billing",
|
||||||
"settingsTab": "Settings",
|
"settingsTab": "Settings",
|
||||||
"authenticationTab": "Authentication",
|
"authenticationTab": "Authentication",
|
||||||
"multiFactorAuth": "Multi-Factor Authentication",
|
"multiFactorAuth": "Multi-Factor Authentication",
|
||||||
"multiFactorAuthDescription": "Set up Multi-Factor Authentication method to further secure your account",
|
"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",
|
|
||||||
"updateProfileSuccess": "Profile successfully updated",
|
"updateProfileSuccess": "Profile successfully updated",
|
||||||
"updateProfileError": "Encountered an error. Please try again",
|
"updateProfileError": "Encountered an error. Please try again",
|
||||||
"updatePasswordSuccess": "Password update request successful",
|
"updatePasswordSuccess": "Password update request successful",
|
||||||
@@ -141,5 +135,7 @@
|
|||||||
"deleteProfileConfirmationInputLabel": "Type DELETE to confirm",
|
"deleteProfileConfirmationInputLabel": "Type DELETE to confirm",
|
||||||
"deleteAccountErrorHeading": "Sorry, we couldn't delete your account",
|
"deleteAccountErrorHeading": "Sorry, we couldn't delete your account",
|
||||||
"needsReauthentication": "Reauthentication Required",
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
"signInHeading": "Sign in to your account",
|
"signInHeading": "Sign in to your account",
|
||||||
"signIn": "Sign In",
|
"signIn": "Sign In",
|
||||||
"getStarted": "Get started",
|
"getStarted": "Get started",
|
||||||
|
"updatePassword": "Update Password",
|
||||||
"signOut": "Sign out",
|
"signOut": "Sign out",
|
||||||
"signingIn": "Signing in...",
|
"signingIn": "Signing in...",
|
||||||
"signingUp": "Signing up...",
|
"signingUp": "Signing up...",
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"about": "About",
|
"about": "About",
|
||||||
"product": "Product",
|
"product": "Product",
|
||||||
"legal": "Legal",
|
"legal": "Legal",
|
||||||
"tos": "Terms of Service",
|
"termsOfService": "Terms of Service",
|
||||||
"cookiePolicy": "Cookie Policy",
|
"cookiePolicy": "Cookie Policy",
|
||||||
"privacyPolicy": "Privacy Policy"
|
"privacyPolicy": "Privacy Policy"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardContent,
|
CardContent,
|
||||||
@@ -8,6 +10,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from '@kit/ui/card';
|
} from '@kit/ui/card';
|
||||||
import { If } from '@kit/ui/if';
|
import { If } from '@kit/ui/if';
|
||||||
|
import { LanguageSelector } from '@kit/ui/language-selector';
|
||||||
import { Trans } from '@kit/ui/trans';
|
import { Trans } from '@kit/ui/trans';
|
||||||
|
|
||||||
import { AccountDangerZone } from './account-danger-zone';
|
import { AccountDangerZone } from './account-danger-zone';
|
||||||
@@ -28,6 +31,8 @@ export function PersonalAccountSettingsContainer(
|
|||||||
};
|
};
|
||||||
}>,
|
}>,
|
||||||
) {
|
) {
|
||||||
|
const supportsLanguageSelection = useSupportMultiLanguage();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={'flex w-full flex-col space-y-6 pb-32'}>
|
<div className={'flex w-full flex-col space-y-6 pb-32'}>
|
||||||
<Card>
|
<Card>
|
||||||
@@ -62,6 +67,24 @@ export function PersonalAccountSettingsContainer(
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
<If condition={supportsLanguageSelection}>
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>
|
||||||
|
<Trans i18nKey={'account:language'} />
|
||||||
|
</CardTitle>
|
||||||
|
|
||||||
|
<CardDescription>
|
||||||
|
<Trans i18nKey={'account:languageDescription'} />
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<LanguageSelector />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</If>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>
|
<CardTitle>
|
||||||
@@ -130,3 +153,8 @@ export function PersonalAccountSettingsContainer(
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useSupportMultiLanguage() {
|
||||||
|
const { i18n } = useTranslation();
|
||||||
|
return i18n.options.supportedLngs && i18n.options.supportedLngs.length > 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -124,7 +124,8 @@
|
|||||||
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
|
"./loading-overlay": "./src/makerkit/loading-overlay.tsx",
|
||||||
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
|
"./profile-avatar": "./src/makerkit/profile-avatar.tsx",
|
||||||
"./mode-toggle": "./src/makerkit/mode-toggle.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": {
|
"typesVersions": {
|
||||||
"*": {
|
"*": {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { useRouter } from 'next/navigation';
|
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -14,11 +12,12 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '../shadcn/select';
|
} from '../shadcn/select';
|
||||||
|
|
||||||
export const LanguageDropdownSwitcher: React.FC<{
|
export function LanguageSelector({
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
onChange?: (locale: string) => unknown;
|
onChange?: (locale: string) => unknown;
|
||||||
}> = ({ onChange }) => {
|
}) {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { language: currentLanguage, options } = i18n;
|
const { language: currentLanguage, options } = i18n;
|
||||||
|
|
||||||
@@ -43,10 +42,8 @@ export const LanguageDropdownSwitcher: React.FC<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
await i18n.changeLanguage(locale);
|
await i18n.changeLanguage(locale);
|
||||||
|
|
||||||
return router.refresh();
|
|
||||||
},
|
},
|
||||||
[i18n, onChange, router],
|
[i18n, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -73,7 +70,7 @@ export const LanguageDropdownSwitcher: React.FC<{
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
function capitalize(lang: string) {
|
function capitalize(lang: string) {
|
||||||
return lang.slice(0, 1).toUpperCase() + lang.slice(1);
|
return lang.slice(0, 1).toUpperCase() + lang.slice(1);
|
||||||
Reference in New Issue
Block a user