fix bug 16 and 17
Build & Push / Pipeline Tests (push) Successful in 1m53s
Test / Type Check (all packages) (push) Successful in 56s
Build & Push / Build & Push Docker Image (push) Failing after 4m53s
Test / API Unit Tests (push) Successful in 1m15s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 41s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Successful in 42s
Test / API Integration Tests (push) Successful in 1m6s
Build & Push / Pipeline Tests (push) Successful in 1m53s
Test / Type Check (all packages) (push) Successful in 56s
Build & Push / Build & Push Docker Image (push) Failing after 4m53s
Test / API Unit Tests (push) Successful in 1m15s
Test / Homepage Unit Tests (push) Successful in 46s
Test / Carplace Unit Tests (push) Successful in 41s
Test / Admin Unit Tests (push) Successful in 40s
Test / Dashboard Unit Tests (push) Successful in 42s
Test / API Integration Tests (push) Successful in 1m6s
This commit is contained in:
@@ -14,10 +14,12 @@ import { AdminSessionProvider, type AdminSessionUser } from './AdminSessionConte
|
||||
|
||||
function buildUnifiedLoginUrl(nextPath: string) {
|
||||
const websiteUrl = resolveBrowserAppUrl(process.env.NEXT_PUBLIC_WEBSITE_URL ?? 'http://localhost:3000')
|
||||
const storedTheme = window.localStorage.getItem('rentaldrivego-theme') ?? window.localStorage.getItem('admin-theme')
|
||||
const theme = storedTheme === 'light' ? 'light' : 'dark'
|
||||
const params = new URLSearchParams({
|
||||
next: `/admin${nextPath || '/dashboard'}`,
|
||||
})
|
||||
return `${websiteUrl}/en/light/admin-sign-in?${params.toString()}`
|
||||
return `${websiteUrl}/en/${theme}/admin-sign-in?${params.toString()}`
|
||||
}
|
||||
|
||||
const navLinks = [
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useEffect, useMemo, useState } from 'react'
|
||||
import { ADMIN_API_BASE } from '@/lib/api'
|
||||
|
||||
type EmployeeRole = 'OWNER' | 'MANAGER' | 'AGENT'
|
||||
type Plan = 'STARTER' | 'GROWTH' | 'PRO'
|
||||
type Plan = 'STARTER' | 'GROWTH' | 'PRO' | 'ENTERPRISE'
|
||||
type MenuItemType = 'INTERNAL_PAGE' | 'EXTERNAL_LINK' | 'PARENT_MENU' | 'SECTION_LABEL' | 'DIVIDER'
|
||||
|
||||
type CompanyOption = {
|
||||
@@ -82,7 +82,7 @@ type FormState = {
|
||||
}
|
||||
|
||||
const ROLES: EmployeeRole[] = ['OWNER', 'MANAGER', 'AGENT']
|
||||
const PLANS: Plan[] = ['STARTER', 'GROWTH', 'PRO']
|
||||
const PLANS: Plan[] = ['STARTER', 'GROWTH', 'PRO', 'ENTERPRISE']
|
||||
const ITEM_TYPES: MenuItemType[] = ['INTERNAL_PAGE', 'EXTERNAL_LINK', 'PARENT_MENU', 'SECTION_LABEL', 'DIVIDER']
|
||||
|
||||
const INPUT =
|
||||
@@ -117,7 +117,7 @@ function emptyForm(): FormState {
|
||||
isRequired: false,
|
||||
isActive: true,
|
||||
roles: ['OWNER', 'MANAGER', 'AGENT'],
|
||||
plans: ['STARTER', 'GROWTH', 'PRO'],
|
||||
plans: ['STARTER', 'GROWTH', 'PRO', 'ENTERPRISE'],
|
||||
companyIds: [],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,19 +64,21 @@ type PlanFeatureForm = {
|
||||
|
||||
// ─── Constants ─────────────────────────────────────────────────────────────
|
||||
|
||||
const PLANS = ['STARTER', 'GROWTH', 'PRO']
|
||||
const PLANS = ['STARTER', 'GROWTH', 'PRO', 'ENTERPRISE']
|
||||
const PERIODS = ['MONTHLY', 'ANNUAL']
|
||||
|
||||
const PLAN_COLORS: Record<string, string> = {
|
||||
STARTER: 'text-zinc-400',
|
||||
GROWTH: 'text-sky-400',
|
||||
PRO: 'text-violet-400',
|
||||
ENTERPRISE: 'text-emerald-400',
|
||||
}
|
||||
|
||||
const PLAN_BADGE: Record<string, string> = {
|
||||
STARTER: 'bg-zinc-800 text-zinc-300',
|
||||
GROWTH: 'bg-sky-900/40 text-sky-300',
|
||||
PRO: 'bg-violet-900/40 text-violet-300',
|
||||
ENTERPRISE: 'bg-emerald-900/40 text-emerald-300',
|
||||
}
|
||||
|
||||
// ─── Helpers ───────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -9,12 +9,12 @@ export const metadata: Metadata = {
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" className="light" suppressHydrationWarning>
|
||||
<html lang="en" className="dark" suppressHydrationWarning>
|
||||
<head>
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html:
|
||||
"(function(){try{var m=document.cookie.match(/(?:^|; )rentaldrivego-theme=([^;]+)/);var theme=m?decodeURIComponent(m[1]):(localStorage.getItem('rentaldrivego-theme')||localStorage.getItem('admin-theme'));if(theme!=='light'&&theme!=='dark'){theme=window.matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}document.documentElement.classList.remove('light','dark');document.documentElement.classList.add(theme);document.documentElement.style.colorScheme=theme;document.body&&document.body.setAttribute('data-theme',theme)}catch(e){}})();",
|
||||
"(function(){try{var m=document.cookie.match(/(?:^|; )rentaldrivego-theme=([^;]+)/);var theme=m?decodeURIComponent(m[1]):(localStorage.getItem('rentaldrivego-theme')||localStorage.getItem('admin-theme'));if(theme!=='light'&&theme!=='dark'){theme='dark'}document.documentElement.classList.remove('light','dark');document.documentElement.classList.add(theme);document.documentElement.style.colorScheme=theme;document.body&&document.body.setAttribute('data-theme',theme)}catch(e){}})();",
|
||||
}}
|
||||
/>
|
||||
</head>
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { headers } from 'next/headers'
|
||||
import { cookies, headers } from 'next/headers'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { resolveServerAppUrl } from '@/lib/appUrls'
|
||||
|
||||
export default async function AdminLoginPage() {
|
||||
const requestHeaders = await headers()
|
||||
const cookieStore = await cookies()
|
||||
const rawTheme = cookieStore.get('rentaldrivego-theme')?.value
|
||||
const theme = rawTheme === 'light' ? 'light' : 'dark'
|
||||
const websiteUrl = resolveServerAppUrl(
|
||||
process.env.NEXT_PUBLIC_WEBSITE_URL ?? 'http://localhost:3000',
|
||||
requestHeaders.get('host'),
|
||||
requestHeaders.get('x-forwarded-proto'),
|
||||
)
|
||||
redirect(`${websiteUrl}/en/light/admin-sign-in?next=/admin/dashboard`)
|
||||
redirect(`${websiteUrl}/en/${theme}/admin-sign-in?next=/admin/dashboard`)
|
||||
}
|
||||
|
||||
@@ -110,10 +110,11 @@ const Context = createContext<AdminI18nContext | null>(null)
|
||||
|
||||
export function AdminI18nProvider({ children }: { children: React.ReactNode }) {
|
||||
const [language, setLanguage] = useState<AdminLanguage>('en')
|
||||
const [theme, setTheme] = useState<AdminTheme>('light')
|
||||
const [theme, setTheme] = useState<AdminTheme>('dark')
|
||||
// Skip the very first write so we don't overwrite a stored preference with
|
||||
// the default 'en' value before the hydration read-effect has applied it.
|
||||
const skipFirstLangWrite = useRef(true)
|
||||
const skipFirstThemeWrite = useRef(true)
|
||||
|
||||
useEffect(() => {
|
||||
const stored = window.localStorage.getItem('admin-language')
|
||||
@@ -124,11 +125,6 @@ export function AdminI18nProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
if (storedTheme === 'light' || storedTheme === 'dark') {
|
||||
setTheme(storedTheme)
|
||||
return
|
||||
}
|
||||
|
||||
if (!window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||
setTheme('light')
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -147,6 +143,10 @@ export function AdminI18nProvider({ children }: { children: React.ReactNode }) {
|
||||
document.documentElement.classList.add(theme)
|
||||
document.documentElement.style.colorScheme = theme
|
||||
document.body.dataset.theme = theme
|
||||
if (skipFirstThemeWrite.current) {
|
||||
skipFirstThemeWrite.current = false
|
||||
return
|
||||
}
|
||||
document.cookie = `${SHARED_THEME_KEY}=${encodeURIComponent(theme)}; path=/; max-age=31536000; SameSite=Lax`
|
||||
window.localStorage.setItem(SHARED_THEME_KEY, theme)
|
||||
window.localStorage.setItem('admin-theme', theme)
|
||||
|
||||
Reference in New Issue
Block a user