feat(web): polish commission flow and default public navigation
This commit is contained in:
@@ -69,6 +69,13 @@ export default async function PublicCommissionRequestPage({
|
||||
async function submitCommissionRequestAction(formData: FormData) {
|
||||
"use server"
|
||||
|
||||
const budgetMin = readNullableNumber(formData, "budgetMin")
|
||||
const budgetMax = readNullableNumber(formData, "budgetMax")
|
||||
|
||||
if (budgetMin != null && budgetMax != null && budgetMax < budgetMin) {
|
||||
redirect(buildRedirect(locale, { error: "budget_range_invalid" }))
|
||||
}
|
||||
|
||||
try {
|
||||
await createPublicCommissionRequest({
|
||||
customerName: readInputString(formData, "customerName"),
|
||||
@@ -77,8 +84,8 @@ export default async function PublicCommissionRequestPage({
|
||||
customerInstagram: readNullableString(formData, "customerInstagram"),
|
||||
title: readInputString(formData, "title"),
|
||||
description: readNullableString(formData, "description"),
|
||||
budgetMin: readNullableNumber(formData, "budgetMin"),
|
||||
budgetMax: readNullableNumber(formData, "budgetMax"),
|
||||
budgetMin,
|
||||
budgetMax,
|
||||
})
|
||||
} catch {
|
||||
redirect(buildRedirect(locale, { error: "submission_failed" }))
|
||||
@@ -110,6 +117,11 @@ export default async function PublicCommissionRequestPage({
|
||||
{t("error")}
|
||||
</section>
|
||||
) : null}
|
||||
{error === "budget_range_invalid" ? (
|
||||
<section className="rounded-xl border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-800">
|
||||
{t("budgetRangeError")}
|
||||
</section>
|
||||
) : null}
|
||||
|
||||
<form action={submitCommissionRequestAction} className="space-y-4 rounded-xl border p-6">
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
@@ -117,6 +129,7 @@ export default async function PublicCommissionRequestPage({
|
||||
<span className="text-xs text-neutral-600">{t("fields.customerName")}</span>
|
||||
<input
|
||||
name="customerName"
|
||||
autoComplete="name"
|
||||
required
|
||||
className="w-full rounded border border-neutral-300 px-3 py-2 text-sm"
|
||||
/>
|
||||
@@ -126,6 +139,7 @@ export default async function PublicCommissionRequestPage({
|
||||
<input
|
||||
name="customerEmail"
|
||||
type="email"
|
||||
autoComplete="email"
|
||||
required
|
||||
className="w-full rounded border border-neutral-300 px-3 py-2 text-sm"
|
||||
/>
|
||||
@@ -137,6 +151,7 @@ export default async function PublicCommissionRequestPage({
|
||||
<span className="text-xs text-neutral-600">{t("fields.customerPhone")}</span>
|
||||
<input
|
||||
name="customerPhone"
|
||||
autoComplete="tel"
|
||||
className="w-full rounded border border-neutral-300 px-3 py-2 text-sm"
|
||||
/>
|
||||
</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { listPublicNavigation } from "@cms/db"
|
||||
import { getLocale } from "next-intl/server"
|
||||
import { getLocale, getTranslations } from "next-intl/server"
|
||||
|
||||
import { Link } from "@/i18n/navigation"
|
||||
|
||||
@@ -7,7 +7,33 @@ import { LanguageSwitcher } from "./language-switcher"
|
||||
|
||||
export async function PublicSiteHeader() {
|
||||
const locale = await getLocale()
|
||||
const navItems = await listPublicNavigation("header", locale)
|
||||
const [navItems, t] = await Promise.all([
|
||||
listPublicNavigation("header", locale),
|
||||
getTranslations("Layout.nav"),
|
||||
])
|
||||
const fallbackNavItems = [
|
||||
{
|
||||
id: "fallback-home",
|
||||
href: "/",
|
||||
label: t("home"),
|
||||
},
|
||||
{
|
||||
id: "fallback-portfolio",
|
||||
href: "/portfolio",
|
||||
label: t("portfolio"),
|
||||
},
|
||||
{
|
||||
id: "fallback-news",
|
||||
href: "/news",
|
||||
label: t("news"),
|
||||
},
|
||||
{
|
||||
id: "fallback-commissions",
|
||||
href: "/commissions",
|
||||
label: t("commissions"),
|
||||
},
|
||||
]
|
||||
const resolvedNavItems = navItems.length > 0 ? navItems : fallbackNavItems
|
||||
|
||||
return (
|
||||
<header className="border-b border-neutral-200 bg-white/80 backdrop-blur">
|
||||
@@ -20,24 +46,15 @@ export async function PublicSiteHeader() {
|
||||
</Link>
|
||||
|
||||
<nav className="flex flex-wrap items-center gap-2">
|
||||
{navItems.length === 0 ? (
|
||||
{resolvedNavItems.map((item) => (
|
||||
<Link
|
||||
href="/"
|
||||
key={item.id}
|
||||
href={item.href}
|
||||
className="rounded-md border border-neutral-300 px-3 py-1.5 text-sm font-medium text-neutral-700 hover:bg-neutral-100"
|
||||
>
|
||||
Home
|
||||
{item.label}
|
||||
</Link>
|
||||
) : (
|
||||
navItems.map((item) => (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={item.href}
|
||||
className="rounded-md border border-neutral-300 px-3 py-1.5 text-sm font-medium text-neutral-700 hover:bg-neutral-100"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))
|
||||
)}
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<LanguageSwitcher />
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
"brand": "CMS Web",
|
||||
"nav": {
|
||||
"home": "Start",
|
||||
"portfolio": "Portfolio",
|
||||
"news": "News",
|
||||
"commissions": "Aufträge",
|
||||
"about": "Über uns",
|
||||
"contact": "Kontakt"
|
||||
},
|
||||
@@ -49,6 +52,7 @@
|
||||
"description": "Teile deine Idee und Projektdetails. Wir prüfen die Anfrage und melden uns zeitnah.",
|
||||
"success": "Deine Auftragsanfrage wurde übermittelt.",
|
||||
"error": "Übermittlung fehlgeschlagen. Bitte prüfe die Eingaben und versuche es erneut.",
|
||||
"budgetRangeError": "Das maximale Budget muss größer oder gleich dem minimalen Budget sein.",
|
||||
"submit": "Anfrage senden",
|
||||
"fields": {
|
||||
"customerName": "Name",
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
"brand": "CMS Web",
|
||||
"nav": {
|
||||
"home": "Home",
|
||||
"portfolio": "Portfolio",
|
||||
"news": "News",
|
||||
"commissions": "Commissions",
|
||||
"about": "About",
|
||||
"contact": "Contact"
|
||||
},
|
||||
@@ -49,6 +52,7 @@
|
||||
"description": "Share your idea and project details. We will review and reply as soon as possible.",
|
||||
"success": "Your commission request was submitted.",
|
||||
"error": "Submission failed. Please review your data and try again.",
|
||||
"budgetRangeError": "Budget max must be greater than or equal to budget min.",
|
||||
"submit": "Submit request",
|
||||
"fields": {
|
||||
"customerName": "Name",
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
"brand": "CMS Web",
|
||||
"nav": {
|
||||
"home": "Inicio",
|
||||
"portfolio": "Portafolio",
|
||||
"news": "Noticias",
|
||||
"commissions": "Comisiones",
|
||||
"about": "Acerca de",
|
||||
"contact": "Contacto"
|
||||
},
|
||||
@@ -49,6 +52,7 @@
|
||||
"description": "Comparte tu idea y detalles del proyecto. Revisaremos la solicitud y responderemos pronto.",
|
||||
"success": "Tu solicitud de comisión fue enviada.",
|
||||
"error": "No se pudo enviar la solicitud. Revisa los datos e inténtalo de nuevo.",
|
||||
"budgetRangeError": "El presupuesto máximo debe ser mayor o igual al mínimo.",
|
||||
"submit": "Enviar solicitud",
|
||||
"fields": {
|
||||
"customerName": "Nombre",
|
||||
|
||||
@@ -21,6 +21,9 @@
|
||||
"brand": "CMS Web",
|
||||
"nav": {
|
||||
"home": "Accueil",
|
||||
"portfolio": "Portfolio",
|
||||
"news": "Actualités",
|
||||
"commissions": "Commissions",
|
||||
"about": "À propos",
|
||||
"contact": "Contact"
|
||||
},
|
||||
@@ -49,6 +52,7 @@
|
||||
"description": "Partagez votre idée et les détails du projet. Nous examinerons la demande et répondrons rapidement.",
|
||||
"success": "Votre demande de commission a été envoyée.",
|
||||
"error": "Échec de l'envoi. Vérifiez les données et réessayez.",
|
||||
"budgetRangeError": "Le budget max doit être supérieur ou égal au budget min.",
|
||||
"submit": "Envoyer la demande",
|
||||
"fields": {
|
||||
"customerName": "Nom",
|
||||
|
||||
Reference in New Issue
Block a user