diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..490842a --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +fetch-timeout=120000 +fetch-retries=5 +fetch-retry-mintimeout=10000 +fetch-retry-maxtimeout=60000 diff --git a/Dockerfile.dev b/Dockerfile.dev index e42cd66..640f33d 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -5,6 +5,7 @@ WORKDIR /app RUN npm install -g npm@10.5.0 --no-fund --no-audit RUN corepack enable +COPY .npmrc ./ COPY package.json package-lock.json turbo.json tsconfig.base.json ./ COPY apps ./apps COPY packages ./packages diff --git a/apps/admin/src/app/globals.css b/apps/admin/src/app/globals.css index 116e3fe..06ccac6 100644 --- a/apps/admin/src/app/globals.css +++ b/apps/admin/src/app/globals.css @@ -13,7 +13,7 @@ html.dark { body { @apply text-blue-950 antialiased transition-colors dark:text-slate-100; background-image: - linear-gradient(180deg, #ffffff 0%, #f5f8ff 28%, #eef4ff 58%, #ffffff 100%); + linear-gradient(180deg, #ffffff 0%, #fafafa 28%, #f5f5f5 58%, #ffffff 100%); } .shell { @@ -28,12 +28,12 @@ body { html.dark body { background-image: - linear-gradient(180deg, #0a1128 0%, #0d1b38 35%, #07101e 100%); + linear-gradient(180deg, #172554 0%, #1b3068 35%, #0f1a40 100%); } html.dark .panel { - border-color: rgb(30 60 140 / 0.40); - background-color: rgb(11 25 55 / 0.72); + border-color: rgb(30 64 175 / 0.40); + background-color: rgb(23 37 84 / 0.72); } @layer utilities { @@ -43,25 +43,25 @@ html.dark .panel { /* ── Dark mode: zinc classes → deep navy blue ─────────────── */ .bg-zinc-950 { - background-color: rgb(6 13 30); + background-color: #172554; } .bg-zinc-950\/90 { - background-color: rgb(6 13 30 / 0.9); + background-color: rgb(23 37 84 / 0.9); } .bg-zinc-900 { - background-color: rgb(11 25 55); + background-color: rgb(23 37 84 / 0.78); } .bg-zinc-800, .bg-zinc-800\/50 { - background-color: rgb(20 42 90); + background-color: rgb(30 58 138 / 0.5); } .border-zinc-800, .border-zinc-700 { - border-color: rgb(30 60 130); + border-color: rgb(30 64 175); } .text-zinc-100, @@ -70,17 +70,17 @@ html.dark .panel { } .text-zinc-500 { - color: rgb(148 163 184); + color: rgb(168 162 158); } .text-zinc-400, .text-zinc-300 { - color: rgb(100 116 139); + color: rgb(120 113 108); } .hover\:bg-zinc-800:hover, .hover\:bg-zinc-800\/50:hover { - background-color: rgb(30 55 115); + background-color: rgb(30 64 175 / 0.4); } .hover\:text-zinc-200:hover { diff --git a/apps/dashboard/public/rentaldrivego.jpeg b/apps/dashboard/public/rentaldrivego.jpeg index 3ba550b..4547894 100644 Binary files a/apps/dashboard/public/rentaldrivego.jpeg and b/apps/dashboard/public/rentaldrivego.jpeg differ diff --git a/apps/dashboard/public/rentaldrivego.png b/apps/dashboard/public/rentaldrivego.png new file mode 100644 index 0000000..4547894 Binary files /dev/null and b/apps/dashboard/public/rentaldrivego.png differ diff --git a/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx b/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx index 9720865..1fcf1a1 100644 --- a/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx +++ b/apps/dashboard/src/app/forgot-password/ForgotPasswordPageClient.tsx @@ -8,7 +8,7 @@ import PublicShell from "@/components/layout/PublicShell"; import { API_BASE } from "@/lib/api"; import { marketplaceUrl } from "@/lib/urls"; -const DASHBOARD_LOGO_SRC = "/dashboard/rentalcardrive.png"; +const DASHBOARD_LOGO_SRC = "/dashboard/rentaldrivego.png"; export default function ForgotPasswordPageClient({ embedded = false, @@ -104,6 +104,7 @@ export default function ForgotPasswordPageClient({ width={104} height={104} priority + unoptimized className="h-24 w-24 rounded-[1.75rem] border border-stone-200 bg-white p-1.5 shadow-sm transition-colors dark:border-blue-800 dark:bg-blue-950" /> diff --git a/apps/dashboard/src/app/globals.css b/apps/dashboard/src/app/globals.css index c86098d..dd5eebc 100644 --- a/apps/dashboard/src/app/globals.css +++ b/apps/dashboard/src/app/globals.css @@ -14,7 +14,7 @@ html.dark { --primary: #2563eb; --primary-hover: #1d4ed8; --accent: #f97316; - --sidebar-bg: #0a1128; + --sidebar-bg: #172554; --sidebar-text: #94a3b8; --sidebar-active: #ffffff; @@ -26,9 +26,9 @@ html.dark { } html.dark { - --bg-elevated: rgb(11 25 55 / 0.78); - --border-accent: rgb(30 60 140 / 0.40); - --glass-bg: rgb(11 25 55 / 0.65); + --bg-elevated: rgb(23 37 84 / 0.82); + --border-accent: rgb(30 64 175 / 0.40); + --glass-bg: rgb(23 37 84 / 0.72); --shadow-card: 0 30px 80px rgba(0, 0, 0, 0.36); --shadow-glow: 0 0 20px rgba(249, 115, 22, 0.25); } @@ -41,7 +41,7 @@ html.dark { body { @apply text-blue-950 antialiased transition-colors dark:text-slate-100; background-image: - linear-gradient(180deg, #ffffff 0%, #f5f8ff 28%, #eef4ff 58%, #ffffff 100%); + linear-gradient(180deg, #ffffff 0%, #fafafa 28%, #f5f5f5 58%, #ffffff 100%); } h1, h2, h3, h4, h5, h6 { @@ -51,12 +51,12 @@ html.dark { html.dark body { background-image: - linear-gradient(180deg, #0a1128 0%, #0d1b38 35%, #07101e 100%); + linear-gradient(180deg, #172554 0%, #1b3068 35%, #0f1a40 100%); } @layer components { .fleet-dashboard-shell { - --fleet-bg-primary: #f5f8ff; + --fleet-bg-primary: #ffffff; --fleet-bg-elevated: #ffffff; --fleet-glass-bg: rgb(255 255 255 / 0.82); --fleet-card-bg: rgb(255 255 255 / 0.72); @@ -73,32 +73,32 @@ html.dark body { background: radial-gradient(ellipse at 20% 20%, rgb(37 99 235 / 0.10) 0%, transparent 56%), radial-gradient(ellipse at 82% 72%, rgb(249 115 22 / 0.08) 0%, transparent 54%), - linear-gradient(180deg, #ffffff 0%, #f5f8ff 35%, #edf4ff 100%); + linear-gradient(180deg, #ffffff 0%, #fafafa 35%, #f5f5f5 100%); color: var(--fleet-text-primary); } html.dark .fleet-dashboard-shell { - --fleet-bg-primary: #0a0f1a; - --fleet-bg-elevated: #111827; - --fleet-glass-bg: rgb(15 25 45 / 0.66); - --fleet-card-bg: rgb(30 58 95 / 0.28); - --fleet-border-subtle: rgb(59 130 246 / 0.14); + --fleet-bg-primary: #172554; + --fleet-bg-elevated: #1e3a8a; + --fleet-glass-bg: rgb(23 37 84 / 0.78); + --fleet-card-bg: rgb(23 37 84 / 0.72); + --fleet-border-subtle: rgb(30 64 175 / 0.22); --fleet-text-primary: #e8edf5; --fleet-text-secondary: #8899b4; - --fleet-accent-blue: #3b82f6; - --fleet-accent-blue-light: #60a5fa; + --fleet-accent-blue: #2e50f1; + --fleet-accent-blue-light: #1e3de0; --fleet-accent-orange-light: #fb923c; --fleet-success: #10b981; --fleet-danger: #ef4444; background: - radial-gradient(ellipse at 20% 20%, rgb(59 130 246 / 0.09) 0%, transparent 58%), + radial-gradient(ellipse at 20% 20%, rgb(30 64 175 / 0.09) 0%, transparent 58%), radial-gradient(ellipse at 82% 72%, rgb(249 115 22 / 0.08) 0%, transparent 56%), - linear-gradient(180deg, #0a0f1a 0%, #0d1728 46%, #08111f 100%); + linear-gradient(180deg, #172554 0%, #1b3068 46%, #0f1a40 100%); } .fleet-dashboard-shell * { scrollbar-width: thin; - scrollbar-color: rgb(59 130 246 / 0.22) transparent; + scrollbar-color: rgb(1 26 148 / 0.22) transparent; } .fleet-dashboard-shell *::-webkit-scrollbar { @@ -112,7 +112,7 @@ html.dark body { .fleet-dashboard-shell *::-webkit-scrollbar-thumb { border-radius: 999px; - background: rgb(59 130 246 / 0.22); + background: rgb(1 26 148 / 0.22); } .fleet-dashboard-shell .card, @@ -127,8 +127,8 @@ html.dark body { .fleet-dashboard-shell .card:hover, .fleet-dashboard-shell .glass-card:hover { - border-color: rgb(59 130 246 / 0.24); - box-shadow: 0 18px 60px rgb(15 23 42 / 0.10), 0 0 20px rgb(59 130 246 / 0.10); + border-color: rgb(1 26 148 / 0.24); + box-shadow: 0 18px 60px rgb(15 23 42 / 0.10), 0 0 20px rgb(1 26 148 / 0.10); } html.dark .fleet-dashboard-shell .card, @@ -138,33 +138,45 @@ html.dark body { html.dark .fleet-dashboard-shell .card:hover, html.dark .fleet-dashboard-shell .glass-card:hover { - box-shadow: 0 18px 60px rgb(0 0 0 / 0.26), 0 0 20px rgb(59 130 246 / 0.12); + box-shadow: 0 18px 60px rgb(0 0 0 / 0.26), 0 0 20px rgb(1 26 148 / 0.12); } .fleet-dashboard-shell .btn-primary { position: relative; overflow: hidden; - box-shadow: 0 2px 10px rgb(59 130 246 / 0.30); + box-shadow: 0 2px 10px rgb(1 26 148 / 0.30); @apply rounded-lg bg-gradient-to-br from-blue-500 to-blue-700 text-white hover:from-blue-400 hover:to-blue-600; } + html.dark .fleet-dashboard-shell .btn-primary { + @apply from-[#2e50f1] to-[#090385] hover:from-[#1e3de0] hover:to-[#2e50f1]; + } + .fleet-dashboard-shell .btn-secondary { - border-color: rgb(59 130 246 / 0.18); - background: rgb(59 130 246 / 0.08); + border-color: rgb(1 26 148 / 0.18); + background: rgb(1 26 148 / 0.08); color: var(--fleet-text-primary); @apply rounded-lg hover:border-blue-400/40 hover:bg-blue-500/15 hover:text-white; } + html.dark .fleet-dashboard-shell .btn-secondary { + @apply hover:border-[#2e50f1]/40 hover:bg-[#2e50f1]/15; + } + .fleet-dashboard-shell .input-field { - border-color: rgb(59 130 246 / 0.14); - background: rgb(59 130 246 / 0.07); + border-color: rgb(1 26 148 / 0.14); + background: rgb(1 26 148 / 0.07); color: var(--fleet-text-primary); @apply rounded-lg placeholder:text-slate-500 focus:border-blue-400 focus:ring-blue-500/20; } + html.dark .fleet-dashboard-shell .input-field { + @apply focus:border-[#2e50f1] focus:ring-[#2e50f1]/20; + } + .fleet-dashboard-shell .progress-bar { height: 6px; - background: rgb(59 130 246 / 0.12); + background: rgb(1 26 148 / 0.12); @apply rounded-full; } @@ -172,13 +184,17 @@ html.dark body { @apply bg-gradient-to-r from-blue-500 to-blue-400; } + html.dark .fleet-dashboard-shell .progress-fill.blue { + @apply from-[#2e50f1] to-[#1e3de0]; + } + .fleet-dashboard-shell .progress-fill.orange { @apply bg-gradient-to-r from-orange-500 to-orange-600; } .fleet-dashboard-shell .badge-blue { - border: 1px solid rgb(59 130 246 / 0.30); - background: rgb(59 130 246 / 0.15); + border: 1px solid rgb(1 26 148 / 0.30); + background: rgb(1 26 148 / 0.15); color: var(--fleet-accent-blue-light); } @@ -228,8 +244,8 @@ html.dark body { } html.dark .glass-card { - border-color: rgb(30 60 140 / 0.40); - background-color: rgb(11 25 55 / 0.65); + border-color: rgb(30 64 175 / 0.40); + background-color: rgb(23 37 84 / 0.65); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.36); } @@ -239,7 +255,7 @@ html.dark body { } html.dark .progress-bar { - background-color: rgb(30 60 140 / 0.3); + background-color: rgb(30 64 175 / 0.3); } .progress-fill { @@ -255,7 +271,7 @@ html.dark body { } html.dark .progress-fill.blue { - @apply bg-gradient-to-r from-blue-400 to-blue-300; + @apply bg-gradient-to-r from-[#2e50f1] to-[#1e3de0]; } html.dark .progress-fill.orange { @@ -303,8 +319,8 @@ html.dark body { } html.dark .card { - border-color: rgb(30 60 140 / 0.40); - background-color: rgb(11 25 55 / 0.72); + border-color: rgb(30 64 175 / 0.40); + background-color: rgb(23 37 84 / 0.72); } .badge-green { @@ -312,7 +328,7 @@ html.dark body { } .badge-blue { - @apply inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-700 dark:bg-blue-950/40 dark:text-blue-300; + @apply inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-700 dark:bg-[#2e50f1]/40 dark:text-[#1e3de0]; } .badge-amber { @@ -324,7 +340,7 @@ html.dark body { } .badge-gray { - @apply inline-flex items-center rounded-full bg-stone-100 px-2.5 py-0.5 text-xs font-medium text-stone-600 dark:bg-blue-950/40 dark:text-slate-300; + @apply inline-flex items-center rounded-full bg-stone-100 px-2.5 py-0.5 text-xs font-medium text-stone-600 dark:bg-[#2e50f1]/40 dark:text-slate-300; } .badge-purple { @@ -349,20 +365,20 @@ html.dark body { @layer utilities { .dark .bg-slate-50 { - background-color: rgb(7 16 46); + background-color: #172554; } .dark .bg-white { - background-color: rgb(11 25 55); + background-color: rgb(23 37 84 / 0.78); } .dark .bg-white\/90 { - background-color: rgb(11 25 55 / 0.9); + background-color: rgb(23 37 84 / 0.9); } .dark .border-slate-200, .dark .border-stone-200 { - border-color: rgb(30 60 140 / 0.50); + border-color: rgb(30 64 175 / 0.50); } .dark .text-slate-900 { @@ -370,22 +386,22 @@ html.dark body { } .dark .text-slate-700 { - color: rgb(203 213 225); + color: rgb(214 211 209); } .dark .text-slate-600, .dark .text-stone-500 { - color: rgb(148 163 184); + color: rgb(168 162 158); } .dark .text-slate-500, .dark .text-slate-400, .dark .text-stone-400 { - color: rgb(100 116 139); + color: rgb(120 113 108); } .dark .hover\:bg-slate-100:hover { - background-color: rgb(20 42 90); + background-color: rgb(30 58 138 / 0.4); } .dark .hover\:text-slate-900:hover { diff --git a/apps/dashboard/src/app/layout.tsx b/apps/dashboard/src/app/layout.tsx index 1d1733c..e1917e5 100644 --- a/apps/dashboard/src/app/layout.tsx +++ b/apps/dashboard/src/app/layout.tsx @@ -17,8 +17,8 @@ export const metadata: Metadata = { title: 'RentalDriveGo Dashboard', description: 'Manage your rental car business', icons: { - icon: '/dashboard/icon.svg', - shortcut: '/dashboard/icon.svg', + icon: '/icon.svg', + shortcut: '/icon.svg', }, } diff --git a/apps/dashboard/src/app/public-auth-pages.test.ts b/apps/dashboard/src/app/public-auth-pages.test.ts index 7d93993..7ad59c2 100644 --- a/apps/dashboard/src/app/public-auth-pages.test.ts +++ b/apps/dashboard/src/app/public-auth-pages.test.ts @@ -5,6 +5,8 @@ import ResetPasswordPageClient from "./reset-password/ResetPasswordPageClient"; import ForgotPasswordPage from "./forgot-password/page"; import ResetPasswordPage from "./reset-password/page"; import AcceptInvitePage from "./onboarding/accept-invite/page"; +import SignUpForm from "./sign-up/[[...sign-up]]/SignUpForm"; +import SignUpPage from "./sign-up/[[...sign-up]]/page"; function collectText(node: unknown): string[] { if (node === null || node === undefined || typeof node === "boolean") @@ -56,6 +58,19 @@ describe("dashboard public auth pages", () => { expect(page.props.embedded).toBe(true); }); + it("renders sign-up in embedded mode when requested", async () => { + const page = await SignUpPage({ + searchParams: Promise.resolve({ embedded: "1" }), + }); + + expect(isValidElement(page)).toBe(true); + expect(page.type).toBe(React.Suspense); + const child = page.props.children; + expect(isValidElement(child)).toBe(true); + expect(child.type).toBe(SignUpForm); + expect(child.props.embedded).toBe(true); + }); + it("renders reset-password in embedded mode for the public shell route", () => { const page = ResetPasswordPage(); diff --git a/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx b/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx index 2ef70ad..68a91c0 100644 --- a/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx +++ b/apps/dashboard/src/app/reset-password/ResetPasswordPageClient.tsx @@ -240,7 +240,6 @@ function ResetShell({
- RentalDriveGo

{title}

{subtitle &&

{subtitle}

}
diff --git a/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx b/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx index f4d166a..d19bad8 100644 --- a/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx +++ b/apps/dashboard/src/app/sign-in/[[...sign-in]]/SignInPageClient.tsx @@ -10,7 +10,7 @@ import { adminUrl, marketplaceUrl } from "@/lib/urls"; import { API_BASE, EMPLOYEE_PROFILE_KEY } from "@/lib/api"; import { toPublicDashboardPath } from "@/lib/dashboardPaths"; -const DASHBOARD_LOGO_SRC = "/dashboard/rentalcardrive.png"; +const DASHBOARD_LOGO_SRC = "/dashboard/rentaldrivego.png"; function notifyParent(message: Record) { if (typeof window === "undefined" || window.parent === window) return; @@ -184,13 +184,11 @@ export default function SignInPageClient({ width={104} height={104} priority + unoptimized className="h-24 w-24 rounded-[1.75rem] border border-stone-200/80 bg-white/85 p-1.5 shadow-sm transition-colors dark:border-blue-800 dark:bg-blue-950/80" />
-

- RentalDriveGo -

{dict.subtitle}

diff --git a/apps/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx b/apps/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx index c7427d8..29446ca 100644 --- a/apps/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx +++ b/apps/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx @@ -1,17 +1,46 @@ "use client"; import Image from "next/image"; -import { useState } from "react"; -import { useSearchParams } from "next/navigation"; +import { useEffect, useRef, useState } from "react"; +import { usePathname, useRouter, useSearchParams } from "next/navigation"; import { apiFetch } from "@/lib/api"; import PublicShell from "@/components/layout/PublicShell"; import { useDashboardI18n } from "@/components/I18nProvider"; import { marketplaceUrl } from "@/lib/urls"; -export default function SignUpForm() { - const { language, setLanguage } = useDashboardI18n(); +const DASHBOARD_LOGO_SRC = "/dashboard/rentaldrivego.png"; + +function notifyParent(message: Record) { + if (typeof window === "undefined" || window.parent === window) return; + window.parent.postMessage(message, "*"); +} + +function withAuthQuery( + path: string, + params: { embedded: boolean; language: "en" | "fr" | "ar"; theme: string }, +) { + const search = new URLSearchParams({ + lang: params.language, + theme: params.theme, + }); + + if (params.embedded) search.set("embedded", "1"); + + return `${path}?${search.toString()}`; +} + +export default function SignUpForm({ + embedded = false, +}: { + embedded?: boolean; +}) { + const { language, theme, setLanguage, setTheme } = useDashboardI18n(); + const pathname = usePathname(); + const router = useRouter(); const searchParams = useSearchParams(); const requestedLanguage = searchParams.get("lang"); + const requestedTheme = searchParams.get("theme"); + const initializedFromQuery = useRef(false); const initialLanguage = requestedLanguage === "en" || requestedLanguage === "fr" || @@ -29,9 +58,79 @@ export default function SignUpForm() { const [error, setError] = useState(null); const [success, setSuccess] = useState(false); + useEffect(() => { + if (initializedFromQuery.current) return; + + if ( + (requestedLanguage === "en" || + requestedLanguage === "fr" || + requestedLanguage === "ar") && + requestedLanguage !== language + ) { + setLanguage(requestedLanguage); + setPreferredLanguage(requestedLanguage); + } + + if ( + (requestedTheme === "light" || requestedTheme === "dark") && + requestedTheme !== theme + ) { + setTheme(requestedTheme); + } + + initializedFromQuery.current = true; + }, [ + language, + requestedLanguage, + requestedTheme, + setLanguage, + setTheme, + theme, + ]); + + useEffect(() => { + if (!initializedFromQuery.current) return; + if (language !== preferredLanguage) setPreferredLanguage(language); + }, [language, preferredLanguage]); + + useEffect(() => { + if (!initializedFromQuery.current) return; + + const params = new URLSearchParams(searchParams.toString()); + let changed = false; + + if (params.get("lang") !== preferredLanguage) { + params.set("lang", preferredLanguage); + changed = true; + } + + if (params.get("theme") !== theme) { + params.set("theme", theme); + changed = true; + } + + if (embedded && params.get("embedded") !== "1") { + params.set("embedded", "1"); + changed = true; + } + + if (!changed) return; + + const nextQuery = params.toString(); + router.replace(nextQuery ? `${pathname}?${nextQuery}` : pathname, { + scroll: false, + }); + }, [embedded, pathname, preferredLanguage, router, searchParams, theme]); + + useEffect(() => { + const currentPath = + window.location.pathname + (window.location.search || ""); + notifyParent({ type: "rentaldrivego:embedded-path", path: currentPath }); + }, [pathname, searchParams]); + const dict = { en: { - title: "Create your workspace", + title: "Create account", subtitle: "Enter your email and password to get started.", email: "Email", emailPlaceholder: "you@company.com", @@ -39,7 +138,7 @@ export default function SignUpForm() { passwordPlaceholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", passwordHint: "Minimum 8 characters", languageLabel: "Preferred language", - create: "Create workspace", + create: "Create account", creating: "Creating\u2026", alreadyHave: "Already have an account?", signIn: "Sign in", @@ -51,7 +150,7 @@ export default function SignUpForm() { successSignIn: "Go to sign in", }, fr: { - title: "Cr\u00e9ez votre espace", + title: "Cr\u00e9er un compte", subtitle: "Saisissez votre email et mot de passe pour commencer.", email: "Email", emailPlaceholder: "vous@entreprise.com", @@ -59,7 +158,7 @@ export default function SignUpForm() { passwordPlaceholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", passwordHint: "Minimum 8 caract\u00e8res", languageLabel: "Langue pr\u00e9f\u00e9r\u00e9e", - create: "Cr\u00e9er mon espace", + create: "Cr\u00e9er un compte", creating: "Cr\u00e9ation\u2026", alreadyHave: "Vous avez d\u00e9j\u00e0 un compte ?", signIn: "Se connecter", @@ -72,7 +171,7 @@ export default function SignUpForm() { }, ar: { title: - "\u0623\u0646\u0634\u0626 \u0645\u0633\u0627\u062d\u0629 \u0639\u0645\u0644\u0643", + "\u0623\u0646\u0634\u0626 \u062d\u0633\u0627\u0628\u064b\u0627", subtitle: "\u0623\u062f\u062e\u0644 \u0628\u0631\u064a\u062f\u0643 \u0627\u0644\u0625\u0644\u0643\u062a\u0631\u0648\u0646\u064a \u0648\u0643\u0644\u0645\u0629 \u0627\u0644\u0645\u0631\u0648\u0631 \u0644\u0644\u0628\u062f\u0621.", email: @@ -85,7 +184,7 @@ export default function SignUpForm() { languageLabel: "\u0627\u0644\u0644\u063a\u0629 \u0627\u0644\u0645\u0641\u0636\u0644\u0629", create: - "\u0623\u0646\u0634\u0626 \u0627\u0644\u0645\u0633\u0627\u062d\u0629", + "\u0623\u0646\u0634\u0626 \u062d\u0633\u0627\u0628\u064b\u0627", creating: "\u062c\u0627\u0631\u064d \u0627\u0644\u0625\u0646\u0634\u0627\u0621\u2026", alreadyHave: @@ -105,6 +204,12 @@ export default function SignUpForm() { }, }[preferredLanguage]; + const signInHref = withAuthQuery("/sign-in", { + embedded, + language: preferredLanguage, + theme, + }); + async function handleSubmit(e: React.FormEvent) { e.preventDefault(); setLoading(true); @@ -135,17 +240,18 @@ export default function SignUpForm() { if (success) { return ( - -
+ +
RentalDriveGo
@@ -156,7 +262,8 @@ export default function SignUpForm() { {dict.successMessage.replace("{email}", email)}

{dict.successSignIn} @@ -169,23 +276,24 @@ export default function SignUpForm() { } return ( - -
+ +
-

+

{dict.title}

@@ -281,32 +389,6 @@ export default function SignUpForm() {

-
- - {dict.languageLabel} * - -
- {(["en", "fr", "ar"] as const).map((lang) => ( - - ))} -
-
-
); diff --git a/apps/homepage/src/app/[locale]/sign-in/page.tsx b/apps/homepage/src/app/[locale]/sign-in/page.tsx new file mode 100644 index 0000000..763d68d --- /dev/null +++ b/apps/homepage/src/app/[locale]/sign-in/page.tsx @@ -0,0 +1,20 @@ +import { SignInForm } from '@/components/auth/SignInForm' +import { isLocale, type Locale } from '@/lib/localization/config' +import { notFound } from 'next/navigation' +import { Suspense } from 'react' + +export default async function SignInPage({ + params, +}: { + params: Promise<{ locale: string }> +}) { + const { locale: localeValue } = await params + if (!isLocale(localeValue)) notFound() + const locale = localeValue as Locale + + return ( + + + + ) +} diff --git a/apps/homepage/src/components/app-shell/AuthHeader.module.css b/apps/homepage/src/components/app-shell/AuthHeader.module.css new file mode 100644 index 0000000..0e0671c --- /dev/null +++ b/apps/homepage/src/components/app-shell/AuthHeader.module.css @@ -0,0 +1,292 @@ +.header { + position: sticky; + top: 0; + z-index: 50; + border-bottom: 1px solid rgb(231 229 228 / 0.8); + background: rgb(255 255 255 / 0.78); + backdrop-filter: blur(24px); + -webkit-backdrop-filter: blur(24px); + box-shadow: 0 10px 30px rgba(15, 23, 42, 0.08); +} + +:global(html[data-theme='dark']) .header { + border-color: rgb(30 58 138 / 0.8); + background: rgb(23 37 84 / 0.76); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.32); +} + +.inner { + display: flex; + min-height: 3.5rem; + max-width: var(--container-wide); + margin-inline: auto; + flex-direction: column; + gap: 0.375rem; + padding: 0.375rem 1rem; + align-items: stretch; +} + +@media (min-width: 640px) { + .inner { + padding-left: 1.5rem; + padding-right: 1.5rem; + } +} + +@media (min-width: 1024px) { + .inner { + flex-direction: row; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + padding: 0.5rem 2rem; + } +} + +.brand { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; +} + +.brandLink { + display: flex; + align-items: center; + gap: 0.375rem; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.14em; + color: #1c1917; + text-decoration: none; +} + +:global(html[data-theme='dark']) .brandLink { + color: #f5f5f4; +} + +@media (min-width: 640px) { + .brandLink { + font-size: 0.875rem; + letter-spacing: 0.2em; + } +} + +.mark { + width: 2rem; + height: 2rem; + border-radius: 0.375rem; + object-fit: contain; + flex-shrink: 0; +} + +@media (min-width: 640px) { + .mark { + width: 2.25rem; + height: 2.25rem; + } +} + +.controls { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +@media (min-width: 1024px) { + .controls { + flex-direction: row; + align-items: center; + gap: 0.75rem; + } +} + +.pill { + display: flex; + align-items: center; + align-self: flex-start; + border-radius: 999px; + border: 1px solid rgb(231 229 228 / 0.8); + background: rgb(255 255 255 / 0.95); + padding: 0.125rem; + box-shadow: 0 1px 2px rgba(0,0,0,0.05); +} + +:global(html[data-theme='dark']) .pill { + border-color: #1e40af; + background: rgb(23 37 84 / 0.85); +} + +@media (min-width: 640px) { + .pill { + align-self: auto; + padding: 0.25rem; + } +} + +.localeBtn { + display: inline-flex; + min-width: 4.75rem; + align-items: center; + justify-content: center; + gap: 0.375rem; + border-radius: 999px; + padding: 0.375rem 0.625rem; + font-size: 11px; + font-weight: 600; + color: #44403c; + background: transparent; + border: none; + cursor: pointer; + transition: background 0.15s; +} + +.localeBtn:hover { + background: #f5f5f4; +} + +:global(html[data-theme='dark']) .localeBtn { + color: #e7e5e4; +} + +:global(html[data-theme='dark']) .localeBtn:hover { + background: rgb(30 58 138 / 0.4); +} + +@media (min-width: 640px) { + .localeBtn { + min-width: 5.25rem; + padding: 0.5rem 0.75rem; + font-size: 0.75rem; + } +} + +.chevron { + width: 0.875rem; + height: 0.875rem; + transition: transform 0.15s; +} + +.chevronOpen { + transform: rotate(180deg); +} + +.localeMenuWrap { + position: relative; +} + +.localeMenu { + position: absolute; + top: 100%; + z-index: 30; + margin-top: 0.5rem; + width: 12rem; + max-width: calc(100vw - 2rem); + overflow: hidden; + border-radius: 1rem; + border: 1px solid #e7e5e4; + background: rgb(255 255 255 / 0.95); + text-align: left; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.12); + backdrop-filter: blur(16px); + left: 0; +} + +@media (min-width: 640px) { + .localeMenu { + left: auto; + right: 0; + } +} + +:global(html[data-theme='dark']) .localeMenu { + border-color: #1e3a8a; + background: rgb(23 37 84 / 0.95); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35); +} + +.localeOption { + display: flex; + width: 100%; + align-items: center; + gap: 0.75rem; + padding: 0.75rem 1rem; + font-size: 0.875rem; + color: #44403c; + background: transparent; + border: none; + cursor: pointer; + transition: background 0.15s, color 0.15s; +} + +.localeOption:hover { + background: #f5f5f4; + color: #1e3a8a; +} + +:global(html[data-theme='dark']) .localeOption { + color: #e7e5e4; +} + +:global(html[data-theme='dark']) .localeOption:hover { + background: rgb(30 58 138 / 0.4); + color: #fff; +} + +.divider { + width: 1px; + height: 1.5rem; + margin: 0 0.25rem; + background: #e7e5e4; +} + +:global(html[data-theme='dark']) .divider { + background: #334155; +} + +.themeBtn { + display: inline-flex; + align-items: center; + gap: 0.375rem; + border-radius: 999px; + padding: 0.25rem 0.5rem; + background: transparent; + border: none; + cursor: pointer; + transition: background 0.15s; +} + +.themeBtn:hover { + background: #f5f5f4; +} + +:global(html[data-theme='dark']) .themeBtn:hover { + background: rgb(30 58 138 / 0.4); +} + +@media (min-width: 640px) { + .themeBtn { + padding: 0.375rem 0.625rem; + } +} + +.themeLabel { + border-radius: 999px; + background: #1e3a8a; + padding: 0.25rem 0.75rem; + font-size: 11px; + font-weight: 600; + color: #fff; +} + +:global(html[data-theme='dark']) .themeLabel { + background: #fb923c; +} + +@media (min-width: 640px) { + .themeLabel { + padding: 0.375rem 0.875rem; + font-size: 0.75rem; + } +} diff --git a/apps/homepage/src/components/app-shell/AuthHeader.tsx b/apps/homepage/src/components/app-shell/AuthHeader.tsx new file mode 100644 index 0000000..eb870c7 --- /dev/null +++ b/apps/homepage/src/components/app-shell/AuthHeader.tsx @@ -0,0 +1,146 @@ +'use client'; + +import { localizedPath, locales, type Locale } from '@/lib/localization/config'; +import { persistTheme, themePreferenceEvent } from '@/lib/theme/client'; +import { type ThemePreference } from '@/lib/theme/config'; +import Image from 'next/image'; +import { usePathname, useRouter } from 'next/navigation'; +import { useEffect, useRef, useState } from 'react'; +import styles from './AuthHeader.module.css'; + +const langLabels: Record = { en: 'EN', fr: 'FR', ar: 'AR' }; +const langFlags: Record = { en: '🇺🇸', fr: '🇫🇷', ar: '🇲🇦' }; + +const themeDict: Record> = { + en: { theme: 'Theme', light: 'Light', dark: 'Dark' }, + fr: { theme: 'Mode', light: 'Clair', dark: 'Sombre' }, + ar: { theme: 'الوضع', light: 'فاتح', dark: 'داكن' }, +}; + +interface Props { + locale: Locale; + themePreference: ThemePreference; +} + +export function AuthHeader({ locale, themePreference: initialPref }: Props) { + const pathname = usePathname(); + const router = useRouter(); + const [pref, setPref] = useState(initialPref); + + const t = themeDict[locale] ?? themeDict.en; + const themeLabel = pref === 'dark' ? t.dark : t.light; + + const localeMenuRef = useRef(null); + const [localeOpen, setLocaleOpen] = useState(false); + + useEffect(() => { + setPref(initialPref); + }, [initialPref]); + + useEffect(() => { + const handler = () => { + const stored = localStorage.getItem('hpc.theme.preference'); + if (stored === 'light' || stored === 'dark' || stored === 'system') setPref(stored); + }; + window.addEventListener(themePreferenceEvent, handler); + return () => window.removeEventListener(themePreferenceEvent, handler); + }, []); + + useEffect(() => { + function handleClick(e: MouseEvent) { + if (!localeMenuRef.current?.contains(e.target as Node)) setLocaleOpen(false); + } + document.addEventListener('mousedown', handleClick); + return () => document.removeEventListener('mousedown', handleClick); + }, []); + + const cycleTheme = () => { + const next = pref === 'dark' ? 'light' : 'dark'; + persistTheme(next); + setPref(next); + }; + + const switchLocale = (next: Locale) => { + setLocaleOpen(false); + const segments = pathname.split('/').filter(Boolean); + if (segments.length >= 2) segments[1] = next; + router.push('/' + segments.join('/')); + }; + + const otherLocales = locales.filter((l) => l !== locale); + const homePath = localizedPath('home', locale); + + return ( +
+
+ + +
+
+
+ + {localeOpen && ( +
+ {otherLocales.map((l) => ( + + ))} +
+ )} +
+ +
+
+
+
+ ); +} diff --git a/apps/homepage/src/components/app-shell/BrandLink.tsx b/apps/homepage/src/components/app-shell/BrandLink.tsx index ceebd68..9dae94f 100644 --- a/apps/homepage/src/components/app-shell/BrandLink.tsx +++ b/apps/homepage/src/components/app-shell/BrandLink.tsx @@ -15,12 +15,13 @@ export function BrandLink({ locale, label }: { locale: Locale; label: string }) aria-current={routeIdFromPathname(pathname) === 'home' ? 'page' : undefined} > RentalDriveGo diff --git a/apps/homepage/src/components/app-shell/HeaderRouter.tsx b/apps/homepage/src/components/app-shell/HeaderRouter.tsx new file mode 100644 index 0000000..85c2a5a --- /dev/null +++ b/apps/homepage/src/components/app-shell/HeaderRouter.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { usePathname } from 'next/navigation'; +import { SiteHeader } from './SiteHeader'; +import { AuthHeader } from './AuthHeader'; +import type { ShellMessages } from '@/lib/localization/messages'; +import type { Locale } from '@/lib/localization/config'; +import type { ThemePreference } from '@/lib/theme/config'; + +interface Props { + locale: Locale; + messages: ShellMessages; + themePreference: ThemePreference; +} + +export function HeaderRouter({ locale, messages, themePreference }: Props) { + const pathname = usePathname(); + const isAuth = pathname.endsWith('/sign-in') || pathname.endsWith('/forgot-password'); + + if (isAuth) { + return ; + } + + return ; +} diff --git a/apps/homepage/src/components/app-shell/MobileNavigation.tsx b/apps/homepage/src/components/app-shell/MobileNavigation.tsx index eea8f58..2958a58 100644 --- a/apps/homepage/src/components/app-shell/MobileNavigation.tsx +++ b/apps/homepage/src/components/app-shell/MobileNavigation.tsx @@ -1,9 +1,9 @@ 'use client'; import { ActionLink } from '@/components/actions/ActionLink'; -import { DemoTrigger } from '@/components/integrations/DemoTrigger'; import type { ShellMessages } from '@/lib/localization/messages'; import { localizedPath, type Locale } from '@/lib/localization/config'; +import { accountCreateUrl } from '@/lib/account-urls'; import type { ThemePreference } from '@/lib/theme/config'; import { useEffect, useRef, useState } from 'react'; import { HeaderNavigation } from './HeaderNavigation'; @@ -19,14 +19,12 @@ interface MobileNavigationProps { locale: Locale; messages: ShellMessages; themePreference: ThemePreference; - demoEnabled: boolean; } export function MobileNavigation({ locale, messages, themePreference, - demoEnabled, }: MobileNavigationProps) { const [open, setOpen] = useState(false); const dialogRef = useRef(null); @@ -132,12 +130,9 @@ export function MobileNavigation({ {messages.header.login} - + + {messages.header.demo} +
diff --git a/apps/homepage/src/components/app-shell/SiteFooter.tsx b/apps/homepage/src/components/app-shell/SiteFooter.tsx index 213b7f8..0a9df54 100644 --- a/apps/homepage/src/components/app-shell/SiteFooter.tsx +++ b/apps/homepage/src/components/app-shell/SiteFooter.tsx @@ -1,5 +1,5 @@ import { StatusBadge } from '@/components/foundation/StatusBadge'; -import { DemoTrigger } from '@/components/integrations/DemoTrigger'; +import { accountCreateUrl } from '@/lib/account-urls'; import { localizedPath, type Locale } from '@/lib/localization/config'; import type { ShellMessages } from '@/lib/localization/messages'; import styles from './SiteFooter.module.css'; @@ -11,7 +11,6 @@ function signInUrl(locale: Locale): string { interface SiteFooterProps { locale: Locale; messages: ShellMessages; - demoEnabled: boolean; } function HashLink({ locale, hash, children }: { locale: Locale; hash: string; children: string }) { @@ -46,7 +45,7 @@ function PendingItem({ label, pending }: { label: string; pending: string }) { ); } -export function SiteFooter({ locale, messages, demoEnabled }: SiteFooterProps) { +export function SiteFooter({ locale, messages }: SiteFooterProps) { const footer = messages.footer; const year = new Date().getUTCFullYear(); @@ -98,12 +97,7 @@ export function SiteFooter({ locale, messages, demoEnabled }: SiteFooterProps) {
  • - + {footer.links.demo}
  • diff --git a/apps/homepage/src/components/app-shell/SiteHeader.tsx b/apps/homepage/src/components/app-shell/SiteHeader.tsx index 2d65917..3cc7b5b 100644 --- a/apps/homepage/src/components/app-shell/SiteHeader.tsx +++ b/apps/homepage/src/components/app-shell/SiteHeader.tsx @@ -2,7 +2,7 @@ import { localizedPath, 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 { DemoTrigger } from '@/components/integrations/DemoTrigger'; +import { accountCreateUrl } from '@/lib/account-urls'; import { BrandLink } from './BrandLink'; import { HeaderNavigation } from './HeaderNavigation'; import { LocaleSelector } from './LocaleSelector'; @@ -18,10 +18,9 @@ interface SiteHeaderProps { locale: Locale; messages: ShellMessages; themePreference: ThemePreference; - demoEnabled: boolean; } -export function SiteHeader({ locale, messages, themePreference, demoEnabled }: SiteHeaderProps) { +export function SiteHeader({ locale, messages, themePreference }: SiteHeaderProps) { return (
    @@ -56,27 +55,23 @@ export function SiteHeader({ locale, messages, themePreference, demoEnabled }: S {messages.header.login} - + + {messages.header.demo} +
    - + > + {messages.header.demo} +
    diff --git a/apps/homepage/src/components/auth/AuthShell.tsx b/apps/homepage/src/components/auth/AuthShell.tsx index c46f969..d298cd2 100644 --- a/apps/homepage/src/components/auth/AuthShell.tsx +++ b/apps/homepage/src/components/auth/AuthShell.tsx @@ -26,15 +26,17 @@ export function AuthShell({ locale, title, subtitle, children }: AuthShellProps) }} >
    -
    +
    RentalDriveGo -

    - RentalDriveGo -

    + figure { + inline-size: min(14rem, 52vw); + justify-self: center; + } +} diff --git a/apps/homepage/src/components/homepage/sections/FinalCtaSection.tsx b/apps/homepage/src/components/homepage/sections/FinalCtaSection.tsx index f9db89e..65b9472 100644 --- a/apps/homepage/src/components/homepage/sections/FinalCtaSection.tsx +++ b/apps/homepage/src/components/homepage/sections/FinalCtaSection.tsx @@ -1,33 +1,35 @@ -import { DemoTrigger } from '@/components/integrations/DemoTrigger'; import { Container, Section } from '@/components/layout/LayoutPrimitives'; import { CTA } from '@/components/marketing/CTA'; +import { ResponsiveImage } from '@/components/media/MediaFrame'; import type { HomepageContent } from '@/content/homepage-model'; import styles from './FinalCtaSection.module.css'; interface FinalCtaSectionProps { content: HomepageContent['final']; - demoSubmissionEnabled: boolean; } -export function FinalCtaSection({ content, demoSubmissionEnabled }: FinalCtaSectionProps) { +export function FinalCtaSection({ content }: FinalCtaSectionProps) { return (
    - - } - /> +
    + + +
    ); diff --git a/apps/homepage/src/components/homepage/sections/HeroSection.tsx b/apps/homepage/src/components/homepage/sections/HeroSection.tsx index 78d62f5..cfddfe5 100644 --- a/apps/homepage/src/components/homepage/sections/HeroSection.tsx +++ b/apps/homepage/src/components/homepage/sections/HeroSection.tsx @@ -1,7 +1,6 @@ import { ActionLink } from '@/components/actions/ActionLink'; -import { DemoTrigger } from '@/components/integrations/DemoTrigger'; import { Container, Section } from '@/components/layout/LayoutPrimitives'; -import { ProductPreview } from '@/components/marketing/ProductPreview'; +import { ResponsiveImage } from '@/components/media/MediaFrame'; import { Eyebrow, Heading, Text } from '@/components/typography/Typography'; import type { HomepageContent } from '@/content/homepage-model'; import styles from './HeroSection.module.css'; @@ -9,10 +8,9 @@ import styles from './HeroSection.module.css'; interface HeroSectionProps { content: HomepageContent['hero']; homePath: string; - demoSubmissionEnabled: boolean; } -export function HeroSection({ content, homePath, demoSubmissionEnabled }: HeroSectionProps) { +export function HeroSection({ content, homePath }: HeroSectionProps) { return (
    @@ -24,23 +22,32 @@ export function HeroSection({ content, homePath, demoSubmissionEnabled }: HeroSe {content.body}
    - + + {content.primary.label} + {content.secondary.label}

    - +
    diff --git a/apps/homepage/src/components/homepage/sections/ModulesSection.module.css b/apps/homepage/src/components/homepage/sections/ModulesSection.module.css new file mode 100644 index 0000000..c6c5b73 --- /dev/null +++ b/apps/homepage/src/components/homepage/sections/ModulesSection.module.css @@ -0,0 +1,16 @@ +.modulesLayout { + display: grid; + grid-template-columns: minmax(18rem, 0.78fr) minmax(0, 1.22fr); + align-items: start; + gap: var(--space-8); +} + +.modulesImage { + object-position: center; +} + +@media (max-width: 1023px) { + .modulesLayout { + grid-template-columns: 1fr; + } +} diff --git a/apps/homepage/src/components/homepage/sections/ModulesSection.tsx b/apps/homepage/src/components/homepage/sections/ModulesSection.tsx index 61589bd..7400c93 100644 --- a/apps/homepage/src/components/homepage/sections/ModulesSection.tsx +++ b/apps/homepage/src/components/homepage/sections/ModulesSection.tsx @@ -2,7 +2,9 @@ import { Container, Section, Stack } from '@/components/layout/LayoutPrimitives' import { FeatureCard } from '@/components/marketing/FeatureCard'; import { SectionHeader } from '@/components/marketing/SectionHeader'; import { FeatureGrid } from '@/components/marketing/SectionPatterns'; +import { ResponsiveImage } from '@/components/media/MediaFrame'; import type { HomepageContent } from '@/content/homepage-model'; +import styles from './ModulesSection.module.css'; export function ModulesSection({ content }: { content: HomepageContent['modules'] }) { return ( @@ -10,16 +12,27 @@ export function ModulesSection({ content }: { content: HomepageContent['modules' - - {content.items.map((item) => ( - - ))} - +
    + + + {content.items.map((item) => ( + + ))} + +
    diff --git a/apps/homepage/src/components/homepage/sections/ResultsSection.module.css b/apps/homepage/src/components/homepage/sections/ResultsSection.module.css index 358a00b..b87a412 100644 --- a/apps/homepage/src/components/homepage/sections/ResultsSection.module.css +++ b/apps/homepage/src/components/homepage/sections/ResultsSection.module.css @@ -1,3 +1,7 @@ +.resultsImage { + object-position: center; +} + .methodList { display: grid; gap: var(--space-2); diff --git a/apps/homepage/src/components/homepage/sections/ResultsSection.tsx b/apps/homepage/src/components/homepage/sections/ResultsSection.tsx index 953d9a5..9432e90 100644 --- a/apps/homepage/src/components/homepage/sections/ResultsSection.tsx +++ b/apps/homepage/src/components/homepage/sections/ResultsSection.tsx @@ -2,6 +2,7 @@ import { Container, Section, Stack } from '@/components/layout/LayoutPrimitives' import { FeatureCard } from '@/components/marketing/FeatureCard'; import { SectionHeader } from '@/components/marketing/SectionHeader'; import { FeatureGrid } from '@/components/marketing/SectionPatterns'; +import { ResponsiveImage } from '@/components/media/MediaFrame'; import { Callout } from '@/components/surfaces/Callout'; import type { HomepageContent } from '@/content/homepage-model'; import styles from './ResultsSection.module.css'; @@ -12,6 +13,15 @@ export function ResultsSection({ content }: { content: HomepageContent['results' + {content.metrics.map((metric) => ( + diff --git a/apps/homepage/src/content/homepage-model.ts b/apps/homepage/src/content/homepage-model.ts index 58649dc..79ca8fc 100644 --- a/apps/homepage/src/content/homepage-model.ts +++ b/apps/homepage/src/content/homepage-model.ts @@ -1,16 +1,21 @@ import type { ApprovedIconName } from '@/components/foundation/Icon'; import type { PreviewRow } from '@/components/marketing/ProductPreview'; import type { HomepageMessages, ShellMessages } from '@/lib/localization/messages'; +import type { Locale } from '@/lib/localization/config'; +import { accountCreateUrl } from '@/lib/account-urls'; import { fleetBandIds, pricingPlanIds, type FleetBandId, type PricingPlanId, } from '@/content/pricing-config'; +import { existsSync } from 'node:fs'; +import { join } from 'node:path'; export interface HomepageAction { label: string; - disabledReason: string; + href?: string; + disabledReason?: string; } export interface TitledDescription { @@ -43,6 +48,13 @@ export interface HomepagePricingPlan { cta: string; } +export interface HomepageImageAsset { + src: string; + alt: string; + width: number; + height: number; +} + export interface HomepageContent { hero: { eyebrow: string; @@ -56,6 +68,7 @@ export interface HomepageContent { illustrativeLabel: string; rows: PreviewRow[]; }; + image: HomepageImageAsset; }; trust: { eyebrow: string; @@ -71,12 +84,14 @@ export interface HomepageContent { beforeItems: string[]; afterTitle: string; afterItems: string[]; + image: HomepageImageAsset; }; workflow: { eyebrow: string; title: string; body: string; steps: HomepageWorkflowStep[]; + image: HomepageImageAsset; }; roles: { eyebrow: string; @@ -88,6 +103,7 @@ export interface HomepageContent { title: string; body: string; items: TitledDescription[]; + image: HomepageImageAsset; }; results: { eyebrow: string; @@ -96,6 +112,7 @@ export interface HomepageContent { metrics: TitledDescription[]; methodTitle: string; method: string[]; + image: HomepageImageAsset; }; integrations: { eyebrow: string; @@ -147,6 +164,7 @@ export interface HomepageContent { body: string; primary: HomepageAction; secondary: HomepageAction; + image: HomepageImageAsset; }; } @@ -156,6 +174,42 @@ const moduleIcons: ApprovedIconName[] = ['calendar', 'car', 'check', 'search', ' const resultIcons: ApprovedIconName[] = ['calendar', 'settings', 'car', 'warning']; const securityIcons: ApprovedIconName[] = ['settings', 'search', 'globe', 'warning']; +const homepagePublicPath = join(process.cwd(), 'public'); +const homepagePublicFallbackPath = join(process.cwd(), 'apps/homepage/public'); +const localeAssetSuffix: Record = { + en: 'EN', + fr: 'FR', + ar: 'AR', +}; + +function publicAssetExists(fileName: string): boolean { + return existsSync(join(homepagePublicPath, fileName)) || existsSync(join(homepagePublicFallbackPath, fileName)); +} + +function localizedImage( + subject: string, + locale: Locale, + extension: 'jpeg' | 'png', + dimensions: Pick, + alt: string, +): HomepageImageAsset { + const localizedFile = `${subject}_${localeAssetSuffix[locale]}.${extension}`; + const hyphenLocalizedFile = `${subject}-${locale}.${extension}`; + const defaultFile = `${subject}.${extension}`; + const fileName = publicAssetExists(localizedFile) + ? localizedFile + : publicAssetExists(hyphenLocalizedFile) + ? hyphenLocalizedFile + : defaultFile; + + return { + src: `/${fileName}`, + alt, + width: dimensions.width, + height: dimensions.height, + }; +} + function pair(value: readonly string[], context: string): { title: string; description: string } { const title = value[0]; const description = value[1]; @@ -211,6 +265,7 @@ function mapPairs( export function buildHomepageContent( homepage: HomepageMessages, shell: ShellMessages, + locale: Locale = 'en', ): HomepageContent { const pendingReason = shell.states.pendingBody; @@ -219,7 +274,7 @@ export function buildHomepageContent( eyebrow: homepage.hero.eyebrow, title: homepage.hero.title, body: homepage.hero.body, - primary: { label: homepage.hero.primary, disabledReason: shell.header.demoUnavailable }, + primary: { label: homepage.hero.primary, href: accountCreateUrl(locale) }, secondary: { label: homepage.hero.secondary, disabledReason: pendingReason }, preview: { title: homepage.preview.title, @@ -242,6 +297,13 @@ export function buildHomepageContent( }, ], }, + image: localizedImage( + 'dashboard1', + locale, + 'png', + { width: 1672, height: 941 }, + homepage.preview.title, + ), }, trust: { eyebrow: homepage.trust.eyebrow, @@ -257,6 +319,13 @@ export function buildHomepageContent( beforeItems: homepage.comparison.beforeItems, afterTitle: homepage.comparison.afterTitle, afterItems: homepage.comparison.afterItems, + image: localizedImage( + 'benefit1', + locale, + 'png', + { width: 1672, height: 941 }, + homepage.comparison.title, + ), }, workflow: { eyebrow: homepage.workflow.eyebrow, @@ -266,6 +335,13 @@ export function buildHomepageContent( id: `workflow-${index + 1}`, ...triple(value, `workflow.steps[${index}]`), })), + image: localizedImage( + 'workflow1', + locale, + 'png', + { width: 1672, height: 941 }, + homepage.workflow.title, + ), }, roles: { eyebrow: homepage.roles.eyebrow, @@ -277,6 +353,13 @@ export function buildHomepageContent( title: homepage.modules.title, body: homepage.modules.body, items: mapPairs(homepage.modules.items, 'module', moduleIcons), + image: localizedImage( + 'oilchange1', + locale, + 'png', + { width: 1254, height: 1254 }, + homepage.modules.title, + ), }, results: { eyebrow: homepage.results.eyebrow, @@ -285,6 +368,13 @@ export function buildHomepageContent( metrics: mapPairs(homepage.results.metrics, 'measurement', resultIcons), methodTitle: homepage.results.methodTitle, method: homepage.results.method, + image: localizedImage( + 'dashboard2', + locale, + 'png', + { width: 1672, height: 941 }, + homepage.results.title, + ), }, integrations: { eyebrow: homepage.integrations.eyebrow, @@ -346,8 +436,15 @@ export function buildHomepageContent( eyebrow: homepage.final.eyebrow, title: homepage.final.title, body: homepage.final.body, - primary: { label: homepage.final.primary, disabledReason: shell.header.demoUnavailable }, + primary: { label: homepage.final.primary, href: accountCreateUrl(locale) }, secondary: { label: homepage.final.secondary, disabledReason: pendingReason }, + image: localizedImage( + 'rentaldrivego', + locale, + 'jpeg', + { width: 1220, height: 1167 }, + 'RentalDriveGo', + ), }, }; } diff --git a/apps/homepage/src/lib/account-urls.ts b/apps/homepage/src/lib/account-urls.ts new file mode 100644 index 0000000..48c681a --- /dev/null +++ b/apps/homepage/src/lib/account-urls.ts @@ -0,0 +1,6 @@ +import type { Locale } from '@/lib/localization/config'; + +export function accountCreateUrl(locale: Locale): string { + const params = new URLSearchParams({ lang: locale }); + return `/dashboard/sign-up?${params.toString()}`; +} diff --git a/apps/homepage/src/styles/component-tokens.css b/apps/homepage/src/styles/component-tokens.css index b70c74b..6f48fb6 100644 --- a/apps/homepage/src/styles/component-tokens.css +++ b/apps/homepage/src/styles/component-tokens.css @@ -70,7 +70,7 @@ } html[data-theme='dark'] { - --text-subdued: var(--color-gray-300); + --text-subdued: #a8a29e; --status-success-surface: color-mix(in srgb, var(--color-green-700) 24%, var(--color-navy-900)); --status-success-text: var(--color-green-100); --status-warning-surface: color-mix(in srgb, var(--color-amber-800) 30%, var(--color-navy-900)); @@ -92,18 +92,18 @@ html[data-theme='dark'] { --surface-elevated: var(--color-navy-850); --surface-muted: var(--color-navy-900); --surface-strong: var(--color-navy-800); - --text-primary: var(--color-blue-50); - --text-secondary: var(--color-gray-300); - --text-subdued: var(--color-gray-300); + --text-primary: #f5f5f4; + --text-secondary: #d6d3d1; + --text-subdued: #a8a29e; --text-inverse: var(--color-navy-950); --border-standard: var(--color-navy-800); - --border-strong: var(--color-gray-600); - --interactive-primary: var(--color-blue-200); - --interactive-primary-hover: var(--color-blue-100); + --border-strong: #1d4ed8; + --interactive-primary: var(--color-action-primary); + --interactive-primary-hover: var(--color-action-primary-hover); --interactive-conversion: var(--color-orange-400); --interactive-conversion-hover: var(--color-orange-100); - --link-default: var(--color-blue-200); - --link-hover: var(--color-blue-100); + --link-default: var(--color-action-primary); + --link-hover: var(--color-action-primary-hover); --focus-ring: var(--color-orange-400); --status-success-surface: color-mix(in srgb, var(--color-green-700) 24%, var(--color-navy-900)); --status-success-text: var(--color-green-100); diff --git a/apps/homepage/src/styles/tokens.css b/apps/homepage/src/styles/tokens.css index 23e3812..aeed38f 100644 --- a/apps/homepage/src/styles/tokens.css +++ b/apps/homepage/src/styles/tokens.css @@ -10,11 +10,11 @@ --color-orange-400: #fb923c; --color-orange-600: #ea580c; --color-orange-700: #c2410c; - --color-navy-950: #081426; - --color-navy-900: #0d1b2e; - --color-navy-850: #11233b; - --color-navy-800: #162b46; - --color-gray-25: #fbfdff; + --color-navy-950: #172554; + --color-navy-900: #1b3268; + --color-navy-850: #1e3a8a; + --color-navy-800: #1e40af; + --color-gray-25: #fafafa; --color-gray-50: #f7f9fc; --color-gray-100: #eef2f7; --color-gray-200: #dbe3ed; @@ -77,8 +77,8 @@ html[data-theme='light'] { --color-canvas: var(--color-gray-25); --color-surface: #ffffff; --color-surface-raised: #ffffff; - --color-surface-muted: #f2f6fb; - --color-surface-strong: #e8f0fa; + --color-surface-muted: #f4f4f5; + --color-surface-strong: #e8e8e8; --color-text: #102035; --color-text-muted: #52647a; --color-border: #d8e2ee; @@ -98,19 +98,19 @@ html[data-theme='dark'] { --color-canvas: var(--color-navy-950); --color-surface: var(--color-navy-900); --color-surface-raised: var(--color-navy-850); - --color-surface-muted: #0b1a2d; - --color-surface-strong: #172d48; - --color-text: #eef5ff; - --color-text-muted: #adbed2; - --color-border: #263c56; - --color-border-strong: #3a536e; - --color-action-primary: #76a9ff; - --color-action-primary-hover: #9bc2ff; + --color-surface-muted: #15214a; + --color-surface-strong: var(--color-navy-800); + --color-text: #f5f5f4; + --color-text-muted: #d6d3d1; + --color-border: var(--color-navy-800); + --color-border-strong: #1d4ed8; + --color-action-primary: #2e50f1; + --color-action-primary-hover: #1e3de0; --color-action-conversion: var(--color-orange-400); --color-action-conversion-hover: #fdba74; --color-focus: var(--color-orange-400); --color-on-inverse: var(--color-navy-950); - --color-soft-blue: #10284a; + --color-soft-blue: var(--color-navy-850); --color-soft-orange: #3a2015; --shadow-color: rgba(0, 0, 0, 0.36); color-scheme: dark; diff --git a/apps/homepage/tests/unit/homepage-content.test.ts b/apps/homepage/tests/unit/homepage-content.test.ts index 321e7c6..940ca45 100644 --- a/apps/homepage/tests/unit/homepage-content.test.ts +++ b/apps/homepage/tests/unit/homepage-content.test.ts @@ -13,7 +13,7 @@ describe('homepage content model', () => { ['fr', frHomepage, frShell], ['ar', arHomepage, arShell], ] as const)('builds complete %s route content', (_locale, homepage, shell) => { - const content = buildHomepageContent(homepage, shell); + const content = buildHomepageContent(homepage, shell, _locale); expect(content.hero.title).toBeTruthy(); expect(content.trust.items).toHaveLength(4); @@ -28,7 +28,10 @@ describe('homepage content model', () => { it('keeps unresolved conversion destinations disabled', () => { const content = buildHomepageContent(enHomepage, enShell); - expect(content.hero.primary.disabledReason).toBe(enShell.header.demoUnavailable); + expect(content.hero.primary.href).toBe('/dashboard/sign-up?lang=en'); + expect(content.hero.primary.disabledReason).toBeUndefined(); + expect(content.final.primary.href).toBe('/dashboard/sign-up?lang=en'); + expect(content.final.primary.disabledReason).toBeUndefined(); expect(content.pricing.actionDisabledReason).toBe(enShell.states.pendingBody); expect(content.final.secondary.disabledReason).toBe(enShell.states.pendingBody); }); diff --git a/apps/storefront/public/rentaldrivego.jpeg b/apps/storefront/public/rentaldrivego.jpeg new file mode 100644 index 0000000..4547894 Binary files /dev/null and b/apps/storefront/public/rentaldrivego.jpeg differ diff --git a/apps/storefront/src/app/globals.css b/apps/storefront/src/app/globals.css index a493d66..c16f645 100644 --- a/apps/storefront/src/app/globals.css +++ b/apps/storefront/src/app/globals.css @@ -8,7 +8,7 @@ body { html.dark body { background-image: - linear-gradient(180deg, #0a1535 0%, #0d1f52 35%, #091228 100%); + linear-gradient(180deg, #172554 0%, #1b3068 35%, #0f1a40 100%); } .shell { @@ -22,20 +22,20 @@ html.dark body { } html.dark .card { - border-color: rgb(30 60 140 / 0.40); - background-color: rgb(11 25 55 / 0.72); + border-color: rgb(30 64 175 / 0.40); + background-color: rgb(23 37 84 / 0.72); } .site-page { @apply min-h-screen overflow-hidden text-blue-950; background-image: - linear-gradient(180deg, #ffffff 0%, #f5f8ff 28%, #eef4ff 58%, #ffffff 100%); + linear-gradient(180deg, #ffffff 0%, #fafafa 28%, #f5f5f5 58%, #ffffff 100%); } html.dark .site-page { @apply text-slate-100; background-image: - linear-gradient(180deg, #0a1535 0%, #0d1f52 35%, #091228 100%); + linear-gradient(180deg, #172554 0%, #1b3068 35%, #0f1a40 100%); } .site-glow { @@ -47,7 +47,7 @@ html.dark .site-page { html.dark .site-glow { background-image: radial-gradient(circle at top left, rgba(251, 146, 60, 0.22), transparent 34%), - radial-gradient(circle at top right, rgba(96, 165, 250, 0.18), transparent 26%); + radial-gradient(circle at top right, rgba(30, 64, 175, 0.18), transparent 26%); } .site-section { @@ -61,26 +61,26 @@ html.dark .site-glow { } html.dark .site-panel { - border-color: rgb(30 60 140 / 0.40); - background-color: rgb(11 25 55 / 0.72); + border-color: rgb(30 64 175 / 0.40); + background-color: rgb(23 37 84 / 0.72); box-shadow: 0 30px 80px rgba(0, 0, 0, 0.36); } .site-panel-muted { @apply rounded-[2rem] border p-7 transition-colors sm:p-8; border-color: rgb(231 229 228 / 0.8); - background-image: linear-gradient(160deg, #f5f8ff 0%, #edf2ff 100%); + background-image: linear-gradient(160deg, #fafafa 0%, #f5f5f5 100%); } html.dark .site-panel-muted { - border-color: rgb(30 60 140 / 0.40); - background-image: linear-gradient(160deg, #0c1830 0%, #10203e 100%); + border-color: rgb(30 64 175 / 0.40); + background-image: linear-gradient(160deg, #172554 0%, #1b3068 100%); } .site-panel-contrast { @apply rounded-[2rem] border p-7 text-white shadow-[0_30px_80px_rgba(28,25,23,0.18)] transition-colors sm:p-8; - border-color: rgb(14 40 90 / 0.8); - background-color: rgb(10 25 75); + border-color: rgb(20 45 95 / 0.8); + background-color: #172554; } html.dark .site-panel-contrast { @@ -104,5 +104,5 @@ html.dark .site-panel-contrast { } .site-link-secondary { - @apply rounded-full border border-stone-300 bg-white/85 px-6 py-3 text-sm font-semibold text-stone-700 transition hover:border-blue-900 hover:text-blue-900 dark:border-blue-800 dark:bg-blue-950/30 dark:text-blue-100 dark:hover:border-blue-400 dark:hover:text-white; + @apply rounded-full border border-stone-300 bg-white/85 px-6 py-3 text-sm font-semibold text-stone-700 transition hover:border-blue-900 hover:text-blue-900 dark:border-[#2e50f1] dark:bg-[#2e50f1]/30 dark:text-[#1e3de0] dark:hover:border-[#1e3de0] dark:hover:text-white; } diff --git a/apps/storefront/src/app/layout.tsx b/apps/storefront/src/app/layout.tsx index b138745..8328375 100644 --- a/apps/storefront/src/app/layout.tsx +++ b/apps/storefront/src/app/layout.tsx @@ -8,9 +8,9 @@ export const metadata: Metadata = { title: 'RentalDriveGo Marketplace', description: 'Discover vehicles from trusted rental companies.', icons: { - icon: '/rentalcardrive.png', + icon: '/rentaldrivego.png', shortcut: '/favicon.ico', - apple: '/rentalcardrive.png', + apple: '/rentaldrivego.png', }, } diff --git a/apps/storefront/src/components/MarketplaceHeader.tsx b/apps/storefront/src/components/MarketplaceHeader.tsx index 907235e..1dfc4a7 100644 --- a/apps/storefront/src/components/MarketplaceHeader.tsx +++ b/apps/storefront/src/components/MarketplaceHeader.tsx @@ -106,11 +106,12 @@ export default function MarketplaceHeader({
    RentalDriveGo RentalDriveGo diff --git a/dashboard/public/rentaldrivego.jpeg b/dashboard/public/rentaldrivego.jpeg new file mode 100644 index 0000000..4547894 Binary files /dev/null and b/dashboard/public/rentaldrivego.jpeg differ diff --git a/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx b/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx index f86eb8b..41b9117 100644 --- a/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx +++ b/dashboard/src/app/sign-up/[[...sign-up]]/SignUpForm.tsx @@ -110,11 +110,12 @@ export default function SignUpForm() {
    RentalDriveGo
    @@ -146,11 +147,12 @@ export default function SignUpForm() {
    RentalDriveGo diff --git a/dashboard/src/app/verify-email/page.tsx b/dashboard/src/app/verify-email/page.tsx index dce09d1..e7d0b08 100644 --- a/dashboard/src/app/verify-email/page.tsx +++ b/dashboard/src/app/verify-email/page.tsx @@ -75,11 +75,12 @@ export default function VerifyEmailPage() {
    RentalDriveGo
    diff --git a/dashboard/src/components/public/PublicNavbar.tsx b/dashboard/src/components/public/PublicNavbar.tsx index 85bb083..d948dad 100644 --- a/dashboard/src/components/public/PublicNavbar.tsx +++ b/dashboard/src/components/public/PublicNavbar.tsx @@ -7,7 +7,7 @@ import { useEffect, useRef, useState } from 'react' import { useDashboardI18n } from '@/components/I18nProvider' import { marketplaceUrl } from '@/lib/urls' -const DASHBOARD_LOGO_SRC = '/dashboard/rentalcardrive.png' +const DASHBOARD_LOGO_SRC = '/dashboard/rentaldrivego.png' const languageMeta = { en: { flag: '🇺🇸', shortLabel: 'EN' }, @@ -126,6 +126,7 @@ export default function PublicNavbar() { width={36} height={36} priority + unoptimized className="h-8 w-8 rounded-md object-contain sm:h-9 sm:w-9" /> RentalDriveGo diff --git a/homepage/public/benefit1_AR.png b/homepage/public/benefit1_AR.png new file mode 100644 index 0000000..bc7f5b0 Binary files /dev/null and b/homepage/public/benefit1_AR.png differ diff --git a/homepage/public/benefit1_EN.png b/homepage/public/benefit1_EN.png new file mode 100644 index 0000000..5cd3d5d Binary files /dev/null and b/homepage/public/benefit1_EN.png differ diff --git a/homepage/public/benefit1_FR.png b/homepage/public/benefit1_FR.png new file mode 100644 index 0000000..0288fa7 Binary files /dev/null and b/homepage/public/benefit1_FR.png differ diff --git a/homepage/public/dashboard1_AR.png b/homepage/public/dashboard1_AR.png new file mode 100644 index 0000000..5b1fd4c Binary files /dev/null and b/homepage/public/dashboard1_AR.png differ diff --git a/homepage/public/dashboard1_EN.png b/homepage/public/dashboard1_EN.png new file mode 100644 index 0000000..d48c2c6 Binary files /dev/null and b/homepage/public/dashboard1_EN.png differ diff --git a/homepage/public/dashboard1_FR.png b/homepage/public/dashboard1_FR.png new file mode 100644 index 0000000..c6f0e28 Binary files /dev/null and b/homepage/public/dashboard1_FR.png differ diff --git a/homepage/public/dashboard2_AR.png b/homepage/public/dashboard2_AR.png new file mode 100644 index 0000000..7ce0783 Binary files /dev/null and b/homepage/public/dashboard2_AR.png differ diff --git a/homepage/public/dashboard2_EN.png b/homepage/public/dashboard2_EN.png new file mode 100644 index 0000000..2ece4ea Binary files /dev/null and b/homepage/public/dashboard2_EN.png differ diff --git a/homepage/public/dashboard2_FR.png b/homepage/public/dashboard2_FR.png new file mode 100644 index 0000000..1dac45f Binary files /dev/null and b/homepage/public/dashboard2_FR.png differ diff --git a/homepage/public/dashboard3_EN.png b/homepage/public/dashboard3_EN.png new file mode 100644 index 0000000..f24b917 Binary files /dev/null and b/homepage/public/dashboard3_EN.png differ diff --git a/homepage/public/dashboard4_EN.png b/homepage/public/dashboard4_EN.png new file mode 100644 index 0000000..e78c73c Binary files /dev/null and b/homepage/public/dashboard4_EN.png differ diff --git a/homepage/public/dashboard5_AR.png b/homepage/public/dashboard5_AR.png new file mode 100644 index 0000000..3fb688e Binary files /dev/null and b/homepage/public/dashboard5_AR.png differ diff --git a/homepage/public/dashboard5_EN.png b/homepage/public/dashboard5_EN.png new file mode 100644 index 0000000..76bc626 Binary files /dev/null and b/homepage/public/dashboard5_EN.png differ diff --git a/homepage/public/dashboard5_FR.png b/homepage/public/dashboard5_FR.png new file mode 100644 index 0000000..178eaad Binary files /dev/null and b/homepage/public/dashboard5_FR.png differ diff --git a/homepage/public/oilchange1_AR.png b/homepage/public/oilchange1_AR.png new file mode 100644 index 0000000..c8020b5 Binary files /dev/null and b/homepage/public/oilchange1_AR.png differ diff --git a/homepage/public/oilchange1_EN.png b/homepage/public/oilchange1_EN.png new file mode 100644 index 0000000..92d4bd3 Binary files /dev/null and b/homepage/public/oilchange1_EN.png differ diff --git a/homepage/public/oilchange1_FR.png b/homepage/public/oilchange1_FR.png new file mode 100644 index 0000000..47d3d72 Binary files /dev/null and b/homepage/public/oilchange1_FR.png differ diff --git a/homepage/public/rentaldrivego.jpeg b/homepage/public/rentaldrivego.jpeg deleted file mode 100644 index 3ba550b..0000000 Binary files a/homepage/public/rentaldrivego.jpeg and /dev/null differ diff --git a/homepage/public/rentaldrivego.png b/homepage/public/rentaldrivego.png new file mode 100644 index 0000000..24f50ea Binary files /dev/null and b/homepage/public/rentaldrivego.png differ diff --git a/homepage/public/workflow1_AR.png b/homepage/public/workflow1_AR.png new file mode 100644 index 0000000..28a9b66 Binary files /dev/null and b/homepage/public/workflow1_AR.png differ diff --git a/homepage/public/workflow1_EN.png b/homepage/public/workflow1_EN.png new file mode 100644 index 0000000..dc0ee4a Binary files /dev/null and b/homepage/public/workflow1_EN.png differ diff --git a/homepage/public/workflow1_FR.png b/homepage/public/workflow1_FR.png new file mode 100644 index 0000000..328b9ff Binary files /dev/null and b/homepage/public/workflow1_FR.png differ diff --git a/homepage/src/app/[locale]/layout.tsx b/homepage/src/app/[locale]/layout.tsx index c42b913..d094193 100644 --- a/homepage/src/app/[locale]/layout.tsx +++ b/homepage/src/app/[locale]/layout.tsx @@ -27,8 +27,8 @@ export async function generateMetadata({ params }: { params: Promise<{ locale: s const { locale } = await params; return { icons: { - icon: '/rentaldrivego.jpeg', - apple: '/rentaldrivego.jpeg', + icon: '/rentaldrivego.png', + apple: '/rentaldrivego.png', }, }; } diff --git a/homepage/src/components/app-shell/BrandLink.tsx b/homepage/src/components/app-shell/BrandLink.tsx index ceebd68..9dae94f 100644 --- a/homepage/src/components/app-shell/BrandLink.tsx +++ b/homepage/src/components/app-shell/BrandLink.tsx @@ -15,12 +15,13 @@ export function BrandLink({ locale, label }: { locale: Locale; label: string }) aria-current={routeIdFromPathname(pathname) === 'home' ? 'page' : undefined} > RentalDriveGo diff --git a/homepage/src/components/auth/AuthShell.tsx b/homepage/src/components/auth/AuthShell.tsx index c46f969..328d01e 100644 --- a/homepage/src/components/auth/AuthShell.tsx +++ b/homepage/src/components/auth/AuthShell.tsx @@ -29,11 +29,12 @@ export function AuthShell({ locale, title, subtitle, children }: AuthShellProps)