Replace the landing with the artifact design and rebuild the admin in Ant Design.

Adds consultation leads, section editors, and the one-time content reset for this release.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-01 20:36:06 +03:30
co-authored by Cursor
parent 5147c661f0
commit b0088c35d1
130 changed files with 8024 additions and 2801 deletions
-14
View File
@@ -14,19 +14,5 @@ export const brand = {
export type Brand = typeof brand;
export const SECTION_KEYS = [
"hero",
"marquee",
"manifesto",
"services",
"work",
"process",
"stats",
"team",
"footer",
] as const;
export type SectionKey = (typeof SECTION_KEYS)[number];
export const PAGE_KEYS = ["home", "blog", "gallery"] as const;
export type PageKey = (typeof PAGE_KEYS)[number];
+1
View File
@@ -1,2 +1,3 @@
export * from "./brand";
export * from "./sections";
export * from "./timezone";
+447
View File
@@ -0,0 +1,447 @@
export interface LinkItem {
label: string;
href: string;
}
export interface HeaderContent {
brandTitle: string;
brandSubtitle: string;
/** Reading order (right to left). Desktop renders this list reversed, matching the original markup. */
nav: LinkItem[];
ctaLabel: string;
}
export interface HeroSuggestion {
label: string;
query: string;
}
export interface HeroMockupContent {
domain: string;
version: string;
salesValue: string;
salesGrowth: string;
customersValue: string;
satisfaction: string;
chartDefaultTooltip: string;
chartTooltips: [string, string, string];
goldBarCount: number;
}
export interface AiModalContent {
title: string;
queryLabel: string;
fallbackQuery: string;
packageLabel: string;
packageTitle: string;
matchBadge: string;
items: string[];
primaryCta: string;
closeLabel: string;
}
export interface HeroContent {
eyebrow: string;
titleLine1: string;
titleHighlight: string;
subtitle: string;
searchPlaceholder: string;
searchDefaultQuery: string;
suggestionsLabel: string;
suggestions: HeroSuggestion[];
mockup: HeroMockupContent;
ai: AiModalContent;
}
export const METRIC_ICONS = ["gear", "users", "box"] as const;
export type MetricIcon = (typeof METRIC_ICONS)[number];
export interface MetricItem {
icon: MetricIcon;
value: string;
label: string;
/** "text" renders the value as a bold title, "number" as a large figure. */
emphasis: "text" | "number";
}
export interface MetricsContent {
items: MetricItem[];
}
export interface FeaturesContent {
eyebrow: string;
titleLine1: string;
titleLine2: string;
body: string;
ctaLabel: string;
ctaHref: string;
baseCardLabel: string;
customCardTitle: string;
customCardBadge: string;
salesLabel: string;
salesValue: string;
linkNote: string;
aiLabel: string;
aiStatus: string;
aiNote: string;
}
export const SOLUTION_ACCENTS = ["brand", "sky", "indigo", "cyan", "amber"] as const;
export type SolutionAccent = (typeof SOLUTION_ACCENTS)[number];
export const SOLUTION_ICONS = ["cube", "bars", "avatars", "cart", "gold"] as const;
export type SolutionIcon = (typeof SOLUTION_ICONS)[number];
export interface SolutionItem {
title: string;
subtitle: string;
/** Business type preselected in the consultation form when the card is clicked. */
consultType: string;
accent: SolutionAccent;
icon: SolutionIcon;
}
export interface SolutionsContent {
eyebrow: string;
title: string;
subtitle: string;
items: SolutionItem[];
}
export interface TestimonialsContent {
eyebrow: string;
titleLine1: string;
titleLine2: string;
body: string;
ctaLabel: string;
badge: string;
quote: string;
name: string;
role: string;
avatarUrl: string;
decorAvatars: string[];
}
export interface CtaContent {
titleLine1: string;
titleLine2: string;
body: string;
ctaLabel: string;
}
export interface FooterContent {
contactTitle: string;
productsTitle: string;
products: LinkItem[];
quickLinksTitle: string;
quickLinks: LinkItem[];
brandTitle: string;
brandSubtitle: string;
descriptionLines: string[];
copyright: string;
legalLinks: LinkItem[];
/** Mirrored from site settings by the API; settings take precedence on the landing. */
phone?: string;
email?: string;
address?: string;
}
export interface ConsultTypeOption {
value: string;
label: string;
}
export interface ConsultContent {
title: string;
nameLabel: string;
namePlaceholder: string;
phoneLabel: string;
phonePlaceholder: string;
typeLabel: string;
types: ConsultTypeOption[];
successMessage: string;
errorMessage: string;
submitLabel: string;
}
export interface SiteSections {
header: HeaderContent;
hero: HeroContent;
metrics: MetricsContent;
features: FeaturesContent;
solutions: SolutionsContent;
testimonials: TestimonialsContent;
cta: CtaContent;
footer: FooterContent;
consult: ConsultContent;
}
export const SECTION_KEYS = [
"header",
"hero",
"metrics",
"features",
"solutions",
"testimonials",
"cta",
"footer",
"consult",
] as const;
export type SectionKey = (typeof SECTION_KEYS)[number];
export const SECTION_TITLES_FA: Record<SectionKey, string> = {
header: "هدر و منو",
hero: "هیرو (بخش اول)",
metrics: "نوار آمار",
features: "از نسخه پایه تا اختصاصی",
solutions: "راهکارها و محصولات",
testimonials: "نظرات مشتریان",
cta: "بنر مشاوره",
footer: "فوتر",
consult: "فرم مشاوره",
};
export const LEGACY_SECTION_KEYS = [
"marquee",
"manifesto",
"services",
"work",
"process",
"stats",
"team",
] as const;
const productLinks: LinkItem[] = [
{ label: "نرم‌افزار طلا و جواهر", href: "#solutions" },
{ label: "فروشگاه آنلاین", href: "#solutions" },
{ label: "مدیریت مشتریان (CRM)", href: "#solutions" },
{ label: "حسابداری", href: "#solutions" },
{ label: "نرم‌افزار اختصاصی", href: "#solutions" },
];
const quickLinks: LinkItem[] = [
{ label: "محصولات", href: "#solutions" },
{ label: "نمونه‌کارها", href: "#features" },
{ label: "درباره ما", href: "#about" },
{ label: "نظرات مشتریان", href: "#testimonials" },
{ label: "تماس با ما", href: "#contact" },
];
export const DEFAULT_SECTIONS: SiteSections = {
header: {
brandTitle: "منتک",
brandSubtitle: "MONTECH",
nav: quickLinks,
ctaLabel: "مشاوره رایگان",
},
hero: {
eyebrow: "هوش‌افزار،منظر | منتک",
titleLine1: "کسب‌وکارت را بگو؛",
titleHighlight: "راهکارش را پیدا کن.",
subtitle: "با کمک هوش مصنوعی منتک، بهترین نرم‌افزارها و خدمات متناسب با کسب‌وکار شما پیشنهاد می‌شود.",
searchPlaceholder: "مثلاً: من یک فروشگاه دارم و CRM می‌خواهم...",
searchDefaultQuery: "من یک فروشگاه دارم و CRM می‌خواهم...",
suggestionsLabel: "پیشنهادات پرطرفدار:",
suggestions: [
{ label: "طلا و جواهر", query: "مدیریت موجودی و حسابداری فروشگاه طلا و جواهر" },
{ label: "سیستم CRM", query: "نرم‌افزار CRM و ثبت سفارش آنلاین" },
{ label: "حسابداری", query: "راهکار حسابداری یکپارچه چند شعبه" },
{ label: "فروشگاه آنلاین", query: "فروشگاه اینترنتی متصل به انبار" },
],
mockup: {
domain: "cloud.montech.ir",
version: "نسخه ۴.۲",
salesValue: "۲۸,۴۵۰,۰۰۰,۰۰۰",
salesGrowth: "↑ +۲۴.۵٪ رشد این ماه",
customersValue: "۱,۸۴۲",
satisfaction: "★ ۹۸٪ شاخص رضایت",
chartDefaultTooltip: "شهریور: ۲۸.۴ میلیارد",
chartTooltips: ["تیر: ۹.۴ میلیارد", "مرداد: ۱۶.۲ میلیارد", "شهریور: ۲۸.۴ میلیارد (اوج)"],
goldBarCount: 24,
},
ai: {
title: "پیشنهاد هوشمند هوش‌افزار منتک",
queryLabel: "پرس‌وجوی شما:",
fallbackQuery: "درخواست راهکار نرم‌افزاری",
packageLabel: "بسته پیشنهادی متناسب با نیاز بیزینس شما:",
packageTitle: "بسته جامع هوشمند منتک",
matchBadge: "۹۸٪ تطابق",
items: [
"یکپارچه‌سازی سیستم حسابداری و انبارداری متصل به شبکه بانکی",
"داشبورد تحلیلی CRM با پیش‌بینی هوشمند رفتار مشتریان",
"اپلیکیشن موبایل اختصاصی و داشبورد مدیریتی تحت وب",
],
primaryCta: "ثبت مشاوره این بسته",
closeLabel: "بستن",
},
},
metrics: {
items: [
{ icon: "gear", value: "توسعه اختصاصی", label: "متناسب با نیاز شما", emphasis: "text" },
{ icon: "users", value: "۱۰۰+", label: "پروژه موفق", emphasis: "number" },
{ icon: "box", value: "۱۵+", label: "محصول تخصصی", emphasis: "number" },
],
},
features: {
eyebrow: "از نسخه پایه تا راهکار اختصاصی",
titleLine1: "نرم‌افزار را با",
titleLine2: "کسب‌وکارت هماهنگ کن.",
body: "ما در منتک، نرم‌افزارهای آماده را متناسب با فرایند، نیازها و هویت کسب‌وکار شما شخصی‌سازی و توسعه می‌دهیم.",
ctaLabel: "مشاهده نمونه‌کارها",
ctaHref: "#solutions",
baseCardLabel: "نسخه استاندارد پایه",
customCardTitle: "داشبورد اختصاصی شده برای مشتری",
customCardBadge: "هماهنگ‌شده ۱۰۰٪",
salesLabel: "فروش لحظه‌ای شعبه مرکزی:",
salesValue: "۲۸,۴۵۰,۰۰۰,۰۰۰",
linkNote: "اتصال مستقیم به درگاه بانکی و انبار",
aiLabel: "هوش مصنوعی",
aiStatus: "فعال",
aiNote: "پیش‌بینی تقاضا",
},
solutions: {
eyebrow: "محصولات منتک",
title: "راهکارهای منتک",
subtitle: "مجموعه‌ای از نرم‌افزارها و خدمات برای کسب‌وکارهای مختلف",
items: [
{ title: "نرم‌افزار اختصاصی", subtitle: "توسعه متناسب با نیاز شما", consultType: "نرم‌افزار اختصاصی", accent: "brand", icon: "cube" },
{ title: "حسابداری", subtitle: "مدیریت مالی ساده و دقیق", consultType: "حسابداری", accent: "sky", icon: "bars" },
{ title: "CRM", subtitle: "مدیریت مشتریان و فروش", consultType: "سیستم CRM", accent: "indigo", icon: "avatars" },
{ title: "فروشگاه آنلاین", subtitle: "فروش بیشتر، مدیریت آسان‌تر", consultType: "فروشگاه آنلاین", accent: "cyan", icon: "cart" },
{ title: "طلا و جواهر", subtitle: "نرم‌افزارهای تخصصی صنعت طلا", consultType: "طلا و جواهر", accent: "amber", icon: "gold" },
],
},
testimonials: {
eyebrow: "اعتماد کسب‌وکارها",
titleLine1: "در کنار کسب‌وکارهای",
titleLine2: "واقعی، در مسیر رشد.",
body: "از فروشگاه‌های کوچک تا مجموعه‌های بزرگ، منتک انتخاب کسب‌وکارهایی است که به آینده فکر می‌کنند.",
ctaLabel: "دیدن نظرات مشتریان",
badge: "مشتری تایید شده",
quote: "«نرم‌افزار منتک دقیقاً مطابق با نیاز ما شخصی‌سازی شد و باعث نظم و رشد کسب‌وکارمان شد.»",
name: "علی محمدی",
role: "مدیر فروشگاه طلا",
avatarUrl: "https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=150&q=80",
decorAvatars: [
"https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=150&q=80",
"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=150&q=80",
],
},
cta: {
titleLine1: "برای کسب‌وکار خود",
titleLine2: "مشاوره رایگان دریافت کنید.",
body: "کارشناسان ما آماده‌اند تا بهترین راهکار را متناسب با نیاز شما پیشنهاد دهند.",
ctaLabel: "درخواست مشاوره رایگان",
},
footer: {
contactTitle: "تماس با ما",
productsTitle: "محصولات",
products: productLinks,
quickLinksTitle: "دسترسی سریع",
quickLinks,
brandTitle: "منتک",
brandSubtitle: "MONTECH",
descriptionLines: ["هوش‌افزار منظر منتک", "با تکنولوژی، کسب‌وکارها را هوشمندتر می‌کنیم."],
copyright: "© منتک. تمامی حقوق محفوظ است.",
legalLinks: [
{ label: "حریم خصوصی", href: "#" },
{ label: "شرایط استفاده", href: "#" },
],
},
consult: {
title: "درخواست مشاوره رایگان منتک",
nameLabel: "نام و نام خانوادگی",
namePlaceholder: "مثال: رضا احمدی",
phoneLabel: "شماره تماس (جهت هماهنگی جلسه)",
phonePlaceholder: "0912...",
typeLabel: "زمینه کسب‌وکار شما",
types: [
{ value: "طلا و جواهر", label: "طلا و جواهر" },
{ value: "فروشگاه آنلاین", label: "فروشگاه آنلاین" },
{ value: "سیستم CRM", label: "سیستم CRM" },
{ value: "حسابداری", label: "حسابداری" },
{ value: "نرم‌افزار اختصاصی", label: "نرم‌افزار اختصاصی" },
{ value: "سایر", label: "سایر زمینه‌ها" },
],
successMessage: "✓ درخواست شما با موفقیت ثبت شد. کارشناسان ما به زودی با شما تماس خواهند گرفت.",
errorMessage: "ثبت درخواست با خطا مواجه شد. لطفاً دوباره تلاش کنید.",
submitLabel: "ارسال درخواست",
},
};
export interface SocialLink {
label: string;
url: string;
}
export interface PublicSettings {
brandName: string;
tagline: string;
email: string;
phone: string;
address: string;
timezone: string;
socials: SocialLink[];
logoUrl: string | null;
}
export interface PageSeoData {
title: string;
description: string;
ogImage?: string | null;
canonical?: string | null;
noIndex?: boolean;
}
export interface PublicPage {
key: string;
title: string;
seo: PageSeoData | null;
}
export interface PublicSitePayload {
settings: PublicSettings;
sections: Partial<{ [K in SectionKey]: Partial<SiteSections[K]> }>;
pages: PublicPage[];
}
/** Shallow-merges CMS data over defaults so a partially filled section never breaks rendering. */
export function resolveSection<K extends SectionKey>(key: K, data?: Partial<SiteSections[K]> | null): SiteSections[K] {
const fallback = DEFAULT_SECTIONS[key];
if (!data || typeof data !== "object") return fallback;
const merged = { ...fallback } as Record<string, unknown>;
for (const [k, v] of Object.entries(data)) {
if (v === undefined || v === null) continue;
const base = (fallback as unknown as Record<string, unknown>)[k];
if (base && typeof base === "object" && !Array.isArray(base) && typeof v === "object" && !Array.isArray(v)) {
merged[k] = { ...(base as object), ...(v as object) };
} else {
merged[k] = v;
}
}
return merged as unknown as SiteSections[K];
}
export const LEAD_STATUSES = ["new", "contacted", "closed"] as const;
export type LeadStatus = (typeof LEAD_STATUSES)[number];
export const LEAD_SOURCES = ["consult", "ai", "solution"] as const;
export type LeadSource = (typeof LEAD_SOURCES)[number];
export interface LeadInput {
name: string;
phone: string;
businessType: string;
source?: LeadSource;
aiQuery?: string;
}
export interface Lead extends Required<Omit<LeadInput, "aiQuery">> {
id: string;
aiQuery: string | null;
status: LeadStatus;
note: string | null;
createdAt: string;
updatedAt: string;
}