import dayjs from "dayjs"; import { includes } from "lodash-es"; import { PaperclipIcon, SearchIcon, TrashIcon } from "lucide-react"; import { observer } from "mobx-react-lite"; import { useEffect, useState } from "react"; import AttachmentIcon from "@/components/AttachmentIcon"; import Empty from "@/components/Empty"; import MobileHeader from "@/components/MobileHeader"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import { attachmentServiceClient } from "@/grpcweb"; import useLoading from "@/hooks/useLoading"; import useResponsiveWidth from "@/hooks/useResponsiveWidth"; import i18n from "@/i18n"; import { memoStore } from "@/store"; import { Attachment } from "@/types/proto/api/v1/attachment_service"; import { useTranslate } from "@/utils/i18n"; function groupAttachmentsByDate(attachments: Attachment[]) { const grouped = new Map(); attachments .sort((a, b) => dayjs(b.createTime).unix() - dayjs(a.createTime).unix()) .forEach((item) => { const monthStr = dayjs(item.createTime).format("YYYY-MM"); if (!grouped.has(monthStr)) { grouped.set(monthStr, []); } grouped.get(monthStr)?.push(item); }); return grouped; } interface State { searchQuery: string; } const Attachments = observer(() => { const t = useTranslate(); const { md } = useResponsiveWidth(); const loadingState = useLoading(); const [state, setState] = useState({ searchQuery: "", }); const [attachments, setAttachments] = useState([]); const filteredAttachments = attachments.filter((attachment) => includes(attachment.filename, state.searchQuery)); const groupedAttachments = groupAttachmentsByDate(filteredAttachments.filter((attachment) => attachment.memo)); const unusedAttachments = filteredAttachments.filter((attachment) => !attachment.memo); useEffect(() => { attachmentServiceClient.listAttachments({}).then(({ attachments }) => { setAttachments(attachments); loadingState.setFinish(); Promise.all(attachments.map((attachment) => (attachment.memo ? memoStore.getOrFetchMemoByName(attachment.memo) : null))); }); }, []); const handleDeleteUnusedAttachments = async () => { const confirmed = window.confirm("Are you sure to delete all unused attachments? This action cannot be undone."); if (confirmed) { for (const attachment of unusedAttachments) { await attachmentServiceClient.deleteAttachment({ name: attachment.name }); } setAttachments(attachments.filter((attachment) => attachment.memo)); } }; return (
{!md && }

{t("common.attachments")}

setState({ ...state, searchQuery: e.target.value })} />
{loadingState.isLoading ? (

{t("resource.fetching-data")}

) : ( <> {filteredAttachments.length === 0 ? (

{t("message.no-data")}

) : (
{Array.from(groupedAttachments.entries()).map(([monthStr, attachments]) => { return (
{dayjs(monthStr).year()} {dayjs(monthStr).toDate().toLocaleString(i18n.language, { month: "short" })}
{attachments.map((attachment) => { return (

{attachment.filename}

); })}
); })} {unusedAttachments.length > 0 && ( <>
{t("resource.unused-resources")} ({unusedAttachments.length})

Delete all

{unusedAttachments.map((attachment) => { return (

{attachment.filename}

); })}
)}
)} )}
); }); export default Attachments;