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

@@ -0,0 +1,57 @@
import { z } from "zod";
import { ButtonPropsSchema } from "./buttonSchema";
/**
* Zod schema for UPricingPlan component (basic props only)
* Reference: https://ui.nuxt.com/docs/components/pricing-plan#props
*/
export const PricingPlanPropsSchema = z.object({
/** The title of the pricing plan. */
title: z.string(),
/** The description text shown under the title. */
description: z.string().optional(),
/** The current price of the plan. */
price: z.string().optional(),
/**
* Discounted price.
* When set, the main price will appear with a strikethrough.
*/
discount: z.string().optional(),
/** The unit period (e.g. /month) displayed next to price. */
billingCycle: z.string().optional(),
/** Additional billing text above the billing cycle. */
billingPeriod: z.string().optional(),
/**
* List of plan features.
* Can be an array of strings or array of objects (feature items).
*/
features: z.array(z.string()).optional().default([]),
/** The button displayed at the bottom (ButtonProps). */
button: ButtonPropsSchema.optional(),
/**
* Visual variant of the pricing plan.
* @default "outline"
*/
variant: z.enum(["soft", "solid", "outline", "subtle"]).optional().default("outline"),
/**
* Layout orientation of the component.
* @default "vertical"
*/
orientation: z.enum(["vertical", "horizontal"]).optional().default("vertical"),
/** Optional tagline text displayed above price. */
tagline: z.string().optional(),
/** Terms or disclaimer text displayed below features. */
terms: z.string().optional(),
/**
* Highlights the pricing plan visually (adds a ring around it).
* @default false
*/
highlight: z.boolean().optional().default(false),
/**
* Enlarges the plan card slightly for emphasis.
* @default false
*/
scale: z.boolean().optional().default(false),
});
export type PricingPlanProps = z.infer<typeof PricingPlanPropsSchema>;