feat: FAQ management from admin, public /faq, homepage section, llms.txt
- Backend: faq_questions table (schema + migration), CRUD + reorder API, Swagger docs - Admin: FAQ page with create/edit, enable/disable, show on homepage, drag reorder - Public /faq page fetches enabled FAQs from API; layout builds dynamic JSON-LD - Homepage: FAQ section under Stay updated (homepage-enabled only) with See full FAQ link - llms.txt: FAQ section uses homepage FAQs from API - i18n: home.faq title/seeFull, admin FAQ nav Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -421,6 +421,22 @@ async function migrate() {
|
||||
created_at TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// FAQ questions table
|
||||
await (db as any).run(sql`
|
||||
CREATE TABLE IF NOT EXISTS faq_questions (
|
||||
id TEXT PRIMARY KEY,
|
||||
question TEXT NOT NULL,
|
||||
question_es TEXT,
|
||||
answer TEXT NOT NULL,
|
||||
answer_es TEXT,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
show_on_homepage INTEGER NOT NULL DEFAULT 0,
|
||||
rank INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL
|
||||
)
|
||||
`);
|
||||
} else {
|
||||
// PostgreSQL migrations
|
||||
await (db as any).execute(sql`
|
||||
@@ -790,6 +806,22 @@ async function migrate() {
|
||||
created_at TIMESTAMP NOT NULL
|
||||
)
|
||||
`);
|
||||
|
||||
// FAQ questions table
|
||||
await (db as any).execute(sql`
|
||||
CREATE TABLE IF NOT EXISTS faq_questions (
|
||||
id UUID PRIMARY KEY,
|
||||
question TEXT NOT NULL,
|
||||
question_es TEXT,
|
||||
answer TEXT NOT NULL,
|
||||
answer_es TEXT,
|
||||
enabled INTEGER NOT NULL DEFAULT 1,
|
||||
show_on_homepage INTEGER NOT NULL DEFAULT 0,
|
||||
rank INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL
|
||||
)
|
||||
`);
|
||||
}
|
||||
|
||||
console.log('Migrations completed successfully!');
|
||||
|
||||
@@ -267,6 +267,20 @@ export const sqliteLegalPages = sqliteTable('legal_pages', {
|
||||
createdAt: text('created_at').notNull(),
|
||||
});
|
||||
|
||||
// FAQ questions table (admin-managed, shown on /faq and optionally on homepage)
|
||||
export const sqliteFaqQuestions = sqliteTable('faq_questions', {
|
||||
id: text('id').primaryKey(),
|
||||
question: text('question').notNull(),
|
||||
questionEs: text('question_es'),
|
||||
answer: text('answer').notNull(),
|
||||
answerEs: text('answer_es'),
|
||||
enabled: integer('enabled', { mode: 'boolean' }).notNull().default(true),
|
||||
showOnHomepage: integer('show_on_homepage', { mode: 'boolean' }).notNull().default(false),
|
||||
rank: integer('rank').notNull().default(0),
|
||||
createdAt: text('created_at').notNull(),
|
||||
updatedAt: text('updated_at').notNull(),
|
||||
});
|
||||
|
||||
// Site Settings table for global website configuration
|
||||
export const sqliteSiteSettings = sqliteTable('site_settings', {
|
||||
id: text('id').primaryKey(),
|
||||
@@ -550,6 +564,20 @@ export const pgLegalPages = pgTable('legal_pages', {
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
});
|
||||
|
||||
// FAQ questions table (admin-managed)
|
||||
export const pgFaqQuestions = pgTable('faq_questions', {
|
||||
id: uuid('id').primaryKey(),
|
||||
question: pgText('question').notNull(),
|
||||
questionEs: pgText('question_es'),
|
||||
answer: pgText('answer').notNull(),
|
||||
answerEs: pgText('answer_es'),
|
||||
enabled: pgInteger('enabled').notNull().default(1),
|
||||
showOnHomepage: pgInteger('show_on_homepage').notNull().default(0),
|
||||
rank: pgInteger('rank').notNull().default(0),
|
||||
createdAt: timestamp('created_at').notNull(),
|
||||
updatedAt: timestamp('updated_at').notNull(),
|
||||
});
|
||||
|
||||
// Site Settings table for global website configuration
|
||||
export const pgSiteSettings = pgTable('site_settings', {
|
||||
id: uuid('id').primaryKey(),
|
||||
@@ -597,6 +625,7 @@ export const userSessions = dbType === 'postgres' ? pgUserSessions : sqliteUserS
|
||||
export const invoices = dbType === 'postgres' ? pgInvoices : sqliteInvoices;
|
||||
export const siteSettings = dbType === 'postgres' ? pgSiteSettings : sqliteSiteSettings;
|
||||
export const legalPages = dbType === 'postgres' ? pgLegalPages : sqliteLegalPages;
|
||||
export const faqQuestions = dbType === 'postgres' ? pgFaqQuestions : sqliteFaqQuestions;
|
||||
|
||||
// Type exports
|
||||
export type User = typeof sqliteUsers.$inferSelect;
|
||||
@@ -627,3 +656,5 @@ export type SiteSettings = typeof sqliteSiteSettings.$inferSelect;
|
||||
export type NewSiteSettings = typeof sqliteSiteSettings.$inferInsert;
|
||||
export type LegalPage = typeof sqliteLegalPages.$inferSelect;
|
||||
export type NewLegalPage = typeof sqliteLegalPages.$inferInsert;
|
||||
export type FaqQuestion = typeof sqliteFaqQuestions.$inferSelect;
|
||||
export type NewFaqQuestion = typeof sqliteFaqQuestions.$inferInsert;
|
||||
|
||||
Reference in New Issue
Block a user