feat(themes): add La Maison d'Élève French luxury editorial restaurant design system
This commit is contained in:
@@ -156,6 +156,7 @@ export const restaurantsRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const THEME_NAMES: Record<string, string> = {
|
const THEME_NAMES: Record<string, string> = {
|
||||||
|
maison: "La Maison d'Élève",
|
||||||
swiss: "Swiss Brutalist",
|
swiss: "Swiss Brutalist",
|
||||||
artisan: "Amber Artisan",
|
artisan: "Amber Artisan",
|
||||||
elegant: "Elegant Gold",
|
elegant: "Elegant Gold",
|
||||||
|
|||||||
@@ -31,6 +31,18 @@ export interface ThemeOption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const THEME_OPTIONS: ThemeOption[] = [
|
export const THEME_OPTIONS: ThemeOption[] = [
|
||||||
|
{
|
||||||
|
key: "maison",
|
||||||
|
name: "La Maison d'Élève",
|
||||||
|
category: "Fransız Haute Cuisine & Lüks",
|
||||||
|
color: "#24463A",
|
||||||
|
badgeBg: "#E3DDD4",
|
||||||
|
badgeText: "#24463A",
|
||||||
|
bgPreview: "#EEEAE4",
|
||||||
|
accent: "Orman Yeşili & Fransız Krem",
|
||||||
|
desc: "Zarif serif tipografi, Fransız editoryal mizanpaj, pin çizgileri ve | ₺180 | özel fiyatlandırma stili.",
|
||||||
|
suitableFor: "Lüks otel restoranları, Fransız mutfağı, degustasyon menüleri, şarap salonları",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "swiss",
|
key: "swiss",
|
||||||
name: "Swiss Brutalist",
|
name: "Swiss Brutalist",
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ export function PublicMenuClient({
|
|||||||
<div className="bg-stone-900 text-white px-5 py-4 border-b border-stone-800 animate-slide-up">
|
<div className="bg-stone-900 text-white px-5 py-4 border-b border-stone-800 animate-slide-up">
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<p className="text-xs font-bold uppercase tracking-wider text-stone-400">
|
<p className="text-xs font-bold uppercase tracking-wider text-stone-400">
|
||||||
Canlı Tema Önizleme (7 Şablon)
|
Canlı Tema Önizleme (8 Şablon)
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -325,7 +325,7 @@ export function PublicMenuClient({
|
|||||||
Kapat ✕
|
Kapat ✕
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-7 gap-2">
|
<div className="grid grid-cols-4 sm:grid-cols-4 md:grid-cols-8 gap-2">
|
||||||
{Object.values(THEME_PRESETS).map((preset) => {
|
{Object.values(THEME_PRESETS).map((preset) => {
|
||||||
const isSelected = selectedThemeKey === preset.key;
|
const isSelected = selectedThemeKey === preset.key;
|
||||||
return (
|
return (
|
||||||
@@ -425,12 +425,13 @@ export function PublicMenuClient({
|
|||||||
filteredCategories.map((category, catIndex) => {
|
filteredCategories.map((category, catIndex) => {
|
||||||
const chapterNum = String(catIndex + 1).padStart(2, "0");
|
const chapterNum = String(catIndex + 1).padStart(2, "0");
|
||||||
const isSwiss = theme.key === "swiss";
|
const isSwiss = theme.key === "swiss";
|
||||||
|
const isMaison = theme.key === "maison";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
key={category.id}
|
key={category.id}
|
||||||
id={`category-${category.id}`}
|
id={`category-${category.id}`}
|
||||||
className={isSwiss ? "scroll-mt-28 space-y-0" : "scroll-mt-28 space-y-3.5"}
|
className={isSwiss ? "scroll-mt-28 space-y-0" : isMaison ? "scroll-mt-28 space-y-4" : "scroll-mt-28 space-y-3.5"}
|
||||||
>
|
>
|
||||||
{/* Category Heading Banner */}
|
{/* Category Heading Banner */}
|
||||||
{isSwiss ? (
|
{isSwiss ? (
|
||||||
@@ -452,6 +453,25 @@ export function PublicMenuClient({
|
|||||||
Ø{catIndex + 1}
|
Ø{catIndex + 1}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
) : isMaison ? (
|
||||||
|
<div className="sticky top-[58px] z-20 bg-[#EEEAE4]/95 backdrop-blur-xl border-b border-[#24463A]/20 py-3.5 px-3 flex items-baseline justify-between transition-all">
|
||||||
|
<div className="flex items-baseline gap-3.5">
|
||||||
|
<span className="text-3xl sm:text-4xl font-serif font-light text-[#24463A]">
|
||||||
|
{chapterNum}
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<h2 className="text-xl sm:text-2xl font-serif font-bold uppercase tracking-tight text-[#1A1A1A]">
|
||||||
|
{category.name}
|
||||||
|
</h2>
|
||||||
|
<p className="text-[10px] tracking-widest uppercase text-stone-500 font-sans mt-0.5">
|
||||||
|
{category.description || "Sélection Culinaire"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="w-20 sm:w-32 h-[1px] bg-[#24463A]/40 relative self-center">
|
||||||
|
<div className="absolute right-0 -top-[2px] w-1.5 h-1.5 bg-[#24463A] rounded-full"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
className="sticky top-[58px] z-20 flex items-center justify-between py-2.5 px-3 -mx-3 rounded-2xl backdrop-blur-xl border-b transition-all shadow-sm"
|
className="sticky top-[58px] z-20 flex items-center justify-between py-2.5 px-3 -mx-3 rounded-2xl backdrop-blur-xl border-b transition-all shadow-sm"
|
||||||
@@ -487,6 +507,8 @@ export function PublicMenuClient({
|
|||||||
className={
|
className={
|
||||||
isSwiss
|
isSwiss
|
||||||
? "divide-y-2 divide-black border-b-2 border-black"
|
? "divide-y-2 divide-black border-b-2 border-black"
|
||||||
|
: isMaison
|
||||||
|
? "grid grid-cols-1 gap-4"
|
||||||
: theme.key === "artisan"
|
: theme.key === "artisan"
|
||||||
? "grid grid-cols-2 gap-3 sm:gap-4"
|
? "grid grid-cols-2 gap-3 sm:gap-4"
|
||||||
: theme.config.productLayout === "list"
|
: theme.config.productLayout === "list"
|
||||||
@@ -495,6 +517,60 @@ export function PublicMenuClient({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{category.menu_items.map((item, itemIndex) => {
|
{category.menu_items.map((item, itemIndex) => {
|
||||||
|
if (isMaison) {
|
||||||
|
return (
|
||||||
|
<article
|
||||||
|
key={item.id}
|
||||||
|
onClick={() => setSelectedItem(item)}
|
||||||
|
className="group bg-[#F7F4EF] rounded-2xl p-4 sm:p-5 border border-[#24463A]/15 hover:border-[#24463A]/40 transition-all duration-300 cursor-pointer shadow-sm flex flex-col sm:flex-row gap-4 items-center justify-between"
|
||||||
|
>
|
||||||
|
{/* Image with subtle clay circle and forest green accent */}
|
||||||
|
{item.image_url && (
|
||||||
|
<div className="relative w-full sm:w-32 aspect-video sm:aspect-square flex-shrink-0">
|
||||||
|
<div className="absolute -top-2 -left-2 w-14 h-14 bg-[#C9C1B5]/40 rounded-full z-0 pointer-events-none"></div>
|
||||||
|
<div className="relative z-10 w-full h-full rounded-xl overflow-hidden shadow-sm">
|
||||||
|
<img
|
||||||
|
src={item.image_url}
|
||||||
|
alt={item.name}
|
||||||
|
className="w-full h-full object-cover grayscale-[0.2] group-hover:grayscale-0 group-hover:scale-105 transition-all duration-500"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<div className="absolute -bottom-1 -right-1 w-3.5 h-3.5 bg-[#24463A]"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Details */}
|
||||||
|
<div className="flex-1 min-w-0 w-full">
|
||||||
|
<div className="flex justify-between items-start gap-3">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-lg sm:text-xl font-serif font-bold uppercase tracking-tight text-[#1A1A1A] group-hover:text-[#24463A] transition-colors leading-tight">
|
||||||
|
{item.name}
|
||||||
|
</h3>
|
||||||
|
{item.description && (
|
||||||
|
<p className="text-xs sm:text-sm font-serif text-[#5C5852] mt-1 leading-relaxed line-clamp-2">
|
||||||
|
{item.description}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="text-right flex-shrink-0">
|
||||||
|
<span className="text-[9px] uppercase tracking-wider text-stone-500 font-serif italic block">
|
||||||
|
Maison
|
||||||
|
</span>
|
||||||
|
<div className="w-5 h-[1px] bg-[#24463A]/40 ml-auto mt-1"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-3 flex justify-end">
|
||||||
|
<span className="text-base sm:text-lg font-light font-sans border-x border-[#1A1A1A]/20 px-3 py-0.5 text-[#1A1A1A]">
|
||||||
|
| ₺{item.price.toFixed(0)} |
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
);
|
||||||
|
}
|
||||||
if (isSwiss) {
|
if (isSwiss) {
|
||||||
const itemCode = `${String.fromCharCode(65 + catIndex)}${String(itemIndex + 1).padStart(2, "0")}`;
|
const itemCode = `${String.fromCharCode(65 + catIndex)}${String(itemIndex + 1).padStart(2, "0")}`;
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -16,6 +16,25 @@ export interface ThemePreset {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const THEME_PRESETS: Record<string, ThemePreset> = {
|
export const THEME_PRESETS: Record<string, ThemePreset> = {
|
||||||
|
maison: {
|
||||||
|
key: "maison",
|
||||||
|
name: "La Maison d'Élève",
|
||||||
|
fontFamily: "serif",
|
||||||
|
config: {
|
||||||
|
primaryColor: "#24463A",
|
||||||
|
background: "#EEEAE4",
|
||||||
|
productLayout: "card",
|
||||||
|
categoryLayout: "tabs",
|
||||||
|
},
|
||||||
|
cardBg: "#F7F4EF",
|
||||||
|
cardBorder: "rgba(36, 70, 58, 0.2)",
|
||||||
|
textPrimary: "#1A1A1A",
|
||||||
|
textSecondary: "#5C5852",
|
||||||
|
accentBg: "rgba(36, 70, 58, 0.08)",
|
||||||
|
headerGradient: "linear-gradient(180deg, #182C24 0%, #11201A 100%)",
|
||||||
|
badgeBg: "#E3DDD4",
|
||||||
|
badgeText: "#24463A",
|
||||||
|
},
|
||||||
swiss: {
|
swiss: {
|
||||||
key: "swiss",
|
key: "swiss",
|
||||||
name: "Swiss Brutalist",
|
name: "Swiss Brutalist",
|
||||||
|
|||||||
Reference in New Issue
Block a user