feat(storefront): update FAQ and legal documentation

- Added new FAQ sections for account security, ordering and checkout, returns, shipping, and contact information.
- Introduced legal documents including privacy policy, terms of service, data protection, and general terms and conditions.
- Updated package dependencies to include gray-matter and remark-gfm for enhanced markdown support.
This commit is contained in:
2026-03-13 21:39:25 +03:00
parent f1dbf0b6ee
commit c8f5d8d096
52 changed files with 2273 additions and 261 deletions

View File

@@ -17,6 +17,7 @@ import type * as checkoutActions from "../checkoutActions.js";
import type * as emails from "../emails.js";
import type * as fulfillmentActions from "../fulfillmentActions.js";
import type * as http from "../http.js";
import type * as messages from "../messages.js";
import type * as model_carts from "../model/carts.js";
import type * as model_categories from "../model/categories.js";
import type * as model_checkout from "../model/checkout.js";
@@ -50,6 +51,7 @@ declare const fullApi: ApiFromModules<{
emails: typeof emails;
fulfillmentActions: typeof fulfillmentActions;
http: typeof http;
messages: typeof messages;
"model/carts": typeof model_carts;
"model/categories": typeof model_categories;
"model/checkout": typeof model_checkout;

57
convex/messages.test.ts Normal file
View File

@@ -0,0 +1,57 @@
import { convexTest } from "convex-test";
import { describe, it, expect } from "vitest";
import { api } from "./_generated/api";
import schema from "./schema";
const modules = import.meta.glob("./**/*.ts");
describe("messages.submit", () => {
it("creates one document with correct fields for valid payload", async () => {
const t = convexTest(schema, modules);
const messageId = await t.mutation(api.messages.submit, {
fullName: "Jane Doe",
email: "jane@example.com",
topic: "support",
message: "I need help with my order.",
});
expect(messageId).toBeTruthy();
await t.run(async (ctx) => {
const doc = await ctx.db.get(messageId);
expect(doc).not.toBeNull();
expect(doc!.fullName).toBe("Jane Doe");
expect(doc!.email).toBe("jane@example.com");
expect(doc!.topic).toBe("support");
expect(doc!.message).toBe("I need help with my order.");
expect(doc!.createdAt).toBeGreaterThan(0);
expect(doc!.bookmarked).toBe(false);
});
});
it("rejects invalid topic", async () => {
const t = convexTest(schema, modules);
await expect(
t.mutation(api.messages.submit, {
fullName: "Jane Doe",
email: "jane@example.com",
topic: "invalid_topic",
message: "Hello",
}),
).rejects.toThrow();
});
it("rejects missing required fields", async () => {
const t = convexTest(schema, modules);
const invalidArgs = {
fullName: "Jane Doe",
};
await expect(
t.mutation(api.messages.submit, invalidArgs as {
fullName: string;
email: string;
topic: "products" | "orders" | "support" | "other";
message: string;
}),
).rejects.toThrow();
});
});

32
convex/messages.ts Normal file
View File

@@ -0,0 +1,32 @@
import { mutation } from "./_generated/server";
import { v } from "convex/values";
const topicValidator = v.union(
v.literal("products"),
v.literal("orders"),
v.literal("support"),
v.literal("other"),
);
/**
* Public mutation: submit a contact message. No auth required.
*/
export const submit = mutation({
args: {
fullName: v.string(),
email: v.string(),
topic: topicValidator,
message: v.string(),
},
handler: async (ctx, args) => {
const now = Date.now();
return await ctx.db.insert("messages", {
fullName: args.fullName,
email: args.email,
topic: args.topic,
message: args.message,
createdAt: now,
bookmarked: false,
});
},
});

View File

@@ -313,4 +313,23 @@ export default defineSchema({
})
.index("by_user", ["userId"])
.index("by_session", ["sessionId"]),
// ─── Contact messages ───────────────────────────────────────────────────
messages: defineTable({
fullName: v.string(),
email: v.string(),
topic: v.union(
v.literal("products"),
v.literal("orders"),
v.literal("support"),
v.literal("other"),
),
message: v.string(),
createdAt: v.number(),
bookmarked: v.optional(v.boolean()),
updatedAt: v.optional(v.number()),
})
.index("by_created_at", ["createdAt"])
.index("by_topic", ["topic"])
.index("by_bookmarked", ["bookmarked"]),
});