feat(jobs): embed route-backed workspace
CI and Deploy / test (pull_request) Successful in 4m34s
CI and Deploy / deploy (pull_request) Has been skipped

This commit is contained in:
cesnimda
2026-08-10 11:28:01 +02:00
parent a54b70960c
commit b67a531af4
9 changed files with 303 additions and 19 deletions
@@ -36,11 +36,29 @@ import {
// reuses the component that already owns that domain (Attachments, Correspondence, AiWorkspacePanel).
// docs/architecture/application-workspace.md.
export default function ApplicationWorkspacePage() {
return <ApplicationWorkspace />;
}
type ApplicationWorkspaceProps = {
jobIdOverride?: number;
sectionOverride?: WorkspaceSectionKey;
onSectionChange?: (section: WorkspaceSectionKey) => void;
onClose?: () => void;
fullPageHref?: string;
};
export function ApplicationWorkspace({
jobIdOverride,
sectionOverride,
onSectionChange,
onClose,
fullPageHref,
}: ApplicationWorkspaceProps) {
const { id } = useParams();
const jobId = Number(id);
const jobId = jobIdOverride ?? Number(id);
const navigate = useNavigate();
const [params, setParams] = useSearchParams();
const section = workspaceSection(params.get("section"));
const section = sectionOverride ?? workspaceSection(params.get("section"));
const [overview, setOverview] = useState<WorkspaceOverview | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -57,12 +75,16 @@ export default function ApplicationWorkspacePage() {
load();
}, [load]);
const go = (next: WorkspaceSectionKey) => setParams({ section: next }, { replace: true });
const go = (next: WorkspaceSectionKey) => {
if (onSectionChange) onSectionChange(next);
else setParams({ section: next }, { replace: true });
};
const close = onClose ?? (() => navigate("/jobs"));
if (error) {
return (
<Box sx={{ p: 3 }}>
<Button startIcon={<ArrowBackIcon />} onClick={() => navigate("/jobs")}>Back to applications</Button>
<Button startIcon={<ArrowBackIcon />} onClick={close}>Back to applications</Button>
<Alert severity="error" sx={{ mt: 2 }}>{error}</Alert>
</Box>
);
@@ -73,13 +95,28 @@ export default function ApplicationWorkspacePage() {
<Paper sx={{ p: 1, borderRadius: 3, position: { md: "sticky" }, top: 12 }}>
<Stack direction="row" alignItems="center" spacing={0.5} sx={{ px: 1, py: 0.5 }}>
<Tooltip title="Back to applications">
<IconButton size="small" aria-label="Back to applications" onClick={() => navigate("/jobs")}>
<IconButton size="small" aria-label="Back to applications" onClick={close}>
<ArrowBackIcon fontSize="small" />
</IconButton>
</Tooltip>
<Typography variant="caption" sx={{ fontWeight: 800, letterSpacing: ".08em", textTransform: "uppercase", color: "text.secondary" }}>
Workspace
</Typography>
{fullPageHref ? (
<Tooltip title="Open full-page workspace">
<IconButton
component="a"
href={fullPageHref}
target="_blank"
rel="noopener noreferrer"
size="small"
aria-label="Open full-page workspace"
sx={{ ml: "auto" }}
>
<OpenInNewIcon fontSize="small" />
</IconButton>
</Tooltip>
) : null}
</Stack>
<List dense component="nav" aria-label="Workspace sections">
{WORKSPACE_SECTIONS.map((s) => (