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:
@@ -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,2 +1,3 @@
|
||||
export * from "./brand";
|
||||
export * from "./sections";
|
||||
export * from "./timezone";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user