feat(board): add BoardPost + BoardChannel models and zod schemas
- Add BoardPost model (author, title, seeking/offering, note, tags) with validator requiring at least one of seeking/offering - Add BoardChannel model (name, slackChannelId, tagSlugs) - Add boardPost/boardChannel create+update Zod schemas - Trim Member.board subdoc to only slackHandle (drop topics, details, offerPeerSupport, availability, personalMessage) - Remove old boardUpdateSchema
This commit is contained in:
parent
19d519b153
commit
1da59021a3
4 changed files with 68 additions and 20 deletions
9
server/models/boardChannel.js
Normal file
9
server/models/boardChannel.js
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
const boardChannelSchema = new mongoose.Schema({
|
||||||
|
name: { type: String, required: true },
|
||||||
|
slackChannelId: { type: String, required: true },
|
||||||
|
tagSlugs: [String],
|
||||||
|
}, { timestamps: true })
|
||||||
|
|
||||||
|
export default mongoose.models.BoardChannel || mongoose.model('BoardChannel', boardChannelSchema)
|
||||||
28
server/models/boardPost.js
Normal file
28
server/models/boardPost.js
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import mongoose from 'mongoose'
|
||||||
|
|
||||||
|
const boardPostSchema = new mongoose.Schema({
|
||||||
|
author: {
|
||||||
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
|
ref: 'Member',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
title: { type: String, required: true, maxlength: 120 },
|
||||||
|
seeking: { type: String, maxlength: 500 },
|
||||||
|
offering: { type: String, maxlength: 500 },
|
||||||
|
note: { type: String, maxlength: 300 },
|
||||||
|
tags: [String],
|
||||||
|
}, { timestamps: true })
|
||||||
|
|
||||||
|
boardPostSchema.pre('validate', function (next) {
|
||||||
|
const seeking = (this.seeking || '').trim()
|
||||||
|
const offering = (this.offering || '').trim()
|
||||||
|
if (!seeking && !offering) {
|
||||||
|
this.invalidate('seeking', 'At least one of seeking or offering must be provided')
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
boardPostSchema.index({ author: 1 })
|
||||||
|
boardPostSchema.index({ createdAt: -1 })
|
||||||
|
|
||||||
|
export default mongoose.models.BoardPost || mongoose.model('BoardPost', boardPostSchema)
|
||||||
|
|
@ -73,17 +73,7 @@ const memberSchema = new mongoose.Schema({
|
||||||
|
|
||||||
craftTags: [String],
|
craftTags: [String],
|
||||||
board: {
|
board: {
|
||||||
topics: [
|
|
||||||
{
|
|
||||||
tagSlug: String,
|
|
||||||
state: { type: String, enum: ['help', 'interested', 'seeking'] },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
offerPeerSupport: { type: Boolean, default: false },
|
|
||||||
availability: String,
|
|
||||||
slackHandle: String,
|
slackHandle: String,
|
||||||
personalMessage: String,
|
|
||||||
details: String,
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Privacy settings for profile fields
|
// Privacy settings for profile fields
|
||||||
|
|
|
||||||
|
|
@ -377,16 +377,37 @@ export const tagSuggestionSchema = z.object({
|
||||||
pool: z.enum(['craft', 'cooperative'])
|
pool: z.enum(['craft', 'cooperative'])
|
||||||
})
|
})
|
||||||
|
|
||||||
export const boardUpdateSchema = z.object({
|
// --- Board post / channel schemas ---
|
||||||
topics: z.array(z.object({
|
|
||||||
tagSlug: z.string().min(1).max(100),
|
export const boardPostCreateSchema = z.object({
|
||||||
state: z.enum(['help', 'interested', 'seeking'])
|
title: z.string().trim().min(1).max(120),
|
||||||
})).max(20).optional(),
|
seeking: z.string().max(500).optional(),
|
||||||
offerPeerSupport: z.boolean().optional(),
|
offering: z.string().max(500).optional(),
|
||||||
availability: z.string().max(500).optional(),
|
note: z.string().max(300).optional(),
|
||||||
slackHandle: z.string().max(200).optional(),
|
tags: z.array(z.string().max(100)).optional().default([])
|
||||||
personalMessage: z.string().max(2000).optional(),
|
}).refine(
|
||||||
details: z.string().max(300).optional()
|
(data) => (data.seeking || '').trim().length > 0 || (data.offering || '').trim().length > 0,
|
||||||
|
{ message: 'At least one of seeking or offering must be provided', path: ['seeking'] }
|
||||||
|
)
|
||||||
|
|
||||||
|
export const boardPostUpdateSchema = z.object({
|
||||||
|
title: z.string().trim().min(1).max(120).optional(),
|
||||||
|
seeking: z.string().max(500).optional(),
|
||||||
|
offering: z.string().max(500).optional(),
|
||||||
|
note: z.string().max(300).optional(),
|
||||||
|
tags: z.array(z.string().max(100)).optional()
|
||||||
|
})
|
||||||
|
|
||||||
|
export const boardChannelCreateSchema = z.object({
|
||||||
|
name: z.string().trim().min(1).max(200),
|
||||||
|
slackChannelId: z.string().trim().min(1).max(50).regex(/^[A-Z0-9]+$/, 'Invalid Slack channel ID'),
|
||||||
|
tagSlugs: z.array(z.string().max(100)).optional().default([])
|
||||||
|
})
|
||||||
|
|
||||||
|
export const boardChannelUpdateSchema = z.object({
|
||||||
|
name: z.string().trim().min(1).max(200).optional(),
|
||||||
|
slackChannelId: z.string().trim().min(1).max(50).regex(/^[A-Z0-9]+$/, 'Invalid Slack channel ID').optional(),
|
||||||
|
tagSlugs: z.array(z.string().max(100)).optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- Admin alert schemas ---
|
// --- Admin alert schemas ---
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue