import { Button, Card, Flex, Popconfirm, Segmented, Table, TableProps, Tooltip, Typography, } from 'antd'; import { useEffect, useState } from 'react'; import { colors } from '@/styles/colors'; import { AppstoreOutlined, BarsOutlined, CloudDownloadOutlined, DeleteOutlined, ExclamationCircleFilled, ExclamationCircleOutlined, } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import { durationDateFormat } from '@utils/durationDateFormat'; import { DEFAULT_PAGE_SIZE, IconsMap } from '@/shared/constants'; import { IProjectAttachmentsViewModel, ITaskAttachmentViewModel, } from '@/types/tasks/task-attachment-view-model'; import { useAppSelector } from '@/hooks/useAppSelector'; import { attachmentsApiService } from '@/api/attachments/attachments.api.service'; import logger from '@/utils/errorLogger'; import { evt_project_files_visit } from '@/shared/worklenz-analytics-events'; import { useMixpanelTracking } from '@/hooks/useMixpanelTracking'; const ProjectViewFiles = () => { const { t } = useTranslation('project-view-files'); const { trackMixpanelEvent } = useMixpanelTracking(); const { projectId, refreshTimestamp } = useAppSelector(state => state.projectReducer); const [attachments, setAttachments] = useState({}); const [loading, setLoading] = useState(false); const [downloading, setDownloading] = useState(false); const [paginationConfig, setPaginationConfig] = useState({ total: 0, pageIndex: 1, showSizeChanger: true, defaultPageSize: DEFAULT_PAGE_SIZE, }); const fetchAttachments = async () => { if (!projectId) return; try { setLoading(true); const response = await attachmentsApiService.getProjectAttachments( projectId, paginationConfig.pageIndex, paginationConfig.defaultPageSize ); if (response.done) { setAttachments(response.body || {}); setPaginationConfig(prev => ({ ...prev, total: response.body?.total || 0 })); } } catch (error) { logger.error('Error fetching project attachments', error); } finally { setLoading(false); } }; useEffect(() => { fetchAttachments(); }, [refreshTimestamp]); const getFileTypeIcon = (type: string | undefined) => { if (!type) return IconsMap['search']; return IconsMap[type as string] || IconsMap['search']; }; const downloadAttachment = async (id: string | undefined, filename: string | undefined) => { if (!id || !filename) return; try { setDownloading(true); const response = await attachmentsApiService.downloadAttachment(id, filename); if (response.done) { const link = document.createElement('a'); link.href = response.body || ''; link.download = filename; link.click(); link.remove(); } } catch (error) { logger.error('Error downloading attachment', error); } finally { setDownloading(false); } }; const deleteAttachment = async (id: string | undefined) => { if (!id) return; try { const response = await attachmentsApiService.deleteAttachment(id); if (response.done) { fetchAttachments(); } } catch (error) { logger.error('Error deleting attachment', error); } }; const openAttachment = (url: string | undefined) => { if (!url) return; const a = document.createElement('a'); a.href = url; a.target = '_blank'; a.style.display = 'none'; a.click(); }; useEffect(() => { trackMixpanelEvent(evt_project_files_visit); fetchAttachments(); }, [paginationConfig.pageIndex, projectId]); const columns: TableProps['columns'] = [ { key: 'fileName', title: t('nameColumn'), render: (record: ITaskAttachmentViewModel) => ( openAttachment(record.url)} > {t('fileIconAlt')} [{record.task_key}] {record.name} ), }, { key: 'attachedTask', title: t('attachedTaskColumn'), render: (record: ITaskAttachmentViewModel) => ( openAttachment(record.url)}> {record.task_name} ), }, { key: 'size', title: t('sizeColumn'), render: (record: ITaskAttachmentViewModel) => ( openAttachment(record.url)}> {record.size} ), }, { key: 'uploadedBy', title: t('uploadedByColumn'), render: (record: ITaskAttachmentViewModel) => ( openAttachment(record.url)}> {record.uploader_name} ), }, { key: 'uploadedAt', title: t('uploadedAtColumn'), render: (record: ITaskAttachmentViewModel) => ( openAttachment(record.url)}> {durationDateFormat(record.created_at)} ), }, { key: 'actionBtns', width: 80, render: (record: ITaskAttachmentViewModel) => ( } okText={t('deleteConfirmationOk')} cancelText={t('deleteConfirmationCancel')} onConfirm={() => deleteAttachment(record.id)} >