diff --git a/apps/api/src/controllers/boards.controller.ts b/apps/api/src/controllers/boards.controller.ts index 3d5d67b..5570ac8 100644 --- a/apps/api/src/controllers/boards.controller.ts +++ b/apps/api/src/controllers/boards.controller.ts @@ -4,6 +4,8 @@ import { Board, Company, Post } from '../models/index.js'; import { asyncHandler, AppError } from '../middlewares/index.js'; export const listBoards = asyncHandler(async (req: Request, res: Response): Promise => { + const { boardID } = req.body; + // Get companyID from authenticated user first, then subdomain/context let companyID: mongoose.Types.ObjectId; @@ -24,9 +26,15 @@ export const listBoards = asyncHandler(async (req: Request, res: Response): Prom companyID = defaultCompany._id; } + // Build match query - filter by boardID if provided + const matchQuery: any = { companyID: new mongoose.Types.ObjectId(companyID) }; + if (boardID) { + matchQuery._id = new mongoose.Types.ObjectId(boardID); + } + // Get boards with dynamic postCount via aggregation const boards = await Board.aggregate([ - { $match: { companyID: new mongoose.Types.ObjectId(companyID) } }, + { $match: matchQuery }, { $lookup: { from: 'posts', diff --git a/apps/api/src/middlewares/validate.middleware.ts b/apps/api/src/middlewares/validate.middleware.ts index f4ef078..196c7c5 100644 --- a/apps/api/src/middlewares/validate.middleware.ts +++ b/apps/api/src/middlewares/validate.middleware.ts @@ -45,6 +45,7 @@ export const cursorPaginationSchema = z.object({ // Board schemas export const boardListSchema = z.object({ apiKey: z.string().optional(), + boardID: objectIdSchema.optional(), }); export const boardRetrieveSchema = z.object({ diff --git a/apps/web/src/pages/admin/AdminBoardsPage.tsx b/apps/web/src/pages/admin/AdminBoardsPage.tsx index d9ed76c..473a4ed 100644 --- a/apps/web/src/pages/admin/AdminBoardsPage.tsx +++ b/apps/web/src/pages/admin/AdminBoardsPage.tsx @@ -9,6 +9,8 @@ import { TrashIcon, EyeIcon, EyeSlashIcon, + ClipboardIcon, + CheckIcon, } from '@heroicons/react/24/outline'; import toast from 'react-hot-toast'; @@ -20,6 +22,18 @@ export function AdminBoardsPage() { const [editingBoard, setEditingBoard] = useState<{ id: string; name: string; isPrivate: boolean } | null>(null); const [newBoard, setNewBoard] = useState({ name: '', isPrivate: false }); const [isSubmitting, setIsSubmitting] = useState(false); + const [copiedBoardId, setCopiedBoardId] = useState(null); + + const handleCopyBoardId = async (boardId: string) => { + try { + await navigator.clipboard.writeText(boardId); + setCopiedBoardId(boardId); + toast.success('Board ID copied to clipboard!'); + setTimeout(() => setCopiedBoardId(null), 2000); + } catch (err) { + toast.error('Failed to copy Board ID'); + } + }; useEffect(() => { fetchBoards(); @@ -181,6 +195,18 @@ export function AdminBoardsPage() {
+ @@ -197,6 +224,7 @@ export function AdminBoardsPage() { variant="ghost" onClick={() => handleDelete(board.id, board.name)} className="of-text-red-600 hover:of-text-red-700" + title="Delete Board" >