import { useEffect, useState } from 'react' import { useNavigate } from 'react-router-dom' import { Briefcase, Plus } from 'lucide-react' import { Breadcrumbs } from '../components/Breadcrumbs' import { EmptyState } from '../components/EmptyState' import { PageTitle } from '../components/PageTitle' import { PortfolioCard } from '../components/PortfolioCard' import { PortfolioCommentsModal } from '../components/PortfolioCommentsModal' import { ConfirmDeleteModal } from '../components/ConfirmDeleteModal' import { Pagination } from '../components/Pagination' import { useToast } from '../context/ToastContext' import { useT } from '../i18n/useT' import { ApiError } from '../lib/api' import { PORTFOLIOS_PER_PAGE, deletePortfolio, listPortfolios, updatePortfolio, } from '../services/portfolioService' import type { Portfolio } from '../types/portfolio' import pageStyles from '../components/PageContent.module.css' import styles from './BlogPage.module.css' export function PortfolioListPage() { const navigate = useNavigate() const { showToast } = useToast() const t = useT() const [portfolios, setPortfolios] = useState([]) const [totalPortfolios, setTotalPortfolios] = useState(0) const [currentPage, setCurrentPage] = useState(1) const [isLoading, setIsLoading] = useState(true) const [isDeleting, setIsDeleting] = useState(false) const [movingUpId, setMovingUpId] = useState(null) const [error, setError] = useState('') const [commentCounts, setCommentCounts] = useState>({}) const [deleteTarget, setDeleteTarget] = useState<{ id: string; title: string } | null>(null) const [commentsTarget, setCommentsTarget] = useState<{ id: string; title: string } | null>( null, ) const totalPages = Math.max(1, Math.ceil(totalPortfolios / PORTFOLIOS_PER_PAGE)) useEffect(() => { const controller = new AbortController() void loadPortfolios(currentPage, controller.signal) return () => controller.abort() }, [currentPage]) async function loadPortfolios(page: number, signal?: AbortSignal) { setIsLoading(true) setError('') try { const data = await listPortfolios(page, PORTFOLIOS_PER_PAGE, signal) setPortfolios(data.items) setTotalPortfolios(data.total) setCommentCounts( Object.fromEntries(data.items.map((item) => [item.id, item.commentCount ?? 0])), ) } catch (err) { if (err instanceof DOMException && err.name === 'AbortError') return if (err instanceof ApiError) { setError(err.message) } else { setError(t('portfolio.list.errorLoad')) } } finally { setIsLoading(false) } } function handleEdit(id: string) { navigate(`/portfolios/edit/${id}`) } function handleComments(id: string) { const portfolio = portfolios.find((item) => item.id === id) if (portfolio) { setCommentsTarget({ id, title: portfolio.title }) } } function handleRemoveRequest(id: string) { const portfolio = portfolios.find((item) => item.id === id) if (portfolio) { setDeleteTarget({ id, title: portfolio.title }) } } async function handleMoveUp(id: string) { const index = portfolios.findIndex((item) => item.id === id) if (index < 0) return if (index === 0 && currentPage === 1) return const current = portfolios[index] setMovingUpId(id) setError('') try { if (index === 0) { await updatePortfolio(current.id, { sortOrder: current.sortOrder - 1 }) } else { const previous = portfolios[index - 1] if (current.sortOrder === previous.sortOrder) { await updatePortfolio(current.id, { sortOrder: previous.sortOrder - 1 }) } else { await Promise.all([ updatePortfolio(current.id, { sortOrder: previous.sortOrder }), updatePortfolio(previous.id, { sortOrder: current.sortOrder }), ]) } } showToast(t('portfolio.list.toast.movedUp'), 'success') await loadPortfolios(currentPage) } catch (err) { if (err instanceof ApiError) { setError(err.message) } else { setError(t('portfolio.list.errorMove')) } } finally { setMovingUpId(null) } } async function confirmDelete() { if (!deleteTarget) return setIsDeleting(true) setError('') try { await deletePortfolio(deleteTarget.id) showToast(t('portfolio.list.toast.removed'), 'success') const nextTotal = totalPortfolios - 1 const nextTotalPages = Math.max(1, Math.ceil(nextTotal / PORTFOLIOS_PER_PAGE)) const nextPage = currentPage > nextTotalPages ? nextTotalPages : currentPage setDeleteTarget(null) setCurrentPage(nextPage) await loadPortfolios(nextPage) } catch (err) { if (err instanceof ApiError) { setError(err.message) } else { setError(t('portfolio.list.errorDelete')) } } finally { setIsDeleting(false) } } function handlePageChange(page: number) { setCurrentPage(page) window.scrollTo({ top: 0, behavior: 'smooth' }) } function handleCommentCountChange(count: number) { if (!commentsTarget) return setCommentCounts((prev) => ({ ...prev, [commentsTarget.id]: count, })) } return (
{t('title.myPortfolios')}

{t('portfolio.list.subtitle', { count: totalPortfolios })}

{error && (
{error}
)} {isLoading ? (

{t('portfolio.list.loading')}

) : portfolios.length === 0 ? ( ) : ( <>
{portfolios.map((portfolio, index) => ( ))}
)} !isDeleting && setDeleteTarget(null)} /> {commentsTarget && ( setCommentsTarget(null)} onCountChange={handleCommentCountChange} /> )}
) }