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:
@@ -300,6 +300,7 @@ const en = {
|
||||
'res.filterStatus': 'Filter by status',
|
||||
'res.allKinds': 'All types',
|
||||
'res.status.all': 'All statuses',
|
||||
'res.status.done': 'Finished',
|
||||
'res.status.review': 'Needs review',
|
||||
'res.status.approved': 'Approved',
|
||||
'res.status.working': 'In progress',
|
||||
@@ -797,6 +798,7 @@ const id: Record<Key, string> = {
|
||||
'res.filterStatus': 'Filter status',
|
||||
'res.allKinds': 'Semua jenis',
|
||||
'res.status.all': 'Semua status',
|
||||
'res.status.done': 'Selesai dibuat',
|
||||
'res.status.review': 'Perlu ditinjau',
|
||||
'res.status.approved': 'Disetujui',
|
||||
'res.status.working': 'Sedang dibuat',
|
||||
|
||||
@@ -149,7 +149,7 @@ function ContentActivity({ made, working, loading }: { made: Material[]; working
|
||||
<section className="prod-activity">
|
||||
<div className="section-heading">
|
||||
<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 className="result-tabs" role="tablist">
|
||||
<button role="tab" aria-selected={tab === 'latest'} className={tab === 'latest' ? 'active' : undefined} onClick={() => setTab('latest')}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
|
||||
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 { AudioPlayer } from '../components/AudioPlayer'
|
||||
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.
|
||||
const FIELDS = ['description', 'topic', 'objective', 'learning_objectives', 'user_role', 'ai_character', 'difficulty', 'context'] as const
|
||||
|
||||
type StatusFilter = 'all' | 'review' | 'approved' | 'working' | 'failed'
|
||||
const STATUS_GROUP: Record<MaterialStatus, StatusFilter> = {
|
||||
// 'done' is everything no longer being made except failures, as the Production page counts it; the
|
||||
// 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',
|
||||
}
|
||||
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))
|
||||
|
||||
@@ -35,14 +40,15 @@ export function ResultsPage() {
|
||||
const [tab, setTab] = useState<'content' | 'research'>('content')
|
||||
const [query, setQuery] = useState('')
|
||||
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 materials = results.data?.materials ?? []
|
||||
const sources = results.data?.sources ?? []
|
||||
const kinds = useMemo(() => [...new Set(materials.map(m => m.kind))], [materials])
|
||||
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)))
|
||||
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
|
||||
@@ -73,7 +79,7 @@ export function ResultsPage() {
|
||||
{kinds.map(k => <option key={k} value={k}>{kindLabel(k)}</option>)}
|
||||
</select>
|
||||
<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>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user