fix bug#7
Build & Push / Build & Push Docker Image (push) Failing after 2m50s
Test / Type Check (all packages) (push) Failing after 55s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Push / Build & Push Docker Image (push) Failing after 2m50s
Test / Type Check (all packages) (push) Failing after 55s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
This commit is contained in:
@@ -6,6 +6,7 @@ import Image from 'next/image';
|
||||
import { LocalePill } from './LocalePill';
|
||||
import { ThemePill } from './ThemePill';
|
||||
import styles from './AuthHeader.module.css';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
@@ -13,7 +14,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export function AuthHeader({ locale, themePreference }: Props) {
|
||||
const homePath = localizedModePath('home', locale, themePreference);
|
||||
const currentPreference = useThemePreference(themePreference);
|
||||
const homePath = localizedModePath('home', locale, currentPreference);
|
||||
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
@@ -35,7 +37,7 @@ export function AuthHeader({ locale, themePreference }: Props) {
|
||||
|
||||
<div className={styles.controls}>
|
||||
<div className={styles.pill}>
|
||||
<LocalePill locale={locale} />
|
||||
<LocalePill locale={locale} initialPreference={themePreference} />
|
||||
|
||||
<span className={styles.divider} aria-hidden="true" />
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ThemePreference } from '@/lib/theme/config';
|
||||
import Image from 'next/image';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import styles from './SiteHeader.module.css';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
export function BrandLink({
|
||||
locale,
|
||||
@@ -16,10 +17,11 @@ export function BrandLink({
|
||||
label: string;
|
||||
}) {
|
||||
const pathname = usePathname();
|
||||
const currentMode = useThemePreference(mode);
|
||||
return (
|
||||
<a
|
||||
className={styles.brand}
|
||||
href={localizedModePath('home', locale, mode)}
|
||||
href={localizedModePath('home', locale, currentMode)}
|
||||
aria-label={label}
|
||||
aria-current={routeIdFromPathname(pathname) === 'home' ? 'page' : undefined}
|
||||
>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useEffect, useState } from 'react';
|
||||
import styles from './SiteHeader.module.css';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
export type HeaderNavId = 'product' | 'workflow' | 'modules' | 'pricing' | 'faq';
|
||||
|
||||
@@ -30,6 +31,7 @@ export function HeaderNavigation({
|
||||
const pathname = usePathname();
|
||||
const currentRoute = routeIdFromPathname(pathname);
|
||||
const [currentHash, setCurrentHash] = useState('');
|
||||
const currentMode = useThemePreference(mode);
|
||||
|
||||
useEffect(() => {
|
||||
const updateHash = () => setCurrentHash(window.location.hash.replace(/^#/, ''));
|
||||
@@ -38,7 +40,7 @@ export function HeaderNavigation({
|
||||
return () => window.removeEventListener('hashchange', updateHash);
|
||||
}, []);
|
||||
|
||||
const home = localizedModePath('home', locale, mode);
|
||||
const home = localizedModePath('home', locale, currentMode);
|
||||
|
||||
return (
|
||||
<nav aria-label={label} className={mobile ? styles.drawerNavigation : styles.navigation}>
|
||||
|
||||
@@ -2,27 +2,32 @@
|
||||
|
||||
import { persistLocale } from '@/lib/localization/client';
|
||||
import {
|
||||
localizedModePath,
|
||||
localeSwitchUrl,
|
||||
locales,
|
||||
routeIdFromPathname,
|
||||
type Locale,
|
||||
} from '@/lib/localization/config';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import styles from './LocalePill.module.css';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
const langLabels: Record<Locale, string> = { en: 'EN', fr: 'FR', ar: 'AR' };
|
||||
const langFlags: Record<Locale, string> = { en: '🇺🇸', fr: '🇫🇷', ar: '🇲🇦' };
|
||||
|
||||
interface Props {
|
||||
locale: Locale;
|
||||
initialPreference: ThemePreference;
|
||||
}
|
||||
|
||||
export function LocalePill({ locale }: Props) {
|
||||
export function LocalePill({ locale, initialPreference }: Props) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const menuRef = useRef<HTMLDivElement | null>(null);
|
||||
const [open, setOpen] = useState(false);
|
||||
const preference = useThemePreference(initialPreference);
|
||||
|
||||
useEffect(() => {
|
||||
function handleClick(e: MouseEvent) {
|
||||
@@ -37,6 +42,7 @@ export function LocalePill({ locale }: Props) {
|
||||
persistLocale(next);
|
||||
const routeId = routeIdFromPathname(pathname) ?? 'home';
|
||||
const target = localeSwitchUrl(new URL(window.location.href), next, routeId);
|
||||
target.pathname = localizedModePath(routeId, next, preference);
|
||||
router.push(`${target.pathname}${target.search}${target.hash}`);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
|
||||
import { persistLocale } from '@/lib/localization/client';
|
||||
import {
|
||||
localizedModePath,
|
||||
localeSwitchUrl,
|
||||
locales,
|
||||
routeIdFromPathname,
|
||||
type Locale,
|
||||
} from '@/lib/localization/config';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import styles from './Controls.module.css';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
interface LocaleSelectorProps {
|
||||
currentLocale: Locale;
|
||||
initialPreference: ThemePreference;
|
||||
label: string;
|
||||
localeNames: Record<Locale, string>;
|
||||
compact?: boolean;
|
||||
@@ -19,12 +23,14 @@ interface LocaleSelectorProps {
|
||||
|
||||
export function LocaleSelector({
|
||||
currentLocale,
|
||||
initialPreference,
|
||||
label,
|
||||
localeNames,
|
||||
compact = false,
|
||||
}: LocaleSelectorProps) {
|
||||
const pathname = usePathname();
|
||||
const routeId = routeIdFromPathname(pathname) ?? 'home';
|
||||
const preference = useThemePreference(initialPreference);
|
||||
|
||||
return (
|
||||
<label className={styles.field}>
|
||||
@@ -36,9 +42,9 @@ export function LocaleSelector({
|
||||
onChange={(event) => {
|
||||
const targetLocale = event.target.value as Locale;
|
||||
persistLocale(targetLocale);
|
||||
window.location.assign(
|
||||
localeSwitchUrl(new URL(window.location.href), targetLocale, routeId),
|
||||
);
|
||||
const target = localeSwitchUrl(new URL(window.location.href), targetLocale, routeId);
|
||||
target.pathname = localizedModePath(routeId, targetLocale, preference);
|
||||
window.location.assign(target);
|
||||
}}
|
||||
>
|
||||
{locales.map((locale) => (
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import { ActionLink } from '@/components/actions/ActionLink';
|
||||
import type { ShellMessages } from '@/lib/localization/messages';
|
||||
import { localizedModePath, type Locale } from '@/lib/localization/config';
|
||||
import { accountCreateUrl } from '@/lib/account-urls';
|
||||
import { type Locale } from '@/lib/localization/config';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { HeaderNavigation } from './HeaderNavigation';
|
||||
import { LocaleSelector } from './LocaleSelector';
|
||||
import styles from './SiteHeader.module.css';
|
||||
import { ThemedAccountCreateLink } from './ThemedAccountCreateLink';
|
||||
import { ThemeSelector } from './ThemeSelector';
|
||||
|
||||
function signInUrl(locale: Locale, mode: ThemePreference): string {
|
||||
return localizedModePath('sign-in', locale, mode);
|
||||
}
|
||||
import { ThemedRouteActionLink } from './ThemedRouteActionLink';
|
||||
|
||||
interface MobileNavigationProps {
|
||||
locale: Locale;
|
||||
@@ -116,6 +112,7 @@ export function MobileNavigation({
|
||||
>
|
||||
<LocaleSelector
|
||||
currentLocale={locale}
|
||||
initialPreference={themePreference}
|
||||
label={messages.controls.language}
|
||||
localeNames={messages.controls.localeNames}
|
||||
/>
|
||||
@@ -128,12 +125,21 @@ export function MobileNavigation({
|
||||
</div>
|
||||
|
||||
<div className={styles.drawerActions}>
|
||||
<ActionLink href={signInUrl(locale, themePreference)} variant="button-primary">
|
||||
<ThemedRouteActionLink
|
||||
locale={locale}
|
||||
routeId="sign-in"
|
||||
initialPreference={themePreference}
|
||||
variant="button-primary"
|
||||
>
|
||||
{messages.header.login}
|
||||
</ActionLink>
|
||||
<ActionLink href={accountCreateUrl(locale)} variant="button-conversion">
|
||||
</ThemedRouteActionLink>
|
||||
<ThemedAccountCreateLink
|
||||
locale={locale}
|
||||
initialPreference={themePreference}
|
||||
variant="button-conversion"
|
||||
>
|
||||
{messages.header.demo}
|
||||
</ActionLink>
|
||||
</ThemedAccountCreateLink>
|
||||
</div>
|
||||
</div>
|
||||
</dialog>
|
||||
|
||||
@@ -117,7 +117,7 @@ export function SiteFooter({ locale, mode, messages }: SiteFooterProps) {
|
||||
<PendingItem label={footer.links.contact} pending={footer.pending} />
|
||||
</li>
|
||||
<li>
|
||||
<a href={accountCreateUrl(locale)}>{footer.links.demo}</a>
|
||||
<a href={accountCreateUrl(locale, mode)}>{footer.links.demo}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
import { localizedModePath, type Locale } from '@/lib/localization/config';
|
||||
import { type Locale } from '@/lib/localization/config';
|
||||
import type { ShellMessages } from '@/lib/localization/messages';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { ActionLink } from '@/components/actions/ActionLink';
|
||||
import { accountCreateUrl } from '@/lib/account-urls';
|
||||
import { BrandLink } from './BrandLink';
|
||||
import { HeaderNavigation } from './HeaderNavigation';
|
||||
import { LocalePill } from './LocalePill';
|
||||
import { MobileNavigation } from './MobileNavigation';
|
||||
import styles from './SiteHeader.module.css';
|
||||
import { ThemedAccountCreateLink } from './ThemedAccountCreateLink';
|
||||
import { ThemePill } from './ThemePill';
|
||||
|
||||
function signInUrl(locale: Locale, mode: ThemePreference): string {
|
||||
return localizedModePath('sign-in', locale, mode);
|
||||
}
|
||||
import { ThemedRouteActionLink } from './ThemedRouteActionLink';
|
||||
|
||||
interface SiteHeaderProps {
|
||||
locale: Locale;
|
||||
@@ -40,24 +36,34 @@ export function SiteHeader({ locale, messages, themePreference }: SiteHeaderProp
|
||||
role="group"
|
||||
aria-label={messages.header.controlsLabel}
|
||||
>
|
||||
<LocalePill locale={locale} />
|
||||
<LocalePill locale={locale} initialPreference={themePreference} />
|
||||
<ThemePill initialPreference={themePreference} />
|
||||
<ActionLink href={signInUrl(locale, themePreference)} variant="button-primary">
|
||||
<ThemedRouteActionLink
|
||||
locale={locale}
|
||||
routeId="sign-in"
|
||||
initialPreference={themePreference}
|
||||
variant="button-primary"
|
||||
>
|
||||
{messages.header.login}
|
||||
</ActionLink>
|
||||
<ActionLink href={accountCreateUrl(locale)} variant="button-conversion">
|
||||
</ThemedRouteActionLink>
|
||||
<ThemedAccountCreateLink
|
||||
locale={locale}
|
||||
initialPreference={themePreference}
|
||||
variant="button-conversion"
|
||||
>
|
||||
{messages.header.demo}
|
||||
</ActionLink>
|
||||
</ThemedAccountCreateLink>
|
||||
</div>
|
||||
|
||||
<div className={styles.mobileActions}>
|
||||
<ActionLink
|
||||
href={accountCreateUrl(locale)}
|
||||
<ThemedAccountCreateLink
|
||||
locale={locale}
|
||||
initialPreference={themePreference}
|
||||
variant="button-conversion"
|
||||
className={styles.mobileDemoTrigger}
|
||||
>
|
||||
{messages.header.demo}
|
||||
</ActionLink>
|
||||
</ThemedAccountCreateLink>
|
||||
<MobileNavigation
|
||||
locale={locale}
|
||||
messages={messages}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { ActionLink } from '@/components/actions/ActionLink';
|
||||
import { accountCreateUrl } from '@/lib/account-urls';
|
||||
import type { Locale } from '@/lib/localization/config';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
interface ThemedAccountCreateLinkProps
|
||||
extends Omit<ComponentProps<typeof ActionLink>, 'href'> {
|
||||
locale: Locale;
|
||||
initialPreference: ThemePreference;
|
||||
}
|
||||
|
||||
export function ThemedAccountCreateLink({
|
||||
locale,
|
||||
initialPreference,
|
||||
...props
|
||||
}: ThemedAccountCreateLinkProps) {
|
||||
const preference = useThemePreference(initialPreference);
|
||||
return <ActionLink href={accountCreateUrl(locale, preference)} {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
'use client';
|
||||
|
||||
import { ActionLink } from '@/components/actions/ActionLink';
|
||||
import { localizedModePath, type Locale, type RouteId } from '@/lib/localization/config';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useThemePreference } from './useThemePreference';
|
||||
|
||||
interface ThemedRouteActionLinkProps
|
||||
extends Omit<ComponentProps<typeof ActionLink>, 'href'> {
|
||||
locale: Locale;
|
||||
routeId: RouteId;
|
||||
initialPreference: ThemePreference;
|
||||
}
|
||||
|
||||
export function ThemedRouteActionLink({
|
||||
locale,
|
||||
routeId,
|
||||
initialPreference,
|
||||
...props
|
||||
}: ThemedRouteActionLinkProps) {
|
||||
const preference = useThemePreference(initialPreference);
|
||||
return <ActionLink href={localizedModePath(routeId, locale, preference)} {...props} />;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
'use client';
|
||||
|
||||
import { currentThemePreference, themePreferenceEvent } from '@/lib/theme/client';
|
||||
import type { ThemePreference } from '@/lib/theme/config';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export function useThemePreference(initialPreference: ThemePreference): ThemePreference {
|
||||
const [preference, setPreference] = useState<ThemePreference>(initialPreference);
|
||||
|
||||
useEffect(() => {
|
||||
setPreference(currentThemePreference());
|
||||
|
||||
const handlePreference = (event: Event) => {
|
||||
const next = (event as CustomEvent<ThemePreference>).detail;
|
||||
if (next === 'light' || next === 'dark' || next === 'system') {
|
||||
setPreference(next);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener(themePreferenceEvent, handlePreference);
|
||||
return () => window.removeEventListener(themePreferenceEvent, handlePreference);
|
||||
}, []);
|
||||
|
||||
return preference;
|
||||
}
|
||||
Reference in New Issue
Block a user