add status filter for content table

This commit is contained in:
smanylov
2026-06-02 17:45:07 +07:00
parent c74ef84d37
commit 03766057eb
7 changed files with 219 additions and 72 deletions
+12 -1
View File
@@ -3,7 +3,16 @@
import { getSessionData } from '@/app/actions/session';
import { API_BASE_URL } from '@/app/actions/definitions';
export async function fetchModerationContentList(page?: number, size?: number, sortBy?: string, sortDirection?: 'asc' | 'desc' | string) {
type ModerationStatus = 'BLOCKED' | 'ACTIVE' | 'MODERATION';
export async function fetchModerationContentList(
page?: number,
size?: number,
sortBy?: string,
sortDirection?: 'asc' | 'desc' | string,
statuses?: ModerationStatus[],
isAppeal?: boolean
) {
const token = await getSessionData('token');
try {
@@ -18,6 +27,8 @@ export async function fetchModerationContentList(page?: number, size?: number, s
page_size: size,
sort_by: sortBy || '',
sort_order: sortDirection || 'asc',
statuses: statuses,
is_appeal: isAppeal,
}
}),
headers: {
@@ -22,16 +22,20 @@ export interface ContentForModeration {
totalCount: number;
}
export type ModerationStatus = 'BLOCKED' | 'ACTIVE' | 'MODERATION';
export const useContentForModeration = (
page?: number,
size?: number,
sortBy?: string,
sortDirection?: 'asc' | 'desc' | string
sortDirection?: 'asc' | 'desc' | string,
statuses?: ModerationStatus[],
isAppeal?: boolean
) => {
return useQuery({
queryKey: ['contentForModeration', page, size, sortBy, sortDirection],
queryKey: ['contentForModeration', page, size, sortBy, sortDirection, statuses, isAppeal],
queryFn: () => {
return fetchModerationContentList(page, size, sortBy, sortDirection);
return fetchModerationContentList(page, size, sortBy, sortDirection, statuses, isAppeal);
},
select: (data: ContentForModeration | null) => {
if (!data) {
+82
View File
@@ -771,6 +771,7 @@
@media (max-width: 768px) {
width: auto;
}
}
.table-filtres-item {
display: flex;
@@ -806,6 +807,87 @@
box-shadow: 0 0 0 2px #6365f187;
}
}
.status-filters {
display: flex;
gap: 10px;
align-items: center;
flex-wrap: wrap;
.status-btn {
padding: 4px 12px;
border-radius: 16px;
border: 1px solid;
transition: all 0.2s ease;
cursor: pointer;
font-size: 14px;
font-weight: 500;
&:hover {
transform: translateY(-1px);
}
&:active {
transform: translateY(0);
}
// BLOCK статус
&--block {
background-color: white;
color: #dc2626;
border-color: #fca5a5;
&:hover {
background-color: #fef2f2;
border-color: #f87171;
}
&.active {
background-color: #dc2626;
color: white;
border-color: #b91c1c;
box-shadow: 0 2px 4px rgba(220, 38, 38, 0.2);
}
}
// ACTIVE статус
&--active {
background-color: white;
color: #16a34a;
border-color: #86efac;
&:hover {
background-color: #f0fdf4;
border-color: #4ade80;
}
&.active {
background-color: #16a34a;
color: white;
border-color: #15803d;
box-shadow: 0 2px 4px rgba(22, 163, 74, 0.2);
}
}
// MODERATION статус
&--moderation {
background-color: white;
color: #ca8a04;
border-color: #fde047;
&:hover {
background-color: #fefce8;
border-color: #facc15;
}
&.active {
background-color: #ca8a04;
color: white;
border-color: #a16207;
box-shadow: 0 2px 4px rgba(202, 138, 4, 0.2);
}
}
}
}
}
@@ -116,10 +116,6 @@ export default function ContentAppealsTable({ permission }: { permission: number
getSortParams.sortDirection
);
useEffect(() => {
console.log('Appeals data:', appealsData);
}, [appealsData]);
const queryClient = useQueryClient();
const handleViewFile = useCallback((file: AppealsFile) => {
+85 -35
View File
@@ -17,7 +17,7 @@ import DropDownList from '@/app/components/dropDownList';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import ModalWindow from '@/app/components/modalWindow';
import { toast } from 'sonner';
import { useContentForModeration, ModerationFile, ContentForModeration } from '@/app/hooks/react-query/useContentForModeration';
import { useContentForModeration, ModerationFile, ContentForModeration, ModerationStatus } from '@/app/hooks/react-query/useContentForModeration';
import FileModerationModal from './FileModerationModal';
import { FileTypeIcon } from '@/app/components/FileTypeIcon';
import { getFileType } from '@/app/lib/getFileType';
@@ -59,6 +59,7 @@ const StatusBadge = ({ status }: { status: string }) => {
'BLOCKED': { className: 'bg-red-100 text-red-800', label: 'Заблокировано' },
'MODERATION': { className: 'bg-yellow-100 text-yellow-800', label: 'На модерации' },
'APPROVED': { className: 'bg-green-100 text-green-800', label: 'Одобрено' },
'ACTIVE': { className: 'bg-green-100 text-green-800', label: 'ACTIVE' },
};
const config = statusConfig[status] || { className: 'bg-gray-100 text-gray-800', label: status };
@@ -82,6 +83,10 @@ export default function ContentModerationTable({ permission }: { permission: num
const [selectedFile, setSelectedFile] = useState<ModerationFile | null>(null);
const [isFileLoading, setIsFileLoading] = useState(false);
// НОВЫЕ СОСТОЯНИЯ ДЛЯ ФИЛЬТРОВ
const [selectedStatuses, setSelectedStatuses] = useState<ModerationStatus[]>([]);
const [isAppeal, setIsAppeal] = useState<boolean | undefined>(undefined);
const t = useTranslations("Global");
// Получаем параметры сортировки для API
@@ -104,6 +109,7 @@ export default function ContentModerationTable({ permission }: { permission: num
};
}, [sorting]);
// Используем хук с фильтрами
const {
data: moderationData,
isLoading,
@@ -114,11 +120,45 @@ export default function ContentModerationTable({ permission }: { permission: num
pagination.pageIndex,
pagination.pageSize,
getSortParams.sortBy,
getSortParams.sortDirection
getSortParams.sortDirection,
selectedStatuses.length > 0 ? selectedStatuses : undefined,
isAppeal
);
const queryClient = useQueryClient();
useEffect(() => {
console.log(moderationData);
}, [moderationData])
const toggleStatus = (status: ModerationStatus) => {
setSelectedStatuses(prev => {
if (prev.includes(status)) {
return prev.filter(s => s !== status);
} else {
return [...prev, status];
}
});
setPagination(prev => ({ ...prev, pageIndex: 0 }));
};
const clearAllStatuses = () => {
setSelectedStatuses([]);
setPagination(prev => ({ ...prev, pageIndex: 0 }));
};
// Обработчик изменения isAppeal
const handleAppealChange = (value: string) => {
if (value === 'all') {
setIsAppeal(undefined);
} else if (value === 'hasAppeal') {
setIsAppeal(true);
} else if (value === 'noAppeal') {
setIsAppeal(false);
}
setPagination(prev => ({ ...prev, pageIndex: 0 }));
};
const handleViewFile = useCallback((file: ModerationFile) => {
console.log('View file:', file);
setSelectedFile(file);
@@ -270,36 +310,6 @@ export default function ContentModerationTable({ permission }: { permission: num
</div>
),
},
/* {
accessorKey: 'moderationInfo.hasActiveAppeal',
header: ({ column }) => (
<div className="column">
<span>
Апелляция
</span>
<button
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
className="sort-button"
>
{column.getIsSorted() === 'asc' ?
<span><IconArrowUp /></span>
: column.getIsSorted() === 'desc' ?
<span><IconArrowDown /></span>
: <span><IconFilter /></span>
}
</button>
</div>
),
cell: ({ row }) => (
<div className="text-center">
{row.original.moderationInfo.hasActiveAppeal ? (
<span className="text-blue-600">Активна</span>
) : (
<span className="text-gray-400">Нет</span>
)}
</div>
),
} */
];
if (permission === 3) {
@@ -393,12 +403,52 @@ export default function ContentModerationTable({ permission }: { permission: num
<ModalWindow state={openWindow} callBack={handleCloseModal}>
<FileModerationModal file={selectedFile} onClose={handleCloseModal} />
</ModalWindow>
{/* Фильтры */}
<div className="tanstak-table-filtres">
<h3>
Таблица модерации контента
</h3>
{/* Фильтры */}
<div className="tanstak-table-filtres">
{/* Блок фильтрации по статусам */}
<div className="">
<div className="table-filtres-label">Статусы:</div>
<div className="status-filters">
<button
onClick={() => toggleStatus('BLOCKED')}
className={`status-btn status-btn--block ${selectedStatuses.includes('BLOCKED') ? 'active' : ''}`}
>
BLOCKED
</button>
<button
onClick={() => toggleStatus('ACTIVE')}
className={`status-btn status-btn--active ${selectedStatuses.includes('ACTIVE') ? 'active' : ''}`}
>
ACTIVE
</button>
<button
onClick={() => toggleStatus('MODERATION')}
className={`status-btn status-btn--moderation ${selectedStatuses.includes('MODERATION') ? 'active' : ''}`}
>
MODERATION
</button>
</div>
</div>
{/* Блок фильтрации по апелляции */}
<div className="table-filtres-item appeal">
<div className="table-filtres-label">Апелляция:</div>
<DropDownList
value={isAppeal === undefined ? t('all') : t('has-appeal')}
callBack={handleAppealChange}
>
<li value="all">
{t('all')}
</li>
<li value="hasAppeal">
{t('has-appeal')}
</li>
{/* <li value="noAppeal">Без апелляции</li> */}
</DropDownList>
</div>
<div className="table-filtres-item">
<div className="table-filtres-label">{t('items-per-page')}:</div>
+3 -1
View File
@@ -223,7 +223,9 @@
"save": "Save",
"delete": "Delete",
"create-stuff": "Create stuff",
"download": "Download"
"download": "Download",
"all": "All",
"has-appeal": "Has appeal"
},
"Login-register-form": {
"and": "and",
+3 -1
View File
@@ -223,7 +223,9 @@
"save": "Сохранить",
"delete": "Удалить",
"create-stuff": "Создать сотрудника",
"download": "Скачать"
"download": "Скачать",
"all": "Все",
"has-appeal": "Есть апелляция"
},
"Login-register-form": {
"and": "и",