--- import { Icon } from 'astro-icon/components' import { actions, isInputError } from 'astro:actions' import { z } from 'astro:schema' import { adminAnnouncementActions } from '../../../actions/admin/announcement' import Button from '../../../components/Button.astro' import InputCardGroup from '../../../components/InputCardGroup.astro' import InputSubmitButton from '../../../components/InputSubmitButton.astro' import InputText from '../../../components/InputText.astro' import InputTextArea from '../../../components/InputTextArea.astro' import SortArrowIcon from '../../../components/SortArrowIcon.astro' import TimeFormatted from '../../../components/TimeFormatted.astro' import Tooltip from '../../../components/Tooltip.astro' import { announcementTypes, getAnnouncementTypeInfo, zodAnnouncementTypesById, } from '../../../constants/announcementTypes' import BaseLayout from '../../../layouts/BaseLayout.astro' import { zodParseQueryParamsStoringErrors } from '../../../lib/parseUrlFilters' import { prisma } from '../../../lib/prisma' import type { AnnouncementType, Prisma } from '@prisma/client' const { data: filters } = zodParseQueryParamsStoringErrors( { 'sort-by': z .enum(['title', 'type', 'startDate', 'endDate', 'isActive', 'createdAt']) .default('createdAt'), 'sort-order': z.enum(['asc', 'desc']).default('desc'), search: z.string().optional(), type: zodAnnouncementTypesById.optional(), status: z.enum(['active', 'inactive']).optional(), }, Astro ) // Set up Prisma orderBy with correct typing const prismaOrderBy = { [filters['sort-by']]: filters['sort-order'] === 'asc' ? 'asc' : 'desc', } as const satisfies Prisma.AnnouncementOrderByWithRelationInput // Build where clause based on filters const whereClause: Prisma.AnnouncementWhereInput = {} if (filters.search) { whereClause.OR = [{ content: { contains: filters.search, mode: 'insensitive' } }] } if (filters.type) { whereClause.type = filters.type as AnnouncementType } if (filters.status) { whereClause.isActive = filters.status === 'active' } // Retrieve announcements from the database const announcements = await prisma.announcement.findMany({ where: whereClause, orderBy: prismaOrderBy, }) // Helper for generating sort URLs const makeSortUrl = (slug: NonNullable<(typeof filters)['sort-by']>) => { const currentSortBy = filters['sort-by'] const currentSortOrder = filters['sort-order'] const newSortOrder = currentSortBy === slug && currentSortOrder === 'asc' ? 'desc' : 'asc' const searchParams = new URLSearchParams(Astro.url.search) searchParams.set('sort-by', slug) searchParams.set('sort-order', newSortOrder) return `/admin/announcements?${searchParams.toString()}` } // Current date for form min values const currentDate = new Date().toISOString().slice(0, 16) // Format: YYYY-MM-DDThh:mm // Default new announcement const newAnnouncement = { content: '', type: 'INFO' as const, link: null, linkText: null, startDate: currentDate, endDate: '', isActive: true as boolean, } satisfies Prisma.AnnouncementCreateInput // Get action results const createResult = Astro.getActionResult(adminAnnouncementActions.create) const updateResult = Astro.getActionResult(adminAnnouncementActions.update) const deleteResult = Astro.getActionResult(adminAnnouncementActions.delete) const toggleResult = Astro.getActionResult(adminAnnouncementActions.toggleActive) const createInputErrors = isInputError(createResult?.error) ? createResult.error.fields : {} // Add success messages to banners Astro.locals.banners.addIfSuccess(createResult, 'Announcement created successfully!') Astro.locals.banners.addIfSuccess(updateResult, 'Announcement updated successfully!') Astro.locals.banners.addIfSuccess(deleteResult, 'Announcement deleted successfully!') Astro.locals.banners.addIfSuccess( toggleResult, (data) => `Announcement ${data.updatedAnnouncement.isActive ? 'activated' : 'deactivated'} successfully!` ) // Add error messages to banners if (createResult?.error) { const err = createResult.error Astro.locals.banners.add({ uiMessage: err.message, type: 'error', origin: 'action', error: err, }) } if (updateResult?.error) { const err = updateResult.error Astro.locals.banners.add({ uiMessage: err.message, type: 'error', origin: 'action', error: err, }) } if (deleteResult?.error) { const err = deleteResult.error Astro.locals.banners.add({ uiMessage: err.message, type: 'error', origin: 'action', error: err, }) } if (toggleResult?.error) { const err = toggleResult.error Astro.locals.banners.add({ uiMessage: err.message, type: 'error', origin: 'action', error: err, }) } --- Announcements {announcements.length} announcements Search Type All Types { announcementTypes.map((type) => ( {type.label} )) } Status All Statuses Active Inactive Create New Announcement ({ label: type.label, value: type.value, icon: type.icon, }))} cardSize="sm" required selectedValue={newAnnouncement.type} /> Confirm Deletion Are you sure you want to delete this announcement? This action cannot be undone. Announcements List Scroll horizontally to see more → Title Type Start Date End Date Status Created At Actions { announcements.length === 0 && ( No announcements found matching your criteria. Try adjusting your search or filters, or create a new announcement. ) } { announcements.map((announcement) => { const announcementTypeInfo = getAnnouncementTypeInfo(announcement.type) return ( <> {announcement.content} {announcementTypeInfo.label} {announcement.endDate ? ( ) : ( — )} {announcement.isActive ? 'Active' : 'Inactive'} Edit: {announcement.content} ({ label: type.label, value: type.value, icon: type.icon, }))} cardSize="sm" required selectedValue={announcement.type} /> > ) }) }
Are you sure you want to delete this announcement? This action cannot be undone.
No announcements found matching your criteria.
Try adjusting your search or filters, or create a new announcement.