Add Wiki feature with Markdown, categories, tags, and search
Deploy to LXC / deploy (push) Failing after 4s
Deploy to LXC / deploy (push) Failing after 4s
- wiki_categories, wiki_pages, wiki_tags, wiki_page_tags tables - /wiki overview with category-grouped pages, tag sidebar, search - /wiki/new create page with Markdown editor and live preview - /wiki/[slug] view page with rendered Markdown, tags, edit/delete - /wiki/[slug]/edit with pre-populated editor - Tag input with existing tag suggestions (click to add) - Category management (add/delete) in sidebar - Tailwind Typography plugin for prose styling - marked package for Markdown rendering - Sidebar nav item with book icon Run db:push on server to create wiki tables. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,11 @@
|
||||
label: 'Gallery',
|
||||
icon: 'M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z'
|
||||
},
|
||||
{
|
||||
href: '/wiki',
|
||||
label: 'Wiki',
|
||||
icon: 'M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253'
|
||||
},
|
||||
{
|
||||
href: '/feature-requests',
|
||||
label: 'Requests',
|
||||
|
||||
@@ -352,3 +352,54 @@ export const featureRequests = pgTable('feature_requests', {
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
|
||||
});
|
||||
|
||||
// ─── Wiki ───────────────────────────────────────────────────────────
|
||||
|
||||
export const wikiCategories = pgTable('wiki_categories', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
name: text('name').notNull(),
|
||||
slug: text('slug').unique().notNull(),
|
||||
description: text('description'),
|
||||
sortOrder: integer('sort_order').default(0).notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
|
||||
});
|
||||
|
||||
export const wikiPages = pgTable(
|
||||
'wiki_pages',
|
||||
{
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
title: text('title').notNull(),
|
||||
slug: text('slug').unique().notNull(),
|
||||
content: text('content').notNull(),
|
||||
categoryId: uuid('category_id').references(() => wikiCategories.id, { onDelete: 'set null' }),
|
||||
createdBy: text('created_by'),
|
||||
updatedBy: text('updated_by'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow().notNull()
|
||||
},
|
||||
(table) => [
|
||||
index('wiki_pages_category_idx').on(table.categoryId),
|
||||
index('wiki_pages_slug_idx').on(table.slug)
|
||||
]
|
||||
);
|
||||
|
||||
export const wikiTags = pgTable('wiki_tags', {
|
||||
id: uuid('id').defaultRandom().primaryKey(),
|
||||
name: text('name').unique().notNull()
|
||||
});
|
||||
|
||||
export const wikiPageTags = pgTable(
|
||||
'wiki_page_tags',
|
||||
{
|
||||
pageId: uuid('page_id')
|
||||
.notNull()
|
||||
.references(() => wikiPages.id, { onDelete: 'cascade' }),
|
||||
tagId: uuid('tag_id')
|
||||
.notNull()
|
||||
.references(() => wikiTags.id, { onDelete: 'cascade' })
|
||||
},
|
||||
(table) => [
|
||||
index('wiki_page_tags_page_idx').on(table.pageId),
|
||||
index('wiki_page_tags_tag_idx').on(table.tagId)
|
||||
]
|
||||
);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
export function slugify(text: string): string {
|
||||
return text
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/[\s_]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '')
|
||||
.substring(0, 100);
|
||||
}
|
||||
Reference in New Issue
Block a user