diff --git a/server/index.js b/server/index.js index dc0e24f..781ede8 100644 --- a/server/index.js +++ b/server/index.js @@ -150,6 +150,7 @@ function mapPostRow(row) { sections: row.sections || [], footer: row.footer, isEditorsPick: row.is_editors_pick, + isSold: row.is_sold || false, category: row.category, createdAt: row.created_at, updatedAt: row.updated_at @@ -273,9 +274,11 @@ app.post('/posts', upload.fields(getUploadFields()), async (req, res) => { await ensureEditorsPickLimit(null, isEditorsPick) + const isSold = Boolean(payload.isSold) + const result = await query( - `INSERT INTO blog_posts (title, slug, preview_image, link_url, sections, footer, is_editors_pick, category) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `INSERT INTO blog_posts (title, slug, preview_image, link_url, sections, footer, is_editors_pick, is_sold, category) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *`, [ payload.title.trim(), @@ -285,6 +288,7 @@ app.post('/posts', upload.fields(getUploadFields()), async (req, res) => { JSON.stringify(sections), payload.footer || null, isEditorsPick, + isSold, payload.category || null ] ) @@ -333,6 +337,8 @@ app.put('/posts/:id', upload.fields(getUploadFields()), async (req, res) => { await ensureEditorsPickLimit(Number(id), true) } + const isSold = Boolean(payload.isSold) + const result = await query( `UPDATE blog_posts SET title = $1, @@ -342,8 +348,9 @@ app.put('/posts/:id', upload.fields(getUploadFields()), async (req, res) => { sections = $5, footer = $6, is_editors_pick = $7, - category = $8 - WHERE id = $9 + is_sold = $8, + category = $9 + WHERE id = $10 RETURNING *`, [ payload.title.trim(), @@ -353,6 +360,7 @@ app.put('/posts/:id', upload.fields(getUploadFields()), async (req, res) => { JSON.stringify(sections), payload.footer || null, isEditorsPick, + isSold, payload.category || null, id ] diff --git a/server/migrations.js b/server/migrations.js index 1317ded..7a7ff99 100644 --- a/server/migrations.js +++ b/server/migrations.js @@ -31,6 +31,20 @@ async function runMigrations() { END $$; `) + await query(` + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name = 'blog_posts' + AND column_name = 'is_sold' + ) THEN + ALTER TABLE blog_posts ADD COLUMN is_sold BOOLEAN NOT NULL DEFAULT FALSE; + END IF; + END $$; + `) + await query(` CREATE OR REPLACE FUNCTION set_updated_at() RETURNS TRIGGER AS $$ diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index b834e27..c505700 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -75,6 +75,7 @@ export default function AdminPage() { const [linkUrl, setLinkUrl] = useState('') const [footer, setFooter] = useState('') const [isEditorsPick, setIsEditorsPick] = useState(false) + const [isSold, setIsSold] = useState(false) const [category, setCategory] = useState('') const [mainImage, setMainImage] = useState({ file: null, @@ -116,6 +117,7 @@ export default function AdminPage() { setLinkUrl('') setFooter('') setIsEditorsPick(false) + setIsSold(false) setCategory('') setMainImage({ file: null, existing: null, previewUrl: null, removed: false }) setSections(ensureSectionSlots([])) @@ -128,6 +130,7 @@ export default function AdminPage() { setLinkUrl(post.linkUrl || '') setFooter(post.footer || '') setIsEditorsPick(post.isEditorsPick) + setIsSold(post.isSold || false) // Convert old format to new format const categoryMap: Record = { @@ -276,6 +279,7 @@ export default function AdminPage() { linkUrl: normaliseText(linkUrl) || undefined, footer: normaliseText(footer) || undefined, isEditorsPick, + isSold, category: category || undefined, existingMainImage: mainImage.file || mainImage.removed ? undefined : mainImage.existing, removeMainImage: mainImage.removed, @@ -688,6 +692,17 @@ export default function AdminPage() { + +
diff --git a/src/types/blog.ts b/src/types/blog.ts index 11e5469..caf4d31 100644 --- a/src/types/blog.ts +++ b/src/types/blog.ts @@ -26,6 +26,7 @@ export interface BlogPost { sections: BlogPostSection[] footer?: string | null isEditorsPick: boolean + isSold?: boolean category?: BlogCategory | null excerpt?: string createdAt: string