feat(ui): add A3 discovery filter rail and results layout

This commit is contained in:
cesnimda
2026-11-03 10:22:19 +01:00
parent 172074e90f
commit c9c6b1abc1
61 changed files with 1434 additions and 5 deletions
+11 -5
View File
@@ -56,6 +56,7 @@ export default function JobDiscoveryPage() {
const [mutating, setMutating] = useState(false);
const pending = useRef(false);
const resultsRef = useRef<HTMLDivElement>(null);
const queryRef = useRef<HTMLInputElement>(null);
const removedControlRef = useRef<Element | null>(null);
const busy = loading || savingSearch || updatingSavedSearchId !== null || mutating;
@@ -182,8 +183,12 @@ export default function JobDiscoveryPage() {
return (
<Stack spacing={3} sx={{ minWidth: 0 }}>
<Box component="form" onSubmit={search} sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", md: "2fr 1fr auto" }, gap: 1.5 }}>
<TextField label={t("jobDiscoveryRoleCompany")} value={query} disabled={busy} onChange={(event) => setQuery(event.target.value)} />
<Alert severity="info" variant="outlined" sx={{ color: "text.primary", "& .MuiAlert-icon": { color: "info.main" } }}>{t("jobDiscoverySourceHelp")}</Alert>
<Box data-discovery-layout sx={{ display: "grid", gap: 3, minWidth: 0, alignItems: "start", "@media (min-width:1001px)": { gridTemplateColumns: "minmax(16rem, 20rem) minmax(0, 1fr)" } }}>
<Box component="aside" aria-label={t("jobDiscoveryFilters")} sx={{ display: "grid", gap: 2, minWidth: 0 }}>
<PageHeader level="h2" title={t("jobDiscoveryFilters")} actions={<Button onClick={() => resultsRef.current?.focus()}>{t("jobDiscoveryJumpResults")}</Button>} />
<Box component="form" aria-label={t("jobDiscoveryFilters")} onSubmit={search} sx={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr)", gap: 1.5 }}>
<TextField inputRef={queryRef} label={t("jobDiscoveryRoleCompany")} value={query} disabled={busy} onChange={(event) => setQuery(event.target.value)} />
<TextField label={t("jobDiscoveryMunicipality")} value={location} disabled={busy} onChange={(event) => setLocation(event.target.value)} />
<Button type="submit" variant="contained" startIcon={loading ? <CircularProgress aria-hidden="true" size={18} color="inherit" /> : <SearchIcon />} disabled={busy}>{t("jobDiscoverySearchNav")}</Button>
</Box>
@@ -223,9 +228,9 @@ export default function JobDiscoveryPage() {
)}
</CardContent>
</Card>
<Alert severity="info" variant="outlined" sx={{ color: "text.primary", "& .MuiAlert-icon": { color: "info.main" } }}>{t("jobDiscoverySourceHelp")}</Alert>
</Box>
<Box role="region" aria-label={t("jobDiscoveryResults")} tabIndex={-1} ref={resultsRef} aria-busy={loading} sx={{ display: "grid", gap: 2, minWidth: 0 }}>
{hasSearched ? <PageHeader level="h2" title={t("jobDiscoveryResults")} /> : null}
<PageHeader level="h2" title={t("jobDiscoveryResults")} actions={<Button disabled={busy} onClick={() => queryRef.current?.focus()}>{t("jobDiscoveryReturnFilters")}</Button>} />
<ViewStateNotice compact loading={loading} title={t(error ?? "jobDiscoveryUnavailable")}
error={error ? { kind: "unavailable", message: t(error), retryable: error === "jobDiscoveryUnavailable" } : null}
onRetry={() => {
@@ -245,7 +250,7 @@ export default function JobDiscoveryPage() {
</TextField>
</Box>
) : null}
<Box sx={{ display: "grid", gridTemplateColumns: { xs: "1fr", lg: "repeat(2, minmax(0, 1fr))" }, gap: 2 }}>
<Box sx={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr)", gap: 2 }}>
{sortedJobs.map((job) => (
<Card key={job.id} component="article" aria-label={job.title} variant="outlined" sx={{ display: "flex", flexDirection: "column", minWidth: 0 }}>
<CardContent sx={{ p: "var(--app-card-padding)" }}>
@@ -274,6 +279,7 @@ export default function JobDiscoveryPage() {
))}
</Box>
</Box>
</Box>
</Stack>
);
}