fix: code review corrections - config memoization, Docker cleanup, a11y, lint
This commit is contained in:
parent
528e8bcbbb
commit
59d49d0299
|
|
@ -1,8 +1,3 @@
|
|||
FROM node:20-alpine AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --only=production
|
||||
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"dev": "node node_modules/next/dist/bin/next dev",
|
||||
"build": "node node_modules/next/dist/bin/next build",
|
||||
"start": "node node_modules/next/dist/bin/next start",
|
||||
"lint": "node node_modules/eslint/bin/eslint.js"
|
||||
"lint": "node node_modules/eslint/bin/eslint.js ."
|
||||
},
|
||||
"dependencies": {
|
||||
"lucide-react": "^1.26.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import type { Metadata } from "next"
|
||||
import { Inter } from "next/font/google"
|
||||
import { getConfig } from "@/lib/config"
|
||||
import "./globals.css"
|
||||
|
||||
const inter = Inter({
|
||||
|
|
@ -8,17 +9,19 @@ const inter = Inter({
|
|||
variable: "--font-inter",
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Soch Family",
|
||||
description: "Les applications de la famille",
|
||||
}
|
||||
|
||||
function getInitialTheme(): string {
|
||||
export async function generateMetadata(): Promise<Metadata> {
|
||||
try {
|
||||
const { loadConfig } = require("@/lib/config")
|
||||
return loadConfig().theme.mode
|
||||
const config = getConfig()
|
||||
return {
|
||||
title: config.site.name,
|
||||
description: config.site.title,
|
||||
icons: config.site.favicon ? { icon: config.site.favicon } : undefined,
|
||||
}
|
||||
} catch {
|
||||
return "dark"
|
||||
return {
|
||||
title: "Sochboard",
|
||||
description: "Dashboard",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -27,7 +30,10 @@ export default function RootLayout({
|
|||
}: Readonly<{
|
||||
children: React.ReactNode
|
||||
}>) {
|
||||
const theme = getInitialTheme()
|
||||
let theme = "dark"
|
||||
try {
|
||||
theme = getConfig().theme.mode
|
||||
} catch {}
|
||||
|
||||
return (
|
||||
<html
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import { loadConfig } from "@/lib/config"
|
||||
import { getConfig } from "@/lib/config"
|
||||
import { Dashboard } from "@/components/dashboard/Dashboard"
|
||||
import { ErrorDisplay } from "@/components/ui/ErrorDisplay"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
function getConfig() {
|
||||
function getConfigResult() {
|
||||
try {
|
||||
return { ok: true as const, config: loadConfig() }
|
||||
return { ok: true as const, config: getConfig() }
|
||||
} catch (error) {
|
||||
return {
|
||||
ok: false as const,
|
||||
|
|
@ -19,7 +19,7 @@ function getConfig() {
|
|||
}
|
||||
|
||||
export default function Home() {
|
||||
const result = getConfig()
|
||||
const result = getConfigResult()
|
||||
|
||||
if (!result.ok) {
|
||||
return <ErrorDisplay message={result.message} />
|
||||
|
|
|
|||
|
|
@ -15,13 +15,6 @@ export function DashboardHeader({ site, initialTheme }: DashboardHeaderProps) {
|
|||
document.documentElement.classList.toggle("theme-light", isLight)
|
||||
}, [isLight])
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle(
|
||||
"theme-light",
|
||||
initialTheme === "light"
|
||||
)
|
||||
}, [initialTheme])
|
||||
|
||||
function toggleTheme() {
|
||||
setIsLight((prev) => !prev)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
|||
)}
|
||||
</div>
|
||||
{link.openInNewTab && (
|
||||
<>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
|
|
@ -58,12 +59,14 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
|||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="shrink-0 text-[var(--text-tertiary)] transition-colors group-hover:text-[var(--text-secondary)]"
|
||||
aria-label="Ouvre dans un nouvel onglet"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M15 3h6v6" />
|
||||
<path d="M10 14 21 3" />
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
||||
</svg>
|
||||
<span className="sr-only">Nouvel onglet</span>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
|
|
@ -83,6 +86,7 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
|||
)}
|
||||
</div>
|
||||
{link.openInNewTab && (
|
||||
<>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="14"
|
||||
|
|
@ -94,12 +98,14 @@ export function ServiceCard({ link, index = 0, layout = "horizontal" }: ServiceC
|
|||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="mt-1 text-[var(--text-tertiary)] transition-colors group-hover:text-[var(--text-secondary)]"
|
||||
aria-label="Ouvre dans un nouvel onglet"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M15 3h6v6" />
|
||||
<path d="M10 14 21 3" />
|
||||
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
|
||||
</svg>
|
||||
<span className="sr-only">Nouvel onglet</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { cache } from "react"
|
||||
import { readYamlFile } from "./yaml"
|
||||
import { dashboardConfigSchema } from "./validation"
|
||||
import type { DashboardConfig } from "@/types/config"
|
||||
|
|
@ -21,3 +22,5 @@ export function loadConfig(configPath?: string): DashboardConfig {
|
|||
|
||||
return result.data
|
||||
}
|
||||
|
||||
export const getConfig = cache((configPath?: string) => loadConfig(configPath))
|
||||
|
|
|
|||
Loading…
Reference in a new issue