add status filter for content table
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -771,39 +771,121 @@
|
||||
@media (max-width: 768px) {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.table-filtres-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 12.5rem;
|
||||
.table-filtres-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 12.5rem;
|
||||
}
|
||||
|
||||
.table-filtres-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
|
||||
.table-filtres-text-filter {
|
||||
background-color: #fff;
|
||||
border: 2px solid v.$border-color-1;
|
||||
border-radius: 12px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
transition: background-color .2s ease-in-out;
|
||||
display: flex;
|
||||
box-shadow: 0 1px 2px #0000000d;
|
||||
|
||||
&:focus {
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid v.$black;
|
||||
box-shadow: 0 0 0 2px #6365f187;
|
||||
}
|
||||
}
|
||||
|
||||
.table-filtres-label {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
.status-filters {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
|
||||
|
||||
.table-filtres-text-filter {
|
||||
background-color: #fff;
|
||||
border: 2px solid v.$border-color-1;
|
||||
border-radius: 12px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 8px 16px;
|
||||
.status-btn {
|
||||
padding: 4px 12px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.25;
|
||||
transition: background-color .2s ease-in-out;
|
||||
display: flex;
|
||||
box-shadow: 0 1px 2px #0000000d;
|
||||
|
||||
&:focus {
|
||||
outline-offset: 2px;
|
||||
outline: 2px solid v.$black;
|
||||
box-shadow: 0 0 0 2px #6365f187;
|
||||
&: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) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
<h3>
|
||||
Таблица модерации контента
|
||||
</h3>
|
||||
{/* Фильтры */}
|
||||
<div className="tanstak-table-filtres">
|
||||
<h3>
|
||||
Таблица модерации контента
|
||||
</h3>
|
||||
{/* Блок фильтрации по статусам */}
|
||||
<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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -223,7 +223,9 @@
|
||||
"save": "Сохранить",
|
||||
"delete": "Удалить",
|
||||
"create-stuff": "Создать сотрудника",
|
||||
"download": "Скачать"
|
||||
"download": "Скачать",
|
||||
"all": "Все",
|
||||
"has-appeal": "Есть апелляция"
|
||||
},
|
||||
"Login-register-form": {
|
||||
"and": "и",
|
||||
|
||||
Reference in New Issue
Block a user