add appeals change status
This commit is contained in:
@@ -1,18 +1,29 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
import ContentModerationTable from '@/app/ui/content/content-moderation-table';
|
import ContentModerationTable from '@/app/ui/content/content-moderation-table';
|
||||||
import ContentAppealsTable from '@/app/ui/content/content-appeals-table';
|
import ContentAppealsTable from '@/app/ui/content/content-appeals-table';
|
||||||
|
import { useEmployeInfo } from '@/app/hooks/react-query/useEmployeInfo';
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
|
const { getPermissionLevel } = useEmployeInfo();
|
||||||
|
const contentModerationPermissionLevel = getPermissionLevel('content_moderation');
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="admin-content">
|
<div className="admin-content">
|
||||||
<div className="content-header">
|
<div className="content-header">
|
||||||
<h3>Управление контентом</h3>
|
<h3>Управление контентом</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="content-body">
|
{getPermissionLevel('content_moderation') > 0 && (
|
||||||
<ContentModerationTable permission={3} />
|
<>
|
||||||
</div>
|
<div className="content-body">
|
||||||
<div className="content-body">
|
<ContentModerationTable permission={contentModerationPermissionLevel} />
|
||||||
<ContentAppealsTable permission={3} />
|
</div>
|
||||||
</div>
|
<div className="content-body">
|
||||||
|
<ContentAppealsTable permission={contentModerationPermissionLevel} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -86,13 +86,20 @@ export async function changeModerationContentStatus(fileId?: string, fileStatus?
|
|||||||
|
|
||||||
export async function downloadFile(fileId: string, fileName: string) {
|
export async function downloadFile(fileId: string, fileName: string) {
|
||||||
const token = await getSessionData('token');
|
const token = await getSessionData('token');
|
||||||
|
console.log('downloadFile');
|
||||||
|
|
||||||
|
/* ${API_DASHBOARD_URL} */
|
||||||
|
/* ${API_BASE_URL} */
|
||||||
const response = await fetch(`${API_DASHBOARD_URL}/api/v1/files/download/${fileId}`, {
|
const response = await fetch(`${API_DASHBOARD_URL}/api/v1/files/download/${fileId}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${token}`,
|
'Authorization': `Bearer ${token}`,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* console.log(response); */
|
||||||
|
const text = await response.text();
|
||||||
|
console.log(text);
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw 'failed-to-download-file';
|
throw 'failed-to-download-file';
|
||||||
}
|
}
|
||||||
@@ -140,6 +147,47 @@ export async function fetchAppealsContentList(page?: number, size?: number, sort
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
throw new Error(`${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function changeAppealContentStatus(appealId?: string, approve?: boolean, comment?: string) {
|
||||||
|
const token = await getSessionData('token');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${API_BASE_URL}/api/admin/control`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
version: 1,
|
||||||
|
msg_id: 40002,
|
||||||
|
message_body: {
|
||||||
|
action: 'review_appeal',
|
||||||
|
appeal_id: appealId,
|
||||||
|
approve: approve,
|
||||||
|
comment: comment
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const parsed = await response.json();
|
||||||
|
console.log(parsed);
|
||||||
|
|
||||||
|
if (parsed.message_code === 0) {
|
||||||
|
return parsed.message_body.message_body;
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`${response.status}`);
|
throw new Error(`${response.status}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,8 @@ export interface AppealsFile {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
fileId: string;
|
fileId: string;
|
||||||
resolvedAt: string;
|
resolvedAt: string;
|
||||||
status: 'BLOCKED' | 'APPROVED' | string;
|
fileName: string;
|
||||||
|
status: 'REJECTED' | 'APPROVED' | string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ContentForAppeals {
|
export interface ContentForAppeals {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import { useState } from 'react';
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import DropDownList from '@/app/components/dropDownList';
|
import DropDownList from '@/app/components/dropDownList';
|
||||||
/* import { changeAppealStatus } from '@/app/actions/contentActions'; */
|
|
||||||
import { AppealsFile } from '@/app/hooks/react-query/useContentForAppeals';
|
import { AppealsFile } from '@/app/hooks/react-query/useContentForAppeals';
|
||||||
|
import { changeAppealContentStatus } from '@/app/actions/contentActions';
|
||||||
|
|
||||||
interface AppealModerationModalProps {
|
interface AppealModerationModalProps {
|
||||||
file: AppealsFile | null;
|
file: AppealsFile | null;
|
||||||
@@ -14,7 +14,7 @@ interface AppealModerationModalProps {
|
|||||||
|
|
||||||
const getLabel = (status: string) => {
|
const getLabel = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'BLOCKED':
|
case 'REJECTED':
|
||||||
return 'Заблокировано';
|
return 'Заблокировано';
|
||||||
case 'MODERATION':
|
case 'MODERATION':
|
||||||
return 'На модерации';
|
return 'На модерации';
|
||||||
@@ -25,10 +25,23 @@ const getLabel = (status: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getStatusValue = (label: string) => {
|
||||||
|
switch (label) {
|
||||||
|
case 'REJECTED':
|
||||||
|
return 'REJECTED';
|
||||||
|
case 'MODERATION':
|
||||||
|
return 'MODERATION';
|
||||||
|
case 'APPROVED':
|
||||||
|
return 'APPROVED';
|
||||||
|
default:
|
||||||
|
return 'MODERATION';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const StatusBadge = ({ status }: { status: string }) => {
|
const StatusBadge = ({ status }: { status: string }) => {
|
||||||
const getBadgeClass = () => {
|
const getBadgeClass = () => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'BLOCKED':
|
case 'REJECTED':
|
||||||
return 'status-badge--blocked';
|
return 'status-badge--blocked';
|
||||||
case 'MODERATION':
|
case 'MODERATION':
|
||||||
return 'status-badge--moderation';
|
return 'status-badge--moderation';
|
||||||
@@ -46,16 +59,16 @@ const StatusBadge = ({ status }: { status: string }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default function AppealModerationModal({ file, onClose }: AppealModerationModalProps) {
|
export default function AppealModerationModal({ file, onClose }: AppealModerationModalProps) {
|
||||||
const [selectedStatus, setSelectedStatus] = useState<string>(file?.status || 'MODERATION');
|
const [selectedStatus, setSelectedStatus] = useState<string>(getLabel(file?.status || 'MODERATION'));
|
||||||
const [adminComment, setAdminComment] = useState<string>(file?.adminComment || '');
|
const [adminComment, setAdminComment] = useState<string>(file?.adminComment || '');
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
/* const changeStatusMutation = useMutation({
|
const changeStatusMutation = useMutation({
|
||||||
mutationFn: async ({ appealId, status, comment }: { appealId: string; status: string; comment: string }) => {
|
mutationFn: async ({ appealId, approve, comment }: { appealId: string; approve: boolean; comment: string }) => {
|
||||||
return await changeAppealStatus(appealId, status, comment);
|
return await changeAppealContentStatus(appealId, approve, comment);
|
||||||
},
|
},
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
if (data) {
|
if (data !== null && data !== undefined) {
|
||||||
toast.success('Статус апелляции успешно изменен');
|
toast.success('Статус апелляции успешно изменен');
|
||||||
queryClient.invalidateQueries({ queryKey: ['contentForAppeals'] });
|
queryClient.invalidateQueries({ queryKey: ['contentForAppeals'] });
|
||||||
onClose();
|
onClose();
|
||||||
@@ -66,19 +79,21 @@ export default function AppealModerationModal({ file, onClose }: AppealModeratio
|
|||||||
onError: () => {
|
onError: () => {
|
||||||
toast.error('Произошла ошибка при изменении статуса');
|
toast.error('Произошла ошибка при изменении статуса');
|
||||||
},
|
},
|
||||||
}); */
|
});
|
||||||
|
|
||||||
const handleStatusChange = () => {
|
const handleStatusChange = () => {
|
||||||
if (!file?.appealId) {
|
if (!file?.appealId) {
|
||||||
toast.error('ID апелляции не найден');
|
toast.error('ID апелляции не найден');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const statusValue = getStatusValue(selectedStatus);
|
||||||
|
const approve = statusValue === 'APPROVED';
|
||||||
|
|
||||||
/* changeStatusMutation.mutate({
|
changeStatusMutation.mutate({
|
||||||
appealId: file.appealId,
|
appealId: file.appealId,
|
||||||
status: selectedStatus,
|
approve: approve,
|
||||||
comment: adminComment,
|
comment: adminComment,
|
||||||
}); */
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!file) return null;
|
if (!file) return null;
|
||||||
@@ -91,13 +106,12 @@ export default function AppealModerationModal({ file, onClose }: AppealModeratio
|
|||||||
{/* Информация о файле */}
|
{/* Информация о файле */}
|
||||||
<div className="file-moderation-modal__info-section">
|
<div className="file-moderation-modal__info-section">
|
||||||
<label className="file-moderation-modal__label">Название файла</label>
|
<label className="file-moderation-modal__label">Название файла</label>
|
||||||
{/* @ts-ignore */}
|
|
||||||
<p className="file-moderation-modal__value">{file?.fileName ? file?.fileName : '---'}</p>
|
<p className="file-moderation-modal__value">{file?.fileName ? file?.fileName : '---'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="file-moderation-modal__info-section">
|
<div className="file-moderation-modal__info-section">
|
||||||
<label className="file-moderation-modal__label">User ID</label>
|
<label className="file-moderation-modal__label">User ID</label>
|
||||||
{/* @ts-ignore */}
|
{/* @ts-ignore сейчас нету айди юзера возможно нужно будет удалить или добавить*/}
|
||||||
<p className="file-moderation-modal__value">{file?.userId ? file?.userId : '---'}</p>
|
<p className="file-moderation-modal__value">{file?.userId ? file?.userId : '---'}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -151,8 +165,8 @@ export default function AppealModerationModal({ file, onClose }: AppealModeratio
|
|||||||
value={getLabel(selectedStatus)}
|
value={getLabel(selectedStatus)}
|
||||||
callBack={(value: string) => setSelectedStatus(value)}
|
callBack={(value: string) => setSelectedStatus(value)}
|
||||||
>
|
>
|
||||||
<li value="MODERATION">На модерации</li>
|
{/* <li value="MODERATION">На модерации</li> */}
|
||||||
<li value="BLOCKED">Заблокировано</li>
|
<li value="REJECTED">Заблокировано</li>
|
||||||
<li value="APPROVED">Одобрено</li>
|
<li value="APPROVED">Одобрено</li>
|
||||||
</DropDownList>
|
</DropDownList>
|
||||||
</div>
|
</div>
|
||||||
@@ -162,17 +176,16 @@ export default function AppealModerationModal({ file, onClose }: AppealModeratio
|
|||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="btn btn-secondary"
|
className="btn btn-secondary"
|
||||||
/* disabled={changeStatusMutation.isPending} */
|
disabled={changeStatusMutation.isPending}
|
||||||
>
|
>
|
||||||
Отмена
|
Отмена
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleStatusChange}
|
onClick={handleStatusChange}
|
||||||
className="btn btn-primary"
|
className="btn btn-primary"
|
||||||
/* disabled={changeStatusMutation.isPending || selectedStatus === file.status} */
|
disabled={changeStatusMutation.isPending || selectedStatus === getLabel(file.status)}
|
||||||
>
|
>
|
||||||
{/* {changeStatusMutation.isPending ? 'Сохранение...' : 'Сохранить'} */}
|
{changeStatusMutation.isPending ? 'Сохранение...' : 'Сохранить'}
|
||||||
Сохранить
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ const cutFileName = (fileName: string) => {
|
|||||||
|
|
||||||
const StatusBadge = ({ status }: { status: string }) => {
|
const StatusBadge = ({ status }: { status: string }) => {
|
||||||
const statusConfig: Record<string, { className: string; label: string }> = {
|
const statusConfig: Record<string, { className: string; label: string }> = {
|
||||||
'BLOCKED': { className: 'bg-red-100 text-red-800', label: 'Заблокировано' },
|
'REJECTED': { className: 'bg-red-100 text-red-800', label: 'Заблокировано' },
|
||||||
'MODERATION': { className: 'bg-yellow-100 text-yellow-800', label: 'На модерации' },
|
'MODERATION': { className: 'bg-yellow-100 text-yellow-800', label: 'На модерации' },
|
||||||
'APPROVED': { className: 'bg-green-100 text-green-800', label: 'Одобрено' },
|
'APPROVED': { className: 'bg-green-100 text-green-800', label: 'Одобрено' },
|
||||||
};
|
};
|
||||||
@@ -168,11 +168,11 @@ export default function ContentAppealsTable({ permission }: { permission: number
|
|||||||
const columns = useMemo<ColumnDef<AppealsFile>[]>(() => {
|
const columns = useMemo<ColumnDef<AppealsFile>[]>(() => {
|
||||||
const allColumns: ColumnDef<AppealsFile>[] = [
|
const allColumns: ColumnDef<AppealsFile>[] = [
|
||||||
{
|
{
|
||||||
accessorKey: 'fileId',
|
accessorKey: 'fileName',
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<div className="column start">
|
<div className="column start">
|
||||||
<span>
|
<span>
|
||||||
fileId
|
fileName
|
||||||
</span>
|
</span>
|
||||||
<button
|
<button
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
||||||
@@ -189,11 +189,12 @@ export default function ContentAppealsTable({ permission }: { permission: number
|
|||||||
),
|
),
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex items-center space-x-2 table-item table-item-file-name">
|
<div className="flex items-center space-x-2 table-item table-item-file-name">
|
||||||
<FileTypeIcon type={row.original.fileId} />
|
<FileTypeIcon type={row.original.fileName} />
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium w-full truncate" title={row.original.fileId}>
|
<div className="font-medium w-full truncate" title={row.original.fileName}>
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{cutFileName(row.original.fileId)}
|
{/* {cutFileName(row.original.fileName)} */}
|
||||||
|
{row.original.fileName}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -419,7 +420,7 @@ export default function ContentAppealsTable({ permission }: { permission: number
|
|||||||
<span className="pagination-info-pages">
|
<span className="pagination-info-pages">
|
||||||
{t('page')}{' '}
|
{t('page')}{' '}
|
||||||
<strong>
|
<strong>
|
||||||
{(appealsData?.currentPage ?? 0)} {t('out-of')} {appealsData?.totalPages ?? 1}
|
{(appealsData?.currentPage ?? 0) + 1} {t('out-of')} {appealsData?.totalPages ?? 1}
|
||||||
</strong>
|
</strong>
|
||||||
</span>
|
</span>
|
||||||
<span className="pagination-info-files">
|
<span className="pagination-info-files">
|
||||||
|
|||||||
Reference in New Issue
Block a user