MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m39s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
95 lines
3.9 KiB
TypeScript
95 lines
3.9 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { Loader2, RotateCcw, Trash2 } from 'lucide-react';
|
|
import { api, triggerHaptic } from '../../lib/api';
|
|
import { formatDeletedAt } from '../../lib/due';
|
|
import type { Thought } from '../../lib/types';
|
|
import { ReadingModal } from '../../components/ReadingModal';
|
|
|
|
interface TriageTrashProps {
|
|
onClose: () => void;
|
|
// Something was restored or deleted: counters and the deck change
|
|
onChanged: () => void;
|
|
}
|
|
|
|
// The triage's own trash: thoughts thrown away while sorting. Back into the Quick Dump, or deleted for
|
|
// good (one by one, all at once, or after the delay from Kontoeinstellungen → Aufbewahrung). Deleting
|
|
// for good takes a second tap on the same button, like the notes trash.
|
|
export const TriageTrash: React.FC<TriageTrashProps> = ({ onClose, onChanged }) => {
|
|
const [items, setItems] = useState<Thought[] | null>(null);
|
|
const [confirmId, setConfirmId] = useState<string | null>(null);
|
|
|
|
const load = async () => {
|
|
const list = await api.getThoughts('discarded').catch(() => []);
|
|
setItems(Array.isArray(list) ? list : []);
|
|
};
|
|
|
|
useEffect(() => {
|
|
void load();
|
|
}, []);
|
|
|
|
const restore = async (item: Thought) => {
|
|
triggerHaptic(15);
|
|
await api.triageAction(item.id, 'restore');
|
|
await load();
|
|
onChanged();
|
|
};
|
|
|
|
const purge = async (id: string) => {
|
|
if (confirmId !== id) {
|
|
setConfirmId(id);
|
|
return;
|
|
}
|
|
triggerHaptic(20);
|
|
setConfirmId(null);
|
|
if (id === '__all__') await api.purgeDiscarded();
|
|
else await api.deleteThought(id);
|
|
await load();
|
|
onChanged();
|
|
};
|
|
|
|
return (
|
|
<ReadingModal title="Papierkorb der Triage" onClose={onClose}>
|
|
{items === null ? (
|
|
<div className="py-6 flex justify-center text-muted">
|
|
<Loader2 className="w-5 h-5 animate-spin" />
|
|
</div>
|
|
) : items.length === 0 ? (
|
|
<p className="py-6 text-center text-muted">Keine verworfenen Gedanken.</p>
|
|
) : (
|
|
<div className="space-y-3">
|
|
<div className="divide-y divide-border border border-border rounded-card overflow-hidden">
|
|
{items.map((item) => (
|
|
<div key={item.id} className="px-3 py-2 flex items-center justify-between gap-3">
|
|
<div className="min-w-0 flex-1">
|
|
<p className="text-sm text-main truncate">{item.content}</p>
|
|
{item.triagedAt && <p className="text-[11px] text-faint">verworfen {formatDeletedAt(item.triagedAt)}</p>}
|
|
</div>
|
|
<div className="flex items-center gap-2 flex-shrink-0">
|
|
<button type="button" onClick={() => restore(item)} className="btn-action min-h-[38px] sm:min-h-0">
|
|
<RotateCcw className="w-3 h-3 text-accent" /> Zurück
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() => purge(item.id)}
|
|
className={`btn-icon btn-icon-danger min-h-[38px] min-w-[38px] sm:min-h-0 sm:min-w-0 ${confirmId === item.id ? 'text-status-err' : ''}`}
|
|
title={confirmId === item.id ? 'Wirklich endgültig löschen?' : 'Endgültig löschen'}
|
|
aria-label="Endgültig löschen"
|
|
>
|
|
{confirmId === item.id ? <span className="text-[11px] font-semibold px-1">Wirklich?</span> : <Trash2 className="w-3.5 h-3.5" />}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="flex justify-end">
|
|
<button type="button" onClick={() => purge('__all__')} className="btn-action min-h-[38px] sm:min-h-0 text-status-err">
|
|
<Trash2 className="w-3.5 h-3.5" />
|
|
{confirmId === '__all__' ? `Wirklich ${items.length} endgültig löschen?` : 'Papierkorb leeren'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</ReadingModal>
|
|
);
|
|
};
|