turash/bugulma/frontend/pages/admin/AnnouncementsPage.tsx
2025-12-15 10:06:41 +01:00

195 lines
6.5 KiB
TypeScript

import { DataTable } from '@/components/admin/DataTable.tsx';
import { Badge, Button } from '@/components/ui';
import { useAnnouncements, useDeleteAnnouncement } from '@/hooks/api/useAdminAPI.ts';
import { useTranslation } from '@/hooks/useI18n.tsx';
import type { Announcement } from '@/services/admin-api.ts';
import { Edit, Plus, Trash2 } from 'lucide-react';
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
const AnnouncementsPage = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const pageSize = 25;
const [isActiveFilter, setIsActiveFilter] = useState<boolean | undefined>();
const { data, isLoading } = useAnnouncements({ isActive: isActiveFilter });
const { mutate: deleteAnnouncement } = useDeleteAnnouncement();
const handleDelete = (announcement: Announcement) => {
if (
window.confirm(
t('adminPage.content.announcements.confirmDelete', { title: announcement.title }) ||
`Delete "${announcement.title}"?`
)
) {
deleteAnnouncement(announcement.id, {
onSuccess: () => {
// Query will refetch automatically
},
});
}
};
const getPriorityBadge = (priority: string) => {
switch (priority) {
case 'urgent':
return (
<Badge variant="destructive">
{t('adminPage.content.announcements.urgent') || 'Urgent'}
</Badge>
);
case 'high':
return (
<Badge variant="default">{t('adminPage.content.announcements.high') || 'High'}</Badge>
);
case 'normal':
return (
<Badge variant="secondary">
{t('adminPage.content.announcements.normal') || 'Normal'}
</Badge>
);
case 'low':
return <Badge variant="outline">{t('adminPage.content.announcements.low') || 'Low'}</Badge>;
default:
return <Badge variant="secondary">{priority}</Badge>;
}
};
const columns = [
{
key: 'title',
header: t('adminPage.content.announcements.table.title') || 'Title',
render: (announcement: Announcement) => (
<span className="font-medium">{announcement.title}</span>
),
},
{
key: 'priority',
header: t('adminPage.content.announcements.table.priority') || 'Priority',
render: (announcement: Announcement) => getPriorityBadge(announcement.priority),
},
{
key: 'displayType',
header: t('adminPage.content.announcements.table.displayType') || 'Display Type',
render: (announcement: Announcement) => (
<Badge variant="outline">{announcement.displayType}</Badge>
),
},
{
key: 'isActive',
header: t('adminPage.content.announcements.table.status') || 'Status',
render: (announcement: Announcement) => (
<Badge variant={announcement.isActive ? 'success' : 'secondary'}>
{announcement.isActive
? t('adminPage.content.announcements.active') || 'Active'
: t('adminPage.content.announcements.inactive') || 'Inactive'}
</Badge>
),
},
{
key: 'views',
header: t('adminPage.content.announcements.table.views') || 'Views',
render: (announcement: Announcement) => (
<span className="text-sm text-muted-foreground">{announcement.views || 0}</span>
),
},
];
const actions = [
{
label: t('adminPage.content.announcements.edit') || 'Edit',
icon: <Edit className="h-4 w-4" />,
onClick: (announcement: Announcement) =>
navigate(`/admin/content/announcements/${announcement.id}/edit`),
},
{
label: t('adminPage.content.announcements.delete') || 'Delete',
icon: <Trash2 className="h-4 w-4" />,
variant: 'destructive' as const,
onClick: (announcement: Announcement) => handleDelete(announcement),
},
];
const announcements = data?.announcements || [];
return (
<div className="space-y-6">
{/* Header */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold">
{t('adminPage.content.announcements.title') || 'Announcements'}
</h1>
<p className="text-muted-foreground">
{t('adminPage.content.announcements.description') ||
'Manage system announcements and notifications'}
</p>
</div>
<Button onClick={() => navigate('/admin/content/announcements/new')}>
<Plus className="h-4 w-4 mr-2" />
{t('adminPage.content.announcements.newAnnouncement') || 'New Announcement'}
</Button>
</div>
{/* Filters */}
<div className="flex gap-2">
<Button
variant={isActiveFilter === undefined ? 'default' : 'outline'}
onClick={() => setIsActiveFilter(undefined)}
>
{t('adminPage.content.announcements.all') || 'All'}
</Button>
<Button
variant={isActiveFilter === true ? 'default' : 'outline'}
onClick={() => setIsActiveFilter(true)}
>
{t('adminPage.content.announcements.active') || 'Active'}
</Button>
<Button
variant={isActiveFilter === false ? 'default' : 'outline'}
onClick={() => setIsActiveFilter(false)}
>
{t('adminPage.content.announcements.inactive') || 'Inactive'}
</Button>
</div>
{/* Table */}
<DataTable
columns={columns}
data={announcements}
getRowId={(announcement) => announcement.id}
isLoading={isLoading}
actions={actions}
pagination={
announcements.length > 0
? {
currentPage,
totalPages: Math.ceil(announcements.length / pageSize),
pageSize,
totalItems: announcements.length,
onPageChange: setCurrentPage,
}
: undefined
}
emptyMessage={
t('adminPage.content.announcements.noAnnouncements') || 'No announcements found'
}
emptyDescription={
t('adminPage.content.announcements.createFirst') ||
'Create your first announcement to get started'
}
emptyAction={
<Button onClick={() => navigate('/admin/content/announcements/new')}>
<Plus className="h-4 w-4 mr-2" />
{t('adminPage.content.announcements.newAnnouncement') || 'New Announcement'}
</Button>
}
/>
</div>
);
};
export default AnnouncementsPage;