Open all content filtered to the activity tab you were on

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Iqbal
2026-10-29 12:49:07 +07:00
parent d00561f75d
commit 5bc0915edf
3 changed files with 15 additions and 7 deletions

View File

@@ -300,6 +300,7 @@ const en = {
'res.filterStatus': 'Filter by status', 'res.filterStatus': 'Filter by status',
'res.allKinds': 'All types', 'res.allKinds': 'All types',
'res.status.all': 'All statuses', 'res.status.all': 'All statuses',
'res.status.done': 'Finished',
'res.status.review': 'Needs review', 'res.status.review': 'Needs review',
'res.status.approved': 'Approved', 'res.status.approved': 'Approved',
'res.status.working': 'In progress', 'res.status.working': 'In progress',
@@ -797,6 +798,7 @@ const id: Record<Key, string> = {
'res.filterStatus': 'Filter status', 'res.filterStatus': 'Filter status',
'res.allKinds': 'Semua jenis', 'res.allKinds': 'Semua jenis',
'res.status.all': 'Semua status', 'res.status.all': 'Semua status',
'res.status.done': 'Selesai dibuat',
'res.status.review': 'Perlu ditinjau', 'res.status.review': 'Perlu ditinjau',
'res.status.approved': 'Disetujui', 'res.status.approved': 'Disetujui',
'res.status.working': 'Sedang dibuat', 'res.status.working': 'Sedang dibuat',

View File

@@ -149,7 +149,7 @@ function ContentActivity({ made, working, loading }: { made: Material[]; working
<section className="prod-activity"> <section className="prod-activity">
<div className="section-heading"> <div className="section-heading">
<div><h3>{t('prod.activityTitle')}</h3><p>{t('prod.activityDescription')}</p></div> <div><h3>{t('prod.activityTitle')}</h3><p>{t('prod.activityDescription')}</p></div>
<Link className="text-btn" to="/results">{t('prod.activityAll')}<Icon name="arrow-right" /></Link> <Link className="text-btn" to={`/results?status=${tab === 'latest' ? 'done' : 'working'}`}>{t('prod.activityAll')}<Icon name="arrow-right" /></Link>
</div> </div>
<div className="result-tabs" role="tablist"> <div className="result-tabs" role="tablist">
<button role="tab" aria-selected={tab === 'latest'} className={tab === 'latest' ? 'active' : undefined} onClick={() => setTab('latest')}> <button role="tab" aria-selected={tab === 'latest'} className={tab === 'latest' ? 'active' : undefined} onClick={() => setTab('latest')}>

View File

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react' import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
import { renderToStaticMarkup } from 'react-dom/server' import { renderToStaticMarkup } from 'react-dom/server'
import { Link } from 'react-router-dom' import { Link, useSearchParams } from 'react-router-dom'
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { AudioPlayer } from '../components/AudioPlayer' import { AudioPlayer } from '../components/AudioPlayer'
import { Icon, KIND_ICON, type IconName } from '../components/Icon' import { Icon, KIND_ICON, type IconName } from '../components/Icon'
@@ -19,10 +19,15 @@ const STUDIO_URL: string = import.meta.env.VITE_STUDIO_URL
// Setup fields of roleplay and simulator content, in display order. // Setup fields of roleplay and simulator content, in display order.
const FIELDS = ['description', 'topic', 'objective', 'learning_objectives', 'user_role', 'ai_character', 'difficulty', 'context'] as const const FIELDS = ['description', 'topic', 'objective', 'learning_objectives', 'user_role', 'ai_character', 'difficulty', 'context'] as const
type StatusFilter = 'all' | 'review' | 'approved' | 'working' | 'failed' // 'done' is everything no longer being made except failures, as the Production page counts it; the
const STATUS_GROUP: Record<MaterialStatus, StatusFilter> = { // Production page links here with ?status=done or ?status=working.
const STATUS_FILTERS = ['all', 'done', 'review', 'approved', 'working', 'failed'] as const
type StatusFilter = typeof STATUS_FILTERS[number]
const STATUS_GROUP: Record<MaterialStatus, Exclude<StatusFilter, 'all' | 'done'>> = {
preparing: 'working', generating: 'working', ready: 'review', accepted: 'approved', failed: 'failed', rejected: 'failed', preparing: 'working', generating: 'working', ready: 'review', accepted: 'approved', failed: 'failed', rejected: 'failed',
} }
const matchesStatus = (m: Material, status: StatusFilter) =>
status === 'all' || (status === 'done' ? !isWorking(m) && m.status !== 'failed' : STATUS_GROUP[m.status] === status)
const fmt = (iso: string) => new Intl.DateTimeFormat(locale(), { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(iso)) const fmt = (iso: string) => new Intl.DateTimeFormat(locale(), { day: 'numeric', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }).format(new Date(iso))
@@ -35,14 +40,15 @@ export function ResultsPage() {
const [tab, setTab] = useState<'content' | 'research'>('content') const [tab, setTab] = useState<'content' | 'research'>('content')
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
const [kind, setKind] = useState<ContentKind | 'all'>('all') const [kind, setKind] = useState<ContentKind | 'all'>('all')
const [status, setStatus] = useState<StatusFilter>('all') const [params] = useSearchParams()
const [status, setStatus] = useState<StatusFilter>(() => STATUS_FILTERS.find(s => s === params.get('status')) ?? 'all')
const [openId, setOpenId] = useState<string | null>(null) const [openId, setOpenId] = useState<string | null>(null)
const materials = results.data?.materials ?? [] const materials = results.data?.materials ?? []
const sources = results.data?.sources ?? [] const sources = results.data?.sources ?? []
const kinds = useMemo(() => [...new Set(materials.map(m => m.kind))], [materials]) const kinds = useMemo(() => [...new Set(materials.map(m => m.kind))], [materials])
const needle = query.trim().toLowerCase() const needle = query.trim().toLowerCase()
const shownMaterials = materials.filter(m => (kind === 'all' || m.kind === kind) && (status === 'all' || STATUS_GROUP[m.status] === status) const shownMaterials = materials.filter(m => (kind === 'all' || m.kind === kind) && matchesStatus(m, status)
&& (!needle || titleOf(m).toLowerCase().includes(needle))) && (!needle || titleOf(m).toLowerCase().includes(needle)))
const shownSources = sources.filter(s => !needle || s.title.toLowerCase().includes(needle) || s.url?.toLowerCase().includes(needle)) const shownSources = sources.filter(s => !needle || s.title.toLowerCase().includes(needle) || s.url?.toLowerCase().includes(needle))
const open = materials.find(m => materialKey(m) === openId) ?? null const open = materials.find(m => materialKey(m) === openId) ?? null
@@ -73,7 +79,7 @@ export function ResultsPage() {
{kinds.map(k => <option key={k} value={k}>{kindLabel(k)}</option>)} {kinds.map(k => <option key={k} value={k}>{kindLabel(k)}</option>)}
</select> </select>
<select aria-label={t('res.filterStatus')} value={status} onChange={e => setStatus(e.target.value as StatusFilter)}> <select aria-label={t('res.filterStatus')} value={status} onChange={e => setStatus(e.target.value as StatusFilter)}>
{(['all', 'review', 'approved', 'working', 'failed'] as const).map(s => <option key={s} value={s}>{t(`res.status.${s}` as Key)}</option>)} {STATUS_FILTERS.map(s => <option key={s} value={s}>{t(`res.status.${s}` as Key)}</option>)}
</select> </select>
</> </>
)} )}