refactor: rename files to PascalCase components and kebab-case APIs (Task 4.4)
- Rename all component files to PascalCase matching exported names - Move components into feature directories (git/, session/, project/, terminal/, workspace/, ui/, layout/) - Rename all page files to PascalCase with Page suffix - Rename all API files to kebab-case - Update all imports across codebase with corrected relative depths - Preserve git history via git mv Quality gates: tsc (pass), eslint (pass), 66/74 tests pass (8 pre-existing failures) Refs: repo-restructure Task 4.4
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { listProjects } from "../api/projects";
|
||||
import { getUserSessions } from "../api/sessions";
|
||||
import { listToolTypes } from "../api/tool-types";
|
||||
import { getUserConfig } from "../api/settings";
|
||||
import { Icon } from "../components/ui/Icon";
|
||||
import { LoadingState, ErrorState } from "../components/ui";
|
||||
import { CreateSessionForm, SessionList } from "../components/features/session";
|
||||
import type { Project } from "../types/project";
|
||||
import type { Session } from "../types/session";
|
||||
import type { ToolType } from "../types/tool-type";
|
||||
|
||||
type SessionsStatus = "loading" | "ready" | "error";
|
||||
|
||||
export const SessionsPage = () => {
|
||||
const navigate = useNavigate();
|
||||
const [status, setStatus] = useState<SessionsStatus>("loading");
|
||||
const [sessions, setSessions] = useState<Session[]>([]);
|
||||
const [lastSessionId, setLastSessionId] = useState<string | null>(null);
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
|
||||
|
||||
const loadSessions = useCallback(async () => {
|
||||
setStatus("loading");
|
||||
try {
|
||||
const [sessionsData, config] = await Promise.all([
|
||||
getUserSessions(),
|
||||
getUserConfig(),
|
||||
]);
|
||||
setSessions(sessionsData);
|
||||
setLastSessionId(config.last_session_id ?? null);
|
||||
setStatus("ready");
|
||||
} catch {
|
||||
setStatus("error");
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
void loadSessions();
|
||||
}, [loadSessions]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadProjects = async () => {
|
||||
try {
|
||||
setProjects(await listProjects());
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
};
|
||||
void loadProjects();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const loadToolTypes = async () => {
|
||||
try {
|
||||
setToolTypes(await listToolTypes());
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
};
|
||||
void loadToolTypes();
|
||||
}, []);
|
||||
|
||||
const activeSessions = useMemo(
|
||||
() =>
|
||||
sessions.filter((s) =>
|
||||
["running", "building", "pending"].includes(s.status),
|
||||
),
|
||||
[sessions],
|
||||
);
|
||||
|
||||
const recentSessions = useMemo(
|
||||
() =>
|
||||
sessions
|
||||
.filter((s) => ["stopped", "error"].includes(s.status))
|
||||
.slice(0, 5),
|
||||
[sessions],
|
||||
);
|
||||
|
||||
const lastSession = useMemo(
|
||||
() => sessions.find((s) => s.id === lastSessionId) ?? null,
|
||||
[sessions, lastSessionId],
|
||||
);
|
||||
|
||||
const handleOpen = useCallback(
|
||||
(session: Session) => {
|
||||
if (session.tool_type_interfaces?.includes("terminal")) {
|
||||
navigate(`/instances/${session.id}/terminal`);
|
||||
} else {
|
||||
navigate(`/projects/${session.project_id}`);
|
||||
}
|
||||
},
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const handleResumeLast = useCallback(() => {
|
||||
if (!lastSession) return;
|
||||
const project = projects.find((p) => p.name === lastSession.project_name);
|
||||
if (project) navigate(`/projects/${project.id}`);
|
||||
}, [lastSession, projects, navigate]);
|
||||
|
||||
return (
|
||||
<section className="stack sessions-page">
|
||||
<div className="page-header">
|
||||
<h1>Sessions</h1>
|
||||
</div>
|
||||
|
||||
{status === "loading" && <LoadingState message="Loading sessions..." />}
|
||||
|
||||
{status === "error" && (
|
||||
<ErrorState
|
||||
message="Failed to load sessions"
|
||||
onRetry={() => void loadSessions()}
|
||||
/>
|
||||
)}
|
||||
|
||||
{status === "ready" && (
|
||||
<>
|
||||
{/* Last Session */}
|
||||
{lastSession && (
|
||||
<div className="last-session-section">
|
||||
<h2>Last Session</h2>
|
||||
<div className="card last-session-card">
|
||||
<div className="last-session-info">
|
||||
<h3>
|
||||
{lastSession.display_name ||
|
||||
lastSession.tool_type_name ||
|
||||
"Unnamed Session"}
|
||||
</h3>
|
||||
<p className="muted">
|
||||
{lastSession.tool_type_name} · {lastSession.project_name} ·{" "}
|
||||
{lastSession.repository_name}
|
||||
</p>
|
||||
{lastSession.url && (
|
||||
<p className="session-url">
|
||||
<a
|
||||
href={lastSession.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{lastSession.url}
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
<span className={`status-badge ${lastSession.status}`}>
|
||||
{lastSession.status}
|
||||
</span>
|
||||
</div>
|
||||
<div className="last-session-actions">
|
||||
{lastSession.url ? (
|
||||
<a
|
||||
href={lastSession.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="primary-button"
|
||||
>
|
||||
<Icon name="external" size="sm" /> Open
|
||||
</a>
|
||||
) : (
|
||||
<button
|
||||
className="primary-button"
|
||||
onClick={handleResumeLast}
|
||||
type="button"
|
||||
>
|
||||
<Icon name="play" size="sm" /> Resume
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Active Sessions */}
|
||||
<div className="active-sessions-section">
|
||||
<h2>
|
||||
Active Sessions
|
||||
{activeSessions.length > 0 && (
|
||||
<span className="badge">{activeSessions.length}</span>
|
||||
)}
|
||||
</h2>
|
||||
<SessionList
|
||||
sessions={activeSessions}
|
||||
variant="active"
|
||||
onSessionChange={loadSessions}
|
||||
onOpen={handleOpen}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Recent Sessions */}
|
||||
{recentSessions.length > 0 && (
|
||||
<div className="recent-sessions-section">
|
||||
<h2>Recent Sessions</h2>
|
||||
<SessionList
|
||||
sessions={recentSessions}
|
||||
variant="recent"
|
||||
onSessionChange={loadSessions}
|
||||
onOpen={handleOpen}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Create Session */}
|
||||
<CreateSessionForm
|
||||
projects={projects}
|
||||
toolTypes={toolTypes}
|
||||
onCreated={loadSessions}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user