import React, { useState, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { TrashIcon, MagnifyingGlassIcon, StarIcon, } from '@heroicons/react/24/outline'; import { StarIcon as StarIconSolid } from '@heroicons/react/24/solid'; import ConfirmDialog from './Shared/ConfirmDialog'; import { getApiPath } from '../config/paths'; interface View { id: number; uid: string; name: string; search_query: string | null; filters: string[]; priority: string | null; due: string | null; defer: string | null; tags: string[]; extras: string[] | null; is_pinned: boolean; } const Views: React.FC = () => { const { t } = useTranslation(); const navigate = useNavigate(); const [views, setViews] = useState([]); const [isLoading, setIsLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [isSearchExpanded, setIsSearchExpanded] = useState(false); const [hoveredViewId, setHoveredViewId] = useState(null); const [isConfirmDialogOpen, setIsConfirmDialogOpen] = useState(false); const [viewToDelete, setViewToDelete] = useState(null); useEffect(() => { fetchViews(); }, []); const fetchViews = async () => { try { const response = await fetch(getApiPath('views'), { credentials: 'include', }); if (response.ok) { const data = await response.json(); const normalized: View[] = data.map((view: View) => ({ ...view, tags: view.tags || [], extras: view.extras || [], defer: view.defer || null, })); setViews(normalized); } } catch (error) { console.error('Error fetching views:', error); } finally { setIsLoading(false); } }; const handleDeleteView = async () => { if (!viewToDelete) return; try { const response = await fetch( getApiPath(`views/${viewToDelete.uid}`), { method: 'DELETE', credentials: 'include', } ); if (response.ok) { setViews(views.filter((v) => v.uid !== viewToDelete.uid)); // Notify sidebar to refresh window.dispatchEvent(new CustomEvent('viewUpdated')); } } catch (error) { console.error('Error deleting view:', error); } finally { setIsConfirmDialogOpen(false); setViewToDelete(null); } }; const togglePin = async (view: View) => { try { const response = await fetch(getApiPath(`views/${view.uid}`), { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, credentials: 'include', body: JSON.stringify({ is_pinned: !view.is_pinned, }), }); if (response.ok) { fetchViews(); // Notify sidebar to refresh window.dispatchEvent(new CustomEvent('viewUpdated')); } } catch (error) { console.error('Error toggling pin:', error); } }; const openConfirmDialog = (view: View) => { setViewToDelete(view); setIsConfirmDialogOpen(true); }; const closeConfirmDialog = () => { setIsConfirmDialogOpen(false); setViewToDelete(null); }; const filteredViews = views.filter((view) => view.name.toLowerCase().includes(searchQuery.toLowerCase()) ); if (isLoading) { return (
{t('views.loading')}
); } return (
{/* Views Header */}

{t('views.title')}

{/* Search input section, collapsible */}
setSearchQuery(e.target.value)} className="w-full bg-transparent border-none focus:ring-0 focus:outline-none dark:text-white" />
{/* Views List */} {filteredViews.length === 0 ? (

{t('views.noViewsFound')}

) : (
    {filteredViews.map((view) => (
  • setHoveredViewId(view.id) } onMouseLeave={() => setHoveredViewId(null)} onClick={() => navigate(`/views/${view.uid}`) } >

    {view.name}

    {view.filters.length > 0 && (

    •{' '} {view.filters.join( ', ' )}

    )} {view.search_query && (

    • " {view.search_query} "

    )} {view.priority && (

    •{' '} {t( 'views.priorityLabel' )}{' '} {view.priority}

    )} {view.due && (

    • {t('views.dueLabel')}{' '} {view.due}

    )} {view.defer && (

    •{' '} {t('search.deferUntil')}{' '} {view.defer}

    )} {view.extras && view.extras.length > 0 && (

    •{' '} {t('search.extras')} :{' '} {view.extras.join( ', ' )}

    )}
    {/* Action buttons */}
  • ))}
)} {/* ConfirmDialog */} {isConfirmDialogOpen && viewToDelete && ( )}
); }; export default Views;