feat(webDev): add inquiry modal for pricing plans

This commit introduces a 'Contact Sales' modal on the web development page, allowing users to inquire about specific service plans.

- The pricing plan buttons now trigger this modal, pre-filled with the selected plan's details.
- Users can add custom remarks to their inquiry.
- On submission, a pre-formatted message is generated and opened in WhatsApp using a new `useWhatsAppMsgSender` composable.
- Adds `NUXT_PUBLIC_WHATSAPP_NUMBER` to the runtime configuration.
- Refactors content validation by introducing Zod schemas for `PricingPlan` and `Button` props to improve type safety.
- Adds new i18n keys for the modal interface and message templates.
This commit is contained in:
xiaomai
2025-11-07 11:04:14 +08:00
parent 40b3ee147f
commit ccfd268682
14 changed files with 393 additions and 40 deletions

View File

@@ -1,4 +1,5 @@
import { defineContentConfig, defineCollection, z } from "@nuxt/content";
import { PricingPlanPropsSchema } from "./app/schemas/PricingPlanSchema";
const defineIndexSchema = () =>
z.object({
@@ -50,36 +51,7 @@ const defineWebDevSchema = () =>
icon: z.string().optional(), // 比如 "lucide:mouse-pointer-click"
// 你原结构里通过 createService 包装,但最终是一个对象
plans: z
.array(
z.object({
title: z.string().min(1),
description: z.string().optional(),
price: z.string().optional(), // 保持为 string例如 "RM499 起"),如需更严可用 regex
discount: z.string().optional(),
tagline: z.string().optional(),
highlight: z.boolean().optional().default(false),
features: z.array(z.string()).optional().default([]),
button: z
.object({
label: z.string().min(1),
href: z.string().url().optional(), // 如果你可能会传链接
// 预留扩展字段例如variant、target 等)
variant: z
.enum([
"link",
"solid",
"outline",
"soft",
"subtle",
"ghost",
])
.default("subtle"),
target: z.enum(["_self", "_blank"]).optional(),
})
.partial({ href: true, variant: true, target: true }) // 全部可选除 label如果你希望 label 也可选,可把 min(1) 去掉或改 optional
.optional(),
})
)
.array(PricingPlanPropsSchema)
.min(1),
// 预留扩展字段例如category、tags、hidden 等)
category: z.string().optional(),