diff --git a/api/src/index.ts b/api/src/index.ts index acc01e1..bda10b5 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -105,6 +105,7 @@ import { calendarRouter } from './routes/calendar'; import { docsRouter } from './routes/docs'; import { units } from './routes/units'; import { modRequestRouter } from './routes/modRequest' +import { discussionRouter } from './routes/discussion'; app.use('/application', applicationRouter); app.use('/ranks', ranks); @@ -121,6 +122,7 @@ app.use('/calendar', calendarRouter) app.use('/units', units) app.use('/docs', docsRouter) app.use('/mod-request', modRequestRouter) +app.use('/discussions', discussionRouter) app.use('/', authRouter) app.get('/ping', (req, res) => { diff --git a/api/src/routes/discussion.ts b/api/src/routes/discussion.ts new file mode 100644 index 0000000..0ef42a7 --- /dev/null +++ b/api/src/routes/discussion.ts @@ -0,0 +1,35 @@ +const express = require('express'); +const router = express.Router(); + +import { Request, Response } from 'express'; +import { requireLogin, requireMemberState, requireRole } from '../middleware/auth'; +import { logger } from '../services/logging/logger'; +import { audit } from '../services/logging/auditLog'; +import { MemberState } from '@app/shared/types/member'; +import { createDiscussion, getAllDiscussions, getDiscussionById, postComment } from '../services/db/discussionService'; +import { ModRequest } from '@app/shared/schemas/modRequest'; +import { DiscussionComment } from '@app/shared/types/discussion'; + +router.use(requireLogin); +router.use(requireMemberState(MemberState.Member)); + +router.post('/comment', async (req: Request, res: Response) => { + try { + let comment = req.body as DiscussionComment; + + console.log(comment); + + await postComment(comment, req.user.id); + + res.sendStatus(201); + } catch (error) { + + } +}); + +router.delete('/comment/:id', async (req: Request, res: Response) => { + +}) + + +export const discussionRouter = router; \ No newline at end of file diff --git a/api/src/services/db/discussionService.ts b/api/src/services/db/discussionService.ts index 4b3cc37..62d7298 100644 --- a/api/src/services/db/discussionService.ts +++ b/api/src/services/db/discussionService.ts @@ -1,10 +1,9 @@ - - import { toDateTime } from "@app/shared/utils/time"; import pool from "../../db"; import { LOARequest, LOAType } from '@app/shared/types/loa' import { PagedData } from '@app/shared/types/pagination' import { DiscussionPost } from '@app/shared/types/discussion'; +import { DiscussionComment } from '@app/shared/types/discussion'; /** * Retrieves all discussion posts with pagination and optional type filtering. @@ -98,7 +97,8 @@ export async function createDiscussion(type: string, authorID: number, postTi * @returns {Promise | null>} The discussion post or null if not found */ export async function getDiscussionById(id: number): Promise | null> { - const sql = ` + // Get the post + const postSql = ` SELECT p.*, m.name as poster_name @@ -107,14 +107,42 @@ export async function getDiscussionById(id: number): Promise[]; - if (results.length === 0) { + const postResults = (await pool.query(postSql, [id])) as DiscussionPost[]; + if (postResults.length === 0) { return null; } - return results[0]; + const post = postResults[0]; + + // Get comments for the post + const commentSql = ` + SELECT + c.* + FROM discussion_comments AS c + WHERE c.post_id = ? + AND c.is_deleted = FALSE + ORDER BY c.created_at ASC; + `; + const comments = (await pool.query(commentSql, [id])) as DiscussionComment[]; + + // Attach comments to post + post.comments = comments; + + return post; } -export async function postComment() { - +export async function getPostComments(postID: number): Promise { + let comments = await pool.query("SELECT * FROM discussion_comments WHERE post_id = ?", [postID]); + return comments; +} + +export async function postComment(commentData: DiscussionComment, poster: number) { + const sql = ` + INSERT INTO discussion_comments (post_id, poster_id, content) VALUES (?, ?, ?); + `; + + const result = await pool.query(sql, [commentData.post_id, poster, commentData.content]); + + if (!result.affectedRows || result.affectedRows !== 1) { + throw new Error('Failed to insert comment: expected 1 row to be inserted'); + } } \ No newline at end of file diff --git a/shared/types/discussion.ts b/shared/types/discussion.ts index 62d6370..3ef2ef3 100644 --- a/shared/types/discussion.ts +++ b/shared/types/discussion.ts @@ -16,9 +16,9 @@ export interface DiscussionPost { export interface DiscussionComment { id?: number; post_id: number; - poster_id: number; + poster_id?: number; content: string; - created_at: Date; - updated_at: Date; - is_deleted: boolean; + created_at?: Date; + updated_at?: Date; + is_deleted?: boolean; } \ No newline at end of file diff --git a/ui/src/api/discussion.ts b/ui/src/api/discussion.ts new file mode 100644 index 0000000..a326d14 --- /dev/null +++ b/ui/src/api/discussion.ts @@ -0,0 +1,22 @@ +import { DiscussionComment } from "@shared/types/discussion"; + +//@ts-expect-error +const addr = import.meta.env.VITE_APIHOST; + +export async function postComment(comment: DiscussionComment) { + const res = await fetch(`${addr}/discussions/comment`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(comment), + credentials: 'include', + }); + + if (res.ok) { + return; + } else { + throw new Error("Failed to submit LOA"); + } +} + diff --git a/ui/src/components/discussion/CommentForm.vue b/ui/src/components/discussion/CommentForm.vue new file mode 100644 index 0000000..513b27e --- /dev/null +++ b/ui/src/components/discussion/CommentForm.vue @@ -0,0 +1,42 @@ + + +