diff --git a/public/images/no-image.png b/public/images/no-image.png
new file mode 100644
index 0000000..942404a
Binary files /dev/null and b/public/images/no-image.png differ
diff --git a/src/app/actions/contentActions.ts b/src/app/actions/contentActions.ts
index 55604cc..a8015ef 100644
--- a/src/app/actions/contentActions.ts
+++ b/src/app/actions/contentActions.ts
@@ -5,16 +5,6 @@ import { API_BASE_URL } from '@/app/actions/definitions';
export async function fetchModerationContentList(page?: number, size?: number, sortBy?: string, sortDirection?: 'asc' | 'desc' | string) {
const token = await getSessionData('token');
- console.log('fetchModerationContentList');
-/* console.log(
- {
- action: 'files_for_moderation',
- page: page,
- page_size: size,
- sort_by: sortBy || '',
- sort_direction: sortDirection || 'asc',
- }
- ) */
try {
const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
@@ -26,11 +16,8 @@ export async function fetchModerationContentList(page?: number, size?: number, s
action: 'files_for_moderation',
page: page,
page_size: size,
-/* sort_by: sortBy || '',
- sort_order: sortDirection || 'asc', */
- /* full_name: nameQuery, */
- /* fileName: '',
- userId: '' */
+ sort_by: sortBy || '',
+ sort_order: sortDirection || 'asc',
}
}),
headers: {
@@ -42,7 +29,6 @@ export async function fetchModerationContentList(page?: number, size?: number, s
if (response.ok) {
const parsed = await response.json();
- console.log(parsed);
if (parsed.message_code === 0) {
return parsed.message_body.message_body;
@@ -60,10 +46,9 @@ export async function fetchModerationContentList(page?: number, size?: number, s
export async function changeModerationContentStatus(fileId?: string, fileStatus?: string) {
const token = await getSessionData('token');
- console.log(`changeModerationContentStatus - ${fileId} - ${fileStatus}`);
try {
- const response = await fetch(`${API_BASE_URL}/api/admin/info`, {
+ const response = await fetch(`${API_BASE_URL}/api/admin/control`, {
method: 'POST',
body: JSON.stringify({
version: 1,
diff --git a/src/app/components/FileTypeIcon.tsx b/src/app/components/FileTypeIcon.tsx
new file mode 100644
index 0000000..3b57350
--- /dev/null
+++ b/src/app/components/FileTypeIcon.tsx
@@ -0,0 +1,22 @@
+import { IconImageFile, IconVideoFile, IconAudioFile, IconDocument } from '@/app/ui/icons/icons';
+
+export const FileTypeIcon = ({ type }: { type: string }) => {
+ switch (type) {
+ case 'image':
+ return
+
+ ;
+ case 'video':
+ return
+
+ ;
+ case 'audio':
+ return
+
+ ;
+ default:
+ return
+
+ ;
+ }
+};
\ No newline at end of file
diff --git a/src/app/hooks/react-query/useContentForModeration.ts b/src/app/hooks/react-query/useContentForModeration.ts
index d4d5077..91d2ed9 100644
--- a/src/app/hooks/react-query/useContentForModeration.ts
+++ b/src/app/hooks/react-query/useContentForModeration.ts
@@ -10,6 +10,8 @@ export interface ModerationFile {
userId: number;
fileId: string;
status: 'BLOCKED' | 'MODERATION' | string;
+ image: string;
+ downloadUrl: string;
}
export interface ContentForModeration {
diff --git a/src/app/lib/getFileType.ts b/src/app/lib/getFileType.ts
new file mode 100644
index 0000000..450badd
--- /dev/null
+++ b/src/app/lib/getFileType.ts
@@ -0,0 +1,34 @@
+export const getFileType = (fileName: string): 'image' | 'video' | 'audio' | 'document' | 'unknown' => {
+ const lastDotIndex = fileName.lastIndexOf('.');
+
+ if (lastDotIndex <= 0) {
+ return 'unknown';
+ }
+
+ const extension = fileName.substring(lastDotIndex + 1).toLowerCase();
+
+ const allowedExtensions = {
+ images: ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'svg', 'ico'],
+ videos: ['mp4', 'webm', 'ogg', 'mov', 'avi', 'mkv', 'flv', 'wmv'],
+ audios: ['mp3', 'wav', 'ogg', 'flac', 'aac', 'm4a', 'wma'],
+ documents: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf', 'csv', 'md']
+ };
+
+ if (allowedExtensions.images.includes(extension)) {
+ return 'image';
+ }
+
+ if (allowedExtensions.videos.includes(extension)) {
+ return 'video';
+ }
+
+ if (allowedExtensions.audios.includes(extension)) {
+ return 'audio';
+ }
+
+ if (allowedExtensions.documents.includes(extension)) {
+ return 'document';
+ }
+
+ return 'unknown';
+}
\ No newline at end of file
diff --git a/src/app/styles/global-styles.scss b/src/app/styles/global-styles.scss
index 6e8f9e1..87f4b79 100644
--- a/src/app/styles/global-styles.scss
+++ b/src/app/styles/global-styles.scss
@@ -356,7 +356,7 @@
flex-wrap: wrap;
gap: 12px;
-/* &-actions {
+ /* &-actions {
display: flex;
gap: 12px;
flex-wrap: wrap;
@@ -1199,6 +1199,22 @@
}
}
}
+
+ .color-image {
+ color: v.$color-image;
+ }
+
+ .color-video {
+ color: v.$color-video;
+ }
+
+ .color-audio {
+ color: v.$color-audio;
+ }
+
+ .color-document {
+ color: v.$color-document;
+ }
}
.empty-state {
diff --git a/src/app/ui/content/FileModerationModal.tsx b/src/app/ui/content/FileModerationModal.tsx
index d03f16a..7e1c791 100644
--- a/src/app/ui/content/FileModerationModal.tsx
+++ b/src/app/ui/content/FileModerationModal.tsx
@@ -12,6 +12,19 @@ interface FileModerationModalProps {
onClose: () => void;
}
+const getLabel = (status: string) => {
+ switch (status) {
+ case 'BLOCKED':
+ return 'Заблокировано';
+ case 'MODERATION':
+ return 'На модерации';
+ case 'ACTIVE':
+ return 'Одобрено';
+ default:
+ return status;
+ }
+};
+
const StatusBadge = ({ status }: { status: string }) => {
const getBadgeClass = () => {
switch (status) {
@@ -19,29 +32,15 @@ const StatusBadge = ({ status }: { status: string }) => {
return 'status-badge--blocked';
case 'MODERATION':
return 'status-badge--moderation';
- case 'APPROVED':
+ case 'ACTIVE':
return 'status-badge--approved';
default:
return 'status-badge--default';
}
};
-
- const getLabel = () => {
- switch (status) {
- case 'BLOCKED':
- return 'Заблокировано';
- case 'MODERATION':
- return 'На модерации';
- case 'APPROVED':
- return 'Одобрено';
- default:
- return status;
- }
- };
-
return (
- {getLabel()}
+ {getLabel(status)}
);
};
@@ -107,11 +106,12 @@ export default function FileModerationModal({ file, onClose }: FileModerationMod
setSelectedStatus(value)}
>
На модерации
Заблокировано
+ Одобрено
diff --git a/src/app/ui/content/content-moderation-table.tsx b/src/app/ui/content/content-moderation-table.tsx
index d448359..c430e1f 100644
--- a/src/app/ui/content/content-moderation-table.tsx
+++ b/src/app/ui/content/content-moderation-table.tsx
@@ -11,7 +11,7 @@ import {
SortingState,
ColumnFiltersState,
} from '@tanstack/react-table';
-import { IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter } from '@/app/ui/icons/icons';
+import { IconEye, IconDoubleArrowRight, IconArrowRight, IconDoubleArrowLeft, IconArrowLeft, IconArrowUp, IconArrowDown, IconFilter, IconFileDownload } from '@/app/ui/icons/icons';
import { useTranslations } from 'next-intl';
import DropDownList from '@/app/components/dropDownList';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
@@ -19,6 +19,15 @@ import ModalWindow from '@/app/components/modalWindow';
import { toast } from 'sonner';
import { useContentForModeration, ModerationFile, ContentForModeration } from '@/app/hooks/react-query/useContentForModeration';
import FileModerationModal from './FileModerationModal';
+import { FileTypeIcon } from '@/app/components/FileTypeIcon';
+import { getFileType } from '@/app/lib/getFileType';
+import Image from 'next/image';
+
+const cutFileExtension = (fileName: string) => {
+ const lastDotIndex = fileName.lastIndexOf('.');
+ const extension = fileName.substring(lastDotIndex + 1);
+ return extension;
+}
const cutFileName = (fileName: string) => {
const MAX_FILE_NAME_LENGTH = 30;
@@ -69,8 +78,8 @@ export default function ContentModerationTable({ permission }: { permission: num
pageSize: 10,
});
const [openWindow, setOpenWindow] = useState(false);
- const [openWindowChildren, setOpenWindowChildren] = useState(null);
const [selectedFile, setSelectedFile] = useState(null);
+ const [isFileLoading, setIsFileLoading] = useState(false);
const t = useTranslations("Global");
@@ -82,7 +91,7 @@ export default function ContentModerationTable({ permission }: { permission: num
const sort = sorting[0];
const sortByMap: Record = {
- 'fileName': 'fileName',
+ 'fileName': 'originalFileName',
'userId': 'userId',
'status': 'status',
'createdAt': 'createdAt',
@@ -124,6 +133,36 @@ export default function ContentModerationTable({ permission }: { permission: num
setSelectedFile(null);
}, []);
+ const handleDownload = async (file: any) => {
+ setIsFileLoading(true);
+ console.log(file.downloadUrl);
+
+ try {
+ const response = await fetch(file.downloadUrl);
+
+ if (!response.ok) {
+ throw new Error('Download failed');
+ }
+
+ const blob = await response.blob();
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = file._original?.fileName || file.fileName || `file-${file.id}`;
+ document.body.appendChild(a);
+ a.click();
+ window.URL.revokeObjectURL(url);
+ document.body.removeChild(a);
+
+ toast.success(`${t('file-is-downloading')} - ${file.fileName}`);
+ } catch (error) {
+ console.error('Download error:', error);
+ toast.error(t('failed-to-download-file'));
+ } finally {
+ setIsFileLoading(false);
+ }
+ }
+
const columns = useMemo[]>(() => {
const allColumns: ColumnDef[] = [
{
@@ -147,9 +186,32 @@ export default function ContentModerationTable({ permission }: { permission: num
),
cell: ({ row }) => (
-
-
- {cutFileName(row.original.fileName)}
+
+ {getFileType(row.original.fileName) === 'image' && row.original?.image.length !== 0 ? (
+
+ {
+ const target = e.target as HTMLImageElement;
+ target.src = '/images/no-image.png';
+ }}
+ />
+
+ ) : (
+
+ )}
+
+
+
+ {cutFileName(row.original.fileName)}
+
+
),
@@ -207,7 +269,7 @@ export default function ContentModerationTable({ permission }: { permission: num
),
},
- {
+ /* {
accessorKey: 'moderationInfo.hasActiveAppeal',
header: ({ column }) => (
@@ -236,7 +298,7 @@ export default function ContentModerationTable({ permission }: { permission: num
)}
),
- }
+ } */
];
if (permission === 3) {
@@ -253,6 +315,15 @@ export default function ContentModerationTable({ permission }: { permission: num
>
+
+
),
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index f02b0ac..142c588 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -222,7 +222,8 @@
"delete-template-warning": "Are you sure you want to delete this template?",
"save": "Save",
"delete": "Delete",
- "create-stuff": "Create stuff"
+ "create-stuff": "Create stuff",
+ "download": "Download"
},
"Login-register-form": {
"and": "and",
diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json
index db4004c..36d1bd2 100644
--- a/src/i18n/messages/ru.json
+++ b/src/i18n/messages/ru.json
@@ -222,7 +222,8 @@
"delete-template-warning": "Вы уверены, что хотите удалить шаблон?",
"save": "Сохранить",
"delete": "Удалить",
- "create-stuff": "Создать сотрудника"
+ "create-stuff": "Создать сотрудника",
+ "download": "Скачать"
},
"Login-register-form": {
"and": "и",