-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSnfArchivePage.jsx
More file actions
157 lines (146 loc) · 5.75 KB
/
Copy pathSnfArchivePage.jsx
File metadata and controls
157 lines (146 loc) · 5.75 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
import React, { useEffect, useMemo, useState } from 'react';
import { Link } from 'react-router-dom';
import {
ArchiveIcon,
FolderOpenIcon,
CaretRightIcon,
} from '@phosphor-icons/react';
import SnfLayout from '../../components/snf/SnfLayout';
import {
SnfPageHeader,
SnfSearchInput,
SnfReveal,
SnfEmpty,
} from '../../components/snf/SnfPrimitives';
import Seo from '../../components/Seo';
import { useSnf } from '../../context/SnfContext';
import { useBooks } from '../../hooks/useStoriesData';
const SnfArchivePage = () => {
const { language, setBreadcrumbs } = useSnf();
const { data: books, loading } = useBooks(language);
const [query, setQuery] = useState('');
useEffect(() => {
setBreadcrumbs([{ label: 'ARCHIVE' }]);
}, [setBreadcrumbs]);
const filtered = useMemo(() => {
const term = query.toLowerCase();
return (books || []).filter((b) => {
const titleMatch = b.bookTitle?.toLowerCase().includes(term);
const scribeMatch = b.episodes?.some((e) =>
e.author?.toLowerCase().includes(term),
);
return titleMatch || scribeMatch;
});
}, [books, query]);
const scribesFor = (book) =>
[...new Set((book.episodes || []).map((e) => e.author))].join(' · ');
return (
<SnfLayout>
<Seo
title="The Archive | Serfs and Frauds Terminal"
description="Browse every volume and field report in the From Serfs and Frauds anthology."
keywords={['serfs and frauds', 'archive', 'volumes', 'noir']}
/>
<SnfPageHeader
fileNo="001"
label="CASE FILES // VOLUMES"
title={language === 'tr' ? 'ARŞİV' : 'THE ARCHIVE'}
subtitle={
language === 'tr'
? 'Sınıflandırılmış ciltler ve saha raporları. Bir dosya seçin.'
: 'Classified volumes and field reports. Select a file to decrypt.'
}
icon={<ArchiveIcon size={40} weight="duotone" />}
/>
<div className="max-w-xl mb-10">
<SnfSearchInput
value={query}
onChange={setQuery}
placeholder={
language === 'tr'
? 'cilt / kâtip ara…'
: 'query by volume or scribe…'
}
/>
</div>
{loading && (
<div className="snf-mono text-sm snf-dim animate-pulse">
accessing archive…
</div>
)}
<div className="space-y-4">
{filtered.map((book, i) => (
<SnfReveal key={book.bookId} delay={Math.min(i * 0.05, 0.3)}>
<div className="snf-panel snf-panel-bracket overflow-hidden">
{/* Folder tab header */}
<Link
to={`/snf/archive/${book.bookId}`}
className="group flex items-center gap-4 px-5 py-4 snf-bar hover:bg-[rgba(var(--snf-phos-rgb),0.05)] transition-colors"
>
<span className="snf-display text-2xl md:text-3xl font-bold text-[var(--snf-phos)] snf-glow-soft tabular-nums flex-none">
{String(book.bookId).padStart(2, '0')}
</span>
<span className="flex-grow min-w-0">
<span className="block snf-display text-base md:text-xl font-semibold uppercase tracking-wide text-[var(--snf-ink)] group-hover:text-[var(--snf-phos)] transition-colors truncate">
{book.bookTitle}
</span>
{book.phaseTitle && (
<span className="block snf-mono text-[10px] tracking-[0.18em] snf-dim mt-0.5 truncate">
{book.phaseTitle}
</span>
)}
</span>
<span className="hidden sm:block text-right flex-none">
<span className="block snf-display text-lg font-bold text-[var(--snf-phos)] tabular-nums">
{String(book.episodes?.length || 0).padStart(2, '0')}
</span>
<span className="block snf-mono text-[9px] tracking-[0.2em] snf-dim">
{language === 'tr' ? 'DOSYA' : 'DOSSIERS'}
</span>
</span>
<FolderOpenIcon
size={22}
weight="duotone"
className="snf-row-arrow snf-dim flex-none"
/>
</Link>
{/* Episode chips */}
<div className="px-5 py-4">
<div className="snf-mono text-[10px] tracking-[0.2em] snf-dim mb-3 truncate">
SCRIBES :: {scribesFor(book) || '—'}
</div>
<div className="flex flex-wrap gap-2">
{(book.episodes || []).map((ep) => (
<Link
key={ep.id}
to={`/snf/archive/${book.bookId}/${ep.id}`}
className="snf-row flex items-center gap-2 px-3 py-1.5 snf-mono text-[11px] text-[var(--snf-ink)] hover:text-[var(--snf-phos)] max-w-full"
>
<span className="snf-phos-text tabular-nums flex-none">
{String(ep.id).padStart(2, '0')}
</span>
<span className="truncate">{ep.title}</span>
<CaretRightIcon
size={11}
weight="bold"
className="snf-row-arrow flex-none"
/>
</Link>
))}
</div>
</div>
</div>
</SnfReveal>
))}
{!loading && filtered.length === 0 && (
<SnfEmpty>
{language === 'tr'
? 'Sorgunuzla eşleşen dosya yok.'
: 'No files match your query.'}
</SnfEmpty>
)}
</div>
</SnfLayout>
);
};
export default SnfArchivePage;