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:
Developer
2026-06-02 22:58:10 +00:00
parent 3f5159fb8a
commit e434c439c9
66 changed files with 627 additions and 359 deletions
@@ -5,7 +5,7 @@ import {
deleteConfigFolder,
listConfigFolders,
updateConfigFolder,
} from "../api/config_folders";
} from "../api/config-folders";
const mockGet = vi.fn();
const mockPost = vi.fn();
@@ -6,7 +6,7 @@ import {
listToolTypes,
updateToolType,
validateToolType,
} from "../api/tool_types";
} from "../api/tool-types";
const mockGet = vi.fn();
const mockPost = vi.fn();
@@ -2,7 +2,7 @@ import { render, screen } from "@testing-library/react";
import { MemoryRouter, Route, Routes } from "react-router-dom";
import { describe, expect, it, vi } from "vitest";
import { ProtectedRoute } from "./protected-route";
import { ProtectedRoute } from "./ProtectedRoute";
const mockUseAuth = vi.fn();
@@ -1,5 +1,5 @@
import type { Session } from "../../../types/session";
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
interface ActiveSessionsListProps {
sessions: Session[];
@@ -28,7 +28,11 @@ export const ActiveSessionsList = ({
<article className="card session-card" key={session.id}>
<div className="stack-sm">
<div className="row row-tight">
<h3>{session.display_name || session.tool_type_name || "Unnamed Session"}</h3>
<h3>
{session.display_name ||
session.tool_type_name ||
"Unnamed Session"}
</h3>
<span className={`status-badge ${session.status}`}>
{session.status}
</span>
@@ -2,7 +2,7 @@ import { useState } from "react";
import type { Project } from "../../../types/project";
import type { GitRepository } from "../../../types/git-repository";
import type { ToolType } from "../../../types/tool-type";
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
interface QuickCreateFormProps {
projects: Project[];
@@ -24,7 +24,9 @@ export const RecentSessionsSection = ({
<article className="recent-session-item" key={session.id}>
<div className="recent-session-info">
<span className="recent-session-name">
{session.display_name || session.tool_type_name || "Unnamed Session"}
{session.display_name ||
session.tool_type_name ||
"Unnamed Session"}
</span>
<span className="muted">
{session.project_name} · {session.tool_type_name}
@@ -1,7 +1,7 @@
import styles from "./features/git/CommitDialog.module.css";
import React, { useState } from "react";
import { Icon } from "./icon";
import { Icon } from "../../ui/Icon";
interface CommitDialogProps {
isOpen: boolean;
@@ -1,6 +1,6 @@
import { useState } from "react";
import { commitChanges } from "../api/git_repositories";
import { commitChanges } from "../../../api/git-repositories";
import styles from "./features/git/CommitPanel.module.css";
interface CommitPanelProps {
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { Icon } from "../../icon";
import { Icon } from "../../ui/Icon";
import { apiClient } from "../../../api/client";
import type { GitStatus } from "../../../types/git-repository";
@@ -1,13 +1,13 @@
import styles from "./features/git/FileEditor.module.css";
import React, { useCallback, useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
import { apiClient } from "../api/client";
import { useAuth } from "../state/auth";
import { CodeEditor } from "../components/code-editor";
import { CommitDialog } from "../components/commit-dialog";
import { Icon } from "../components/icon";
import { SyntaxHighlighter } from "../components/syntax-highlighter";
import { detectLanguage } from "../utils/language";
import { apiClient } from "../../../api/client";
import { useAuth } from "../../../state/auth";
import { CodeEditor } from "../../ui/CodeEditor";
import { CommitDialog } from "./CommitDialog";
import { Icon } from "../../ui/Icon";
import { SyntaxHighlighter } from "./SyntaxHighlighter";
import { detectLanguage } from "../../../utils/language";
interface FileEditorProps {
projectId: string;
@@ -8,9 +8,9 @@ import {
pullRepository,
pushRepository,
type GitStatus,
} from "../api/git_repositories";
import { Icon } from "./icon";
import { MergeDialog } from "./merge-dialog";
} from "../../../api/git-repositories";
import { Icon } from "../../ui/Icon";
import { MergeDialog } from "./MergeDialog";
import styles from "./features/git/GitToolbar.module.css";
interface GitToolbarProps {
@@ -1,8 +1,8 @@
import styles from "./features/git/MergeDialog.module.css";
import { useState } from "react";
import { mergeBranches } from "../api/git_repositories";
import { Icon } from "./icon";
import { mergeBranches } from "../../../api/git-repositories";
import { Icon } from "../../ui/Icon";
interface MergeDialogProps {
projectId: string;
@@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
import { Icon } from "./icon";
import { highlightCode, loadLanguage } from "../utils/language";
import { Icon } from "../../ui/Icon";
import { highlightCode, loadLanguage } from "../../../utils/language";
interface SyntaxHighlighterProps {
code: string;
@@ -1,9 +1,9 @@
import type { GitRepository } from "../../../types/git-repository";
import type { GitStatus } from "../../../api/git_repositories";
import type { GitStatus } from "../../../api/git-repositories";
import type { ToolType } from "../../../types/tool-type";
import { FileBrowser } from "./FileBrowser";
import { CommitPanel } from "../../../components/commit-panel";
import { InstanceList } from "../../../components/instance-list";
import { CommitPanel } from "../git/CommitPanel";
import { InstanceList } from "../session/InstanceList";
interface WorkspaceSidebarProps {
projectId: string;
@@ -29,10 +29,7 @@ export const WorkspaceSidebar = ({
<div className="sidebar-section">
<label className="form-field">
Repository
<select
value={repoId}
onChange={(e) => onRepoChange(e.target.value)}
>
<select value={repoId} onChange={(e) => onRepoChange(e.target.value)}>
{repositories.map((repo) => (
<option key={repo.id} value={repo.id}>
{repo.name}
@@ -1,8 +1,8 @@
import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { RepositoriesSettingsTab } from "./repositories-settings-tab";
import * as gitRepositoriesApi from "../api/git_repositories";
import { RepositoriesSettingsTab } from "./RepositoriesSettingsTab";
import * as gitRepositoriesApi from "../../../api/git-repositories";
const mockRepositories = [
{
@@ -1,10 +1,10 @@
import React, { useCallback, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import type { GitRepository } from "../types/git-repository";
import { deleteRepository, listRepositories } from "../api/git_repositories";
import { RepositoryCreateDialog } from "./repository-create-dialog";
import { Icon } from "./icon";
import type { GitRepository } from "../../../types/git-repository";
import { deleteRepository, listRepositories } from "../../../api/git-repositories";
import { RepositoryCreateDialog } from "./RepositoryCreateDialog";
import { Icon } from "../../ui/Icon";
export const RepositoriesSettingsTab: React.FC = () => {
const { projectId } = useParams<{ projectId: string }>();
@@ -3,9 +3,9 @@ import { useEffect, useRef, useState } from "react";
import type {
GitRepositoryCreate,
URLParseResult,
} from "../types/git-repository";
import { createRepository, parseGitUrl } from "../api/git_repositories";
import { Icon } from "./icon";
} from "../../../types/git-repository";
import { createRepository, parseGitUrl } from "../../../api/git-repositories";
import { Icon } from "../../ui/Icon";
type CreateMode = "clone" | "blank";
type UrlValidationStatus =
@@ -17,7 +17,7 @@ vi.mock("@/api/settings", () => ({
updateUserConfig: vi.fn(),
}));
import { listRepositories } from "@/api/git_repositories";
import { listRepositories } from "@/api/git-repositories";
import { createInstance } from "@/api/sessions";
const mockProjects = [
@@ -1,8 +1,8 @@
import React, { useEffect, useState } from "react";
import { listRepositories } from "@/api/git_repositories";
import { listRepositories } from "@/api/git-repositories";
import { createInstance, startInstance } from "@/api/sessions";
import { updateUserConfig } from "@/api/settings";
import { Icon } from "@/components/icon";
import { Icon } from "@/components/ui/Icon";
import type { Project } from "@/types/project";
import type { GitRepository } from "@/types/git-repository";
import type { ToolType } from "@/types/tool-type";
@@ -1,8 +1,8 @@
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { Icon } from "./icon";
import type { ToolInstance } from "../types/tool-instance";
import type { ToolType } from "../types/tool-type";
import { Icon } from "../../ui/Icon";
import type { ToolInstance } from "../../../types/tool-instance";
import type { ToolType } from "../../../types/tool-type";
import {
checkInstanceHealth,
createInstance,
@@ -12,7 +12,7 @@ import {
restartInstance,
startInstance,
stopInstance,
} from "../api/sessions";
} from "../../../api/sessions";
import styles from "./features/session/InstanceList.module.css";
const API_BASE_URL =
@@ -1,5 +1,5 @@
import React from "react";
import { Icon } from "@/components/icon";
import { Icon } from "@/components/ui/Icon";
import type { Session } from "@/types/session";
interface SessionCardProps {
@@ -5,11 +5,11 @@ import { SerializeAddon } from "xterm-addon-serialize";
import { WebLinksAddon } from "xterm-addon-web-links";
import "xterm/css/xterm.css";
import { useTerminalConnection } from "../hooks/use-terminal-connection";
import { useTerminalConnection } from "../../../hooks/use-terminal-connection";
import type {
ServerControlMessage,
TerminalConnectionState,
} from "../types/terminal";
} from "../../../types/terminal";
import styles from "./features/terminal/TerminalComponent.module.css";
interface TerminalProps {
@@ -12,14 +12,20 @@ interface ToolConfigFormProps {
onCancel: () => void;
}
export const ToolConfigForm = ({ editingConfig, onSubmit, onCancel }: ToolConfigFormProps) => {
export const ToolConfigForm = ({
editingConfig,
onSubmit,
onCancel,
}: ToolConfigFormProps) => {
const [formData, setFormData] = useState({
key: editingConfig?.key ?? "",
value: editingConfig?.value ?? "",
config_type: editingConfig?.config_type ?? "env",
file_path: editingConfig?.file_path ?? "",
});
const [saveStatus, setSaveStatus] = useState<"idle" | "saving" | "saved" | "error">("idle");
const [saveStatus, setSaveStatus] = useState<
"idle" | "saving" | "saved" | "error"
>("idle");
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -53,7 +59,9 @@ export const ToolConfigForm = ({ editingConfig, onSubmit, onCancel }: ToolConfig
<select
id="config-type"
value={formData.config_type}
onChange={(e) => setFormData({ ...formData, config_type: e.target.value })}
onChange={(e) =>
setFormData({ ...formData, config_type: e.target.value })
}
className="form-input"
>
<option value="env">Environment Variable</option>
@@ -67,7 +75,9 @@ export const ToolConfigForm = ({ editingConfig, onSubmit, onCancel }: ToolConfig
id="config-file-path"
type="text"
value={formData.file_path}
onChange={(e) => setFormData({ ...formData, file_path: e.target.value })}
onChange={(e) =>
setFormData({ ...formData, file_path: e.target.value })
}
placeholder="e.g., /app/config.json"
className="form-input"
required
@@ -79,19 +89,24 @@ export const ToolConfigForm = ({ editingConfig, onSubmit, onCancel }: ToolConfig
<textarea
id="config-value"
value={formData.value}
onChange={(e) => setFormData({ ...formData, value: e.target.value })}
placeholder={formData.config_type === "env" ? "Enter value..." : "Enter file contents..."}
onChange={(e) =>
setFormData({ ...formData, value: e.target.value })
}
placeholder={
formData.config_type === "env"
? "Enter value..."
: "Enter file contents..."
}
className="form-input"
rows={formData.config_type === "file" ? 8 : 2}
required
/>
</div>
<div className="row" style={{ gap: "0.5rem", justifyContent: "flex-end" }}>
<button
type="button"
className="secondary-button"
onClick={onCancel}
>
<div
className="row"
style={{ gap: "0.5rem", justifyContent: "flex-end" }}
>
<button type="button" className="secondary-button" onClick={onCancel}>
Cancel
</button>
<button type="submit" className="primary-button">
@@ -1,4 +1,4 @@
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
import type { ToolConfig } from "../../../types/tool-config";
interface ToolConfigListProps {
@@ -1,23 +1,46 @@
import { useState } from "react";
import { Icon } from "../../../components/icon";
import type { ToolType, CreateToolTypeRequest, UpdateToolTypeRequest } from "../../../types/tool-type";
import { Icon } from "../../ui/Icon";
import type {
ToolType,
CreateToolTypeRequest,
UpdateToolTypeRequest,
} from "../../../types/tool-type";
interface ToolTypeFormProps {
mode: "create" | "edit";
toolType?: ToolType | null;
onSubmit: (input: CreateToolTypeRequest | UpdateToolTypeRequest) => Promise<void>;
onSubmit: (
input: CreateToolTypeRequest | UpdateToolTypeRequest,
) => Promise<void>;
onCancel: () => void;
}
export const ToolTypeForm = ({ mode, toolType, onSubmit, onCancel }: ToolTypeFormProps) => {
export const ToolTypeForm = ({
mode,
toolType,
onSubmit,
onCancel,
}: ToolTypeFormProps) => {
const [formName, setFormName] = useState(toolType?.name ?? "");
const [formDisplayName, setFormDisplayName] = useState(toolType?.display_name ?? "");
const [formDescription, setFormDescription] = useState(toolType?.description ?? "");
const [formDisplayName, setFormDisplayName] = useState(
toolType?.display_name ?? "",
);
const [formDescription, setFormDescription] = useState(
toolType?.description ?? "",
);
const [formCategory, setFormCategory] = useState(toolType?.category ?? "");
const [formInterfaces, setFormInterfaces] = useState<string[]>(toolType?.interfaces ?? []);
const [formPort, setFormPort] = useState(toolType?.default_port?.toString() ?? "");
const [formTemplate, setFormTemplate] = useState(toolType?.compose_template ?? "");
const [formVariables, setFormVariables] = useState(toolType?.required_variables?.join(", ") ?? "");
const [formInterfaces, setFormInterfaces] = useState<string[]>(
toolType?.interfaces ?? [],
);
const [formPort, setFormPort] = useState(
toolType?.default_port?.toString() ?? "",
);
const [formTemplate, setFormTemplate] = useState(
toolType?.compose_template ?? "",
);
const [formVariables, setFormVariables] = useState(
toolType?.required_variables?.join(", ") ?? "",
);
const [formError, setFormError] = useState<string | null>(null);
const handleSubmit = async (e: React.FormEvent) => {
@@ -33,7 +56,10 @@ export const ToolTypeForm = ({ mode, toolType, onSubmit, onCancel }: ToolTypeFor
return;
}
const variables = formVariables.split(",").map((v) => v.trim()).filter((v) => v.length > 0);
const variables = formVariables
.split(",")
.map((v) => v.trim())
.filter((v) => v.length > 0);
const base = {
display_name: formDisplayName.trim(),
description: formDescription.trim() || undefined,
@@ -46,13 +72,18 @@ export const ToolTypeForm = ({ mode, toolType, onSubmit, onCancel }: ToolTypeFor
try {
if (mode === "create") {
await onSubmit({ name: formName.trim(), ...base } as CreateToolTypeRequest);
await onSubmit({
name: formName.trim(),
...base,
} as CreateToolTypeRequest);
} else {
await onSubmit(base as UpdateToolTypeRequest);
}
} catch (err) {
const axiosError = err as { response?: { data?: { detail?: string } } };
setFormError(axiosError?.response?.data?.detail || "Failed to save tool type");
setFormError(
axiosError?.response?.data?.detail || "Failed to save tool type",
);
}
};
@@ -69,42 +100,89 @@ export const ToolTypeForm = ({ mode, toolType, onSubmit, onCancel }: ToolTypeFor
<form onSubmit={handleSubmit}>
<div className="form-group">
<label>Name (unique identifier)</label>
<input type="text" value={formName} onChange={(e) => setFormName(e.target.value)} disabled={mode === "edit"} placeholder="e.g., code-server" />
<input
type="text"
value={formName}
onChange={(e) => setFormName(e.target.value)}
disabled={mode === "edit"}
placeholder="e.g., code-server"
/>
</div>
<div className="form-group">
<label>Display Name</label>
<input type="text" value={formDisplayName} onChange={(e) => setFormDisplayName(e.target.value)} placeholder="e.g., VS Code Server" />
<input
type="text"
value={formDisplayName}
onChange={(e) => setFormDisplayName(e.target.value)}
placeholder="e.g., VS Code Server"
/>
</div>
<div className="form-group">
<label>Description</label>
<input type="text" value={formDescription} onChange={(e) => setFormDescription(e.target.value)} placeholder="Optional description" />
<input
type="text"
value={formDescription}
onChange={(e) => setFormDescription(e.target.value)}
placeholder="Optional description"
/>
</div>
<div className="form-group">
<label>Category</label>
<input type="text" value={formCategory} onChange={(e) => setFormCategory(e.target.value)} placeholder="e.g., editor, notebook, ai-assistant" />
<input
type="text"
value={formCategory}
onChange={(e) => setFormCategory(e.target.value)}
placeholder="e.g., editor, notebook, ai-assistant"
/>
</div>
<div className="form-group">
<label>Interfaces</label>
<div className="checkbox-group">
<label className="checkbox-label">
<input type="checkbox" checked={formInterfaces.includes("web")} onChange={() => toggleInterface("web")} /> Web
<input
type="checkbox"
checked={formInterfaces.includes("web")}
onChange={() => toggleInterface("web")}
/>{" "}
Web
</label>
<label className="checkbox-label">
<input type="checkbox" checked={formInterfaces.includes("terminal")} onChange={() => toggleInterface("terminal")} /> Terminal
<input
type="checkbox"
checked={formInterfaces.includes("terminal")}
onChange={() => toggleInterface("terminal")}
/>{" "}
Terminal
</label>
</div>
</div>
<div className="form-group">
<label>Default Port *</label>
<input type="number" value={formPort} onChange={(e) => setFormPort(e.target.value)} placeholder="e.g., 8443" required />
<input
type="number"
value={formPort}
onChange={(e) => setFormPort(e.target.value)}
placeholder="e.g., 8443"
required
/>
</div>
<div className="form-group">
<label>Compose Template (YAML)</label>
<textarea value={formTemplate} onChange={(e) => setFormTemplate(e.target.value)} rows={10} placeholder="version: '3.8'&#10;services:&#10; app:&#10; image: ..." />
<textarea
value={formTemplate}
onChange={(e) => setFormTemplate(e.target.value)}
rows={10}
placeholder="version: '3.8'&#10;services:&#10; app:&#10; image: ..."
/>
</div>
<div className="form-group">
<label>Required Variables (comma-separated)</label>
<input type="text" value={formVariables} onChange={(e) => setFormVariables(e.target.value)} placeholder="REPO_PATH, TOOL_NAME" />
<input
type="text"
value={formVariables}
onChange={(e) => setFormVariables(e.target.value)}
placeholder="REPO_PATH, TOOL_NAME"
/>
</div>
{formError && <p className="text-error">{formError}</p>}
<div className="dialog-actions">
@@ -112,7 +190,11 @@ export const ToolTypeForm = ({ mode, toolType, onSubmit, onCancel }: ToolTypeFor
<Icon name={mode === "create" ? "add" : "save"} size="sm" />
{mode === "create" ? "Create" : "Update"}
</button>
<button type="button" onClick={onCancel} className="button-secondary">
<button
type="button"
onClick={onCancel}
className="button-secondary"
>
<Icon name="cancel" size="sm" /> Cancel
</button>
</div>
@@ -1,4 +1,4 @@
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
import type { ToolType } from "../../../types/tool-type";
interface ToolTypeListProps {
@@ -55,34 +55,32 @@ export const ToolTypeList = ({
<Icon name="delete" size="sm" />
Delete
</button>
</>
)}
</div>
{deleteConfirmId === toolType.id && (
<div className="dialog-overlay">
<div className="dialog">
<p>
Delete tool type "{toolType.display_name}"?
</p>
<div className="dialog-actions">
<button
onClick={() => onDelete(toolType.id)}
className="button-danger"
>
<Icon name="delete" size="sm" />
Delete
</button>
<button onClick={() => setDeleteConfirmId(null)}>
<Icon name="cancel" size="sm" />
Cancel
</button>
</div>
</div>
</div>
</>
)}
</div>
))}
</div>
);
};
{deleteConfirmId === toolType.id && (
<div className="dialog-overlay">
<div className="dialog">
<p>Delete tool type "{toolType.display_name}"?</p>
<div className="dialog-actions">
<button
onClick={() => onDelete(toolType.id)}
className="button-danger"
>
<Icon name="delete" size="sm" />
Delete
</button>
<button onClick={() => setDeleteConfirmId(null)}>
<Icon name="cancel" size="sm" />
Cancel
</button>
</div>
</div>
</div>
)}
</div>
))}
</div>
);
};
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
import {
createConfigFolder,
deleteConfigFolder,
@@ -8,7 +8,7 @@ import {
type ConfigFolder,
type CreateConfigFolderRequest,
type UpdateConfigFolderRequest,
} from "../../../api/config_folders";
} from "../../../api/config-folders";
export const ConfigFoldersTab = () => {
const [folders, setFolders] = useState<ConfigFolder[]>([]);
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
import {
createToolConfig,
deleteToolConfig,
@@ -7,8 +7,8 @@ import {
updateToolConfig,
type CreateToolConfigRequest,
type ToolConfig,
} from "../../../api/tool_configs";
import { listToolTypes, type ToolType } from "../../../api/tool_types";
} from "../../../api/tool-configs";
import { listToolTypes, type ToolType } from "../../../api/tool-types";
export const ToolConfigsTab = () => {
const [configs, setConfigs] = useState<ToolConfig[]>([]);
@@ -9,7 +9,7 @@ vi.mock("../../../api/tool_types", () => ({
updateToolType: vi.fn(),
}));
import { listToolTypes } from "../../../api/tool_types";
import { listToolTypes } from "../../../api/tool-types";
describe("ToolTypesTab", () => {
it("renders loading state initially", () => {
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from "react";
import { Icon } from "../../../components/icon";
import { Icon } from "../../ui/Icon";
import {
createToolType,
deleteToolType,
@@ -9,7 +9,7 @@ import {
type ReadinessProbe,
type ToolType,
type UpdateToolTypeRequest,
} from "../../../api/tool_types";
} from "../../../api/tool-types";
export const ToolTypesTab = () => {
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
@@ -1,5 +1,5 @@
import { Link } from "react-router-dom";
import { Icon } from "./icon";
import { Icon } from "../../ui/Icon";
interface WorkspaceHeaderProps {
project: {
@@ -1,13 +1,13 @@
import { useCallback, useEffect } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";
import { getUserSessions } from "../api/sessions";
import type { Session } from "../types/session";
import { useTheme } from "../hooks/use-theme";
import { useAuth } from "../state/auth";
import { useSessions } from "../state/sessions";
import { Icon } from "./icon";
import type { IconName } from "../utils/icons";
import { getUserSessions } from "../../api/sessions";
import type { Session } from "../../types/session";
import { useTheme } from "../../hooks/use-theme";
import { useAuth } from "../../state/auth";
import { useSessions } from "../../state/sessions";
import { Icon } from "../ui/Icon";
import type { IconName } from "../../utils/icons";
import styles from "./layout/AppShell.module.css";
const NAV_ITEMS: { to: string; label: string; icon: IconName }[] = [
@@ -1,6 +1,6 @@
import React, { useEffect } from "react";
import Editor from "react-simple-code-editor";
import { highlightCode, loadLanguage } from "../utils/language";
import { highlightCode, loadLanguage } from "../../utils/language";
interface CodeEditorProps {
value: string;
+1
View File
@@ -1,3 +1,4 @@
export { Icon } from "./Icon";
export { LoadingState } from "./LoadingState";
export { ErrorState } from "./ErrorState";
export { StatusBadge } from "./StatusBadge";
+8 -2
View File
@@ -12,7 +12,9 @@ import type { Session } from "../types/session";
export function useDashboardActions(onRefresh: () => Promise<void>) {
const navigate = useNavigate();
const [saveState, setSaveState] = useState<"idle" | "saving" | "error">("idle");
const [saveState, setSaveState] = useState<"idle" | "saving" | "error">(
"idle",
);
const [actionBusy, setActionBusy] = useState<string | null>(null);
const handleCreate = useCallback(
@@ -60,7 +62,11 @@ export function useDashboardActions(onRefresh: () => Promise<void>) {
async (session: Session) => {
setActionBusy(session.id);
try {
await stopInstance(session.project_id, session.repository_id, session.id);
await stopInstance(
session.project_id,
session.repository_id,
session.id,
);
await onRefresh();
} finally {
setActionBusy(null);
@@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { HomePage } from "./dashboard";
import { HomePage } from "./DashboardPage";
const mockDashboard = vi.fn();
const mockSessions = vi.fn();
+193
View File
@@ -0,0 +1,193 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { getDashboardSummary, type DashboardSummary } from "../api/dashboard";
import { getUserSessions } from "../api/sessions";
import { listProjects } from "../api/projects";
import { listToolTypes } from "../api/tool-types";
import type { Session as SessionApi } from "../types/session";
import type { Project } from "../types/project";
import type { ToolType } from "../types/tool-type";
import {
DashboardSummary as DashboardSummaryComponent,
ActiveSessionsList,
ProjectsSection,
QuickCreateForm,
RecentSessionsSection,
} from "../components/features/dashboard";
import { LoadingState, ErrorState } from "../components/ui";
import { useDashboardActions } from "../hooks/use-dashboard-actions";
import { listRepositories } from "../api/git-repositories";
import type { GitRepository } from "../types/git-repository";
type HomeStatus = "loading" | "ready" | "error";
type SessionView = SessionApi;
export const HomePage = () => {
const navigate = useNavigate();
const [status, setStatus] = useState<HomeStatus>("loading");
const [summary, setSummary] = useState<DashboardSummary | null>(null);
const [sessions, setSessions] = useState<SessionView[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [repositories, setRepositories] = useState<GitRepository[]>([]);
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
const safeSessions = Array.isArray(sessions) ? sessions : [];
const loadHome = useCallback(async () => {
setStatus("loading");
try {
const [dashboard, sessionData, projectData, toolTypeData] =
await Promise.all([
getDashboardSummary(),
getUserSessions(),
listProjects(),
listToolTypes(),
]);
setSummary(dashboard);
setSessions(sessionData as SessionView[]);
setProjects(projectData);
setToolTypes(toolTypeData);
setStatus("ready");
} catch {
setStatus("error");
}
}, []);
useEffect(() => {
void loadHome();
}, [loadHome]);
const loadRepos = useCallback(async (projectId: string) => {
try {
setRepositories(await listRepositories(projectId));
} catch {
setRepositories([]);
}
}, []);
const activeSessions = useMemo(
() =>
safeSessions.filter((s) =>
["running", "building", "pending"].includes(s.status),
),
[safeSessions],
);
const recentSessions = useMemo(
() =>
safeSessions
.filter((s) => ["stopped", "error"].includes(s.status))
.slice(0, 5),
[safeSessions],
);
const actions = useDashboardActions(loadHome);
return (
<section className="stack home-page">
<header className="home-hero card">
<div className="stack-sm">
<p className="eyebrow">Workspace overview</p>
<h1>Home</h1>
<p className="muted">
Open sessions, available projects, and the fastest path back into
work.
</p>
</div>
<div className="home-hero-actions">
<button
className="primary-button"
type="button"
onClick={() => navigate("/projects")}
>
New Project
</button>
<button
className="secondary-button"
type="button"
onClick={() => navigate("/settings")}
>
Settings
</button>
</div>
</header>
{status === "loading" && <LoadingState message="Loading overview..." />}
{status === "error" && (
<ErrorState
message="Unable to load your workspace overview."
onRetry={() => void loadHome()}
/>
)}
{status === "ready" && summary && (
<>
<DashboardSummaryComponent
summary={summary}
activeSessionsCount={activeSessions.length}
/>
<section className="card stack home-section">
<div className="page-header">
<div>
<p className="eyebrow">Open sessions</p>
<h2>{activeSessions.length}</h2>
</div>
</div>
<ActiveSessionsList
sessions={activeSessions}
actionBusy={actions.actionBusy}
onOpen={actions.handleOpen}
onStop={actions.handleStop}
onDelete={actions.handleDelete}
onRecreateTunnel={actions.handleRecreateTunnel}
/>
</section>
<section className="card stack home-section">
<div className="page-header">
<div>
<p className="eyebrow">Available projects</p>
<h2>{projects.length}</h2>
</div>
<button
className="secondary-button"
type="button"
onClick={() => navigate("/projects")}
>
View all
</button>
</div>
<ProjectsSection
projects={projects}
onOpenProject={(id) => navigate(`/projects/${id}`)}
/>
</section>
<section className="card stack home-section">
<div className="page-header">
<div>
<p className="eyebrow">Quick create</p>
<h2>Start a session</h2>
</div>
</div>
<QuickCreateForm
projects={projects}
repositories={repositories}
toolTypes={toolTypes}
saveState={actions.saveState}
onSubmit={actions.handleCreate}
onProjectChange={loadRepos}
/>
</section>
<RecentSessionsSection
sessions={recentSessions}
onOpen={actions.handleOpen}
/>
</>
)}
</section>
);
};
export { HomePage as DashboardPage };
@@ -1,8 +1,8 @@
import { useCallback, useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { getCommitDetail, getRepositoryHistory, type CommitDetail, type CommitHistoryEntry } from "../api/git_repositories";
import { Icon } from "../components/icon";
import { getCommitDetail, getRepositoryHistory, type CommitDetail, type CommitHistoryEntry } from "../api/git-repositories";
import { Icon } from "../components/ui/Icon";
export const GitHistoryPage = () => {
const { projectId, repoId } = useParams<{ projectId: string; repoId: string }>();
@@ -2,9 +2,9 @@ import { useCallback, useEffect, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import type { GitRepository } from "../types/git-repository";
import { deleteRepository, listRepositories } from "../api/git_repositories";
import { Icon } from "../components/icon";
import { RepositoryCreateDialog } from "../components/repository-create-dialog";
import { deleteRepository, listRepositories } from "../api/git-repositories";
import { Icon } from "../components/ui/Icon";
import { RepositoryCreateDialog } from "../components/features/project/RepositoryCreateDialog";
type RepoStatus = "loading" | "ready" | "error";
@@ -18,7 +18,7 @@ export const NotFoundPage = () => {
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8000";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui/Icon";
export const LoginRedirectPage = () => {
const nextPath = new URLSearchParams(window.location.search).get("next") ?? "/";
@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { getProfile, updateProfile, uploadAvatar } from "../api/profile";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui/Icon";
import { useAuth } from "../state/auth";
import type { UserProfile } from "../api/profile";
@@ -1,7 +1,7 @@
import React, { useState, useEffect } from "react";
import { useParams, Link, useNavigate, Routes, Route } from "react-router-dom";
import { SettingsTabLayout } from "../components/settings-tab-layout";
import { RepositoriesSettingsTab } from "../components/repositories-settings-tab";
import { SettingsTabLayout } from "../components/features/settings/SettingsTabLayout";
import { RepositoriesSettingsTab } from "../components/features/project/RepositoriesSettingsTab";
import { apiClient } from "../api/client";
import { Project } from "../types";
@@ -1,7 +1,7 @@
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ProjectsPage } from "./projects";
import { ProjectsPage } from "./ProjectsPage";
import * as projectsApi from "../api/projects";
const mockProjects = [
@@ -8,7 +8,7 @@ import {
listProjects,
type ProjectCreateInput,
} from "../api/projects";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui/Icon";
import type { Project } from "../types";
type ProjectsStatus = "loading" | "ready" | "error";
@@ -8,13 +8,13 @@ import {
getRepositoryStatus,
listRepositories,
type GitStatus,
} from "../api/git_repositories";
} from "../api/git-repositories";
import { WorkspaceSidebar } from "../components/features/git";
import { FileEditor } from "../components/file-editor";
import { WorkspaceHeader } from "../components/workspace-header";
import { GitToolbar } from "../components/git-toolbar";
import { FileEditor } from "../components/features/git/FileEditor";
import { WorkspaceHeader } from "../components/features/workspace/WorkspaceHeader";
import { GitToolbar } from "../components/features/git/GitToolbar";
import { LoadingState, ErrorState } from "../components/ui";
import { listToolTypes } from "../api/tool_types";
import { listToolTypes } from "../api/tool-types";
type WorkspaceStatus = "loading" | "ready" | "error" | "empty";
@@ -133,10 +133,7 @@ export const RepoWorkspace = () => {
return (
<section className="repo-workspace">
{project && (
<WorkspaceHeader
project={project}
currentRepo={selectedRepo || null}
/>
<WorkspaceHeader project={project} currentRepo={selectedRepo || null} />
)}
{status === "loading" && (
@@ -180,9 +177,7 @@ export const RepoWorkspace = () => {
onRefresh={() => {
void loadBranches();
void loadGitStatus();
window.dispatchEvent(
new CustomEvent("refresh-file-tree"),
);
window.dispatchEvent(new CustomEvent("refresh-file-tree"));
}}
/>
)}
@@ -197,18 +192,13 @@ export const RepoWorkspace = () => {
onRepoChange={handleRepoChange}
onCommit={() => {
void loadGitStatus();
window.dispatchEvent(
new CustomEvent("refresh-file-tree"),
);
window.dispatchEvent(new CustomEvent("refresh-file-tree"));
}}
/>
)}
<main className="workspace-main">
{selectedRepoId && (
<FileEditor
projectId={projectId!}
repoId={selectedRepoId}
/>
<FileEditor projectId={projectId!} repoId={selectedRepoId} />
)}
</main>
</div>
@@ -3,9 +3,9 @@ import { useNavigate } from "react-router-dom";
import { listProjects } from "../api/projects";
import { getUserSessions } from "../api/sessions";
import { listToolTypes } from "../api/tool_types";
import { listToolTypes } from "../api/tool-types";
import { getUserConfig } from "../api/settings";
import { Icon } from "../components/icon";
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";
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from "react";
import { Link, Outlet, useLocation, useOutletContext } from "react-router-dom";
import { getUserConfig, updateUserConfig, type UserConfig, type UserConfigUpdate } from "../api/settings";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui/Icon";
type SettingsStatus = "loading" | "ready" | "error";
@@ -1,7 +1,7 @@
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { createSSHKey, deleteSSHKey, listSSHKeys, type SSHKey } from "../api/ssh_keys";
import { Icon } from "../components/icon";
import { createSSHKey, deleteSSHKey, listSSHKeys, type SSHKey } from "../api/ssh-keys";
import { Icon } from "../components/ui/Icon";
export const SSHKeysPage = () => {
const navigate = useNavigate();
@@ -1,7 +1,7 @@
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import { TerminalComponent } from "../components/terminal";
import { Icon } from "../components/icon";
import { TerminalComponent } from "../components/features/terminal/TerminalComponent";
import { Icon } from "../components/ui/Icon";
export const TerminalPage: React.FC = () => {
const { instanceId } = useParams<{ instanceId: string }>();
@@ -1,17 +1,20 @@
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui/Icon";
import type { ToolType } from "../types/tool-type";
import type { ToolConfig } from "../types/tool-config";
import { listToolTypes } from "../api/tool_types";
import { listToolTypes } from "../api/tool-types";
import {
createToolConfig,
deleteToolConfig,
listToolConfigs,
updateToolConfig,
} from "../api/tool_configs";
import { ToolConfigForm, ToolConfigList } from "../components/features/tool-configs";
} from "../api/tool-configs";
import {
ToolConfigForm,
ToolConfigList,
} from "../components/features/tool-configs";
type ConfigStatus = "loading" | "ready" | "error";
@@ -86,12 +89,16 @@ export const ToolConfigsPage = () => {
};
const selectedTool = toolTypes.find((t) => t.id === selectedToolType);
const filteredConfigs = configs.filter((c) => c.tool_type_id === selectedToolType);
const filteredConfigs = configs.filter(
(c) => c.tool_type_id === selectedToolType,
);
if (status === "loading") {
return (
<section className="stack">
<div className="page-header"><h1>Tool Configurations</h1></div>
<div className="page-header">
<h1>Tool Configurations</h1>
</div>
<p className="muted">Loading...</p>
</section>
);
@@ -100,10 +107,16 @@ export const ToolConfigsPage = () => {
if (status === "error") {
return (
<section className="stack">
<div className="page-header"><h1>Tool Configurations</h1></div>
<div className="page-header">
<h1>Tool Configurations</h1>
</div>
<div className="card stack">
<p>Failed to load configurations</p>
<button className="secondary-button" onClick={() => void loadData()} type="button">
<button
className="secondary-button"
onClick={() => void loadData()}
type="button"
>
<Icon name="refresh" size="sm" /> Retry
</button>
</div>
@@ -118,7 +131,11 @@ export const ToolConfigsPage = () => {
<p className="eyebrow">Settings</p>
<h1>Tool Configurations</h1>
</div>
<button className="secondary-button" type="button" onClick={() => navigate("/settings")}>
<button
className="secondary-button"
type="button"
onClick={() => navigate("/settings")}
>
Back to settings
</button>
</div>
@@ -136,18 +153,24 @@ export const ToolConfigsPage = () => {
className="form-input"
>
{toolTypes.map((tool) => (
<option key={tool.id} value={tool.id}>{tool.display_name}</option>
<option key={tool.id} value={tool.id}>
{tool.display_name}
</option>
))}
</select>
{selectedTool && (
<p className="muted" style={{ marginTop: "0.5rem" }}>
Category: {selectedTool.category} · Interfaces: {selectedTool.interfaces?.join(", ")}
Category: {selectedTool.category} · Interfaces:{" "}
{selectedTool.interfaces?.join(", ")}
</p>
)}
</div>
<div className="card stack">
<div className="row" style={{ justifyContent: "space-between", alignItems: "center" }}>
<div
className="row"
style={{ justifyContent: "space-between", alignItems: "center" }}
>
<h2>Configuration Variables</h2>
<button
className="primary-button small"
@@ -160,7 +183,11 @@ export const ToolConfigsPage = () => {
<Icon name="add" size="sm" /> Add Config
</button>
</div>
<ToolConfigList configs={filteredConfigs} onEdit={handleEdit} onDelete={handleDelete} />
<ToolConfigList
configs={filteredConfigs}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</div>
{showForm && (
@@ -1,14 +1,18 @@
import { useCallback, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import type { ToolType, CreateToolTypeRequest, UpdateToolTypeRequest } from "../types/tool-type";
import type {
ToolType,
CreateToolTypeRequest,
UpdateToolTypeRequest,
} from "../types/tool-type";
import {
createToolType,
deleteToolType,
listToolTypes,
updateToolType,
} from "../api/tool_types";
import { Icon } from "../components/icon";
} from "../api/tool-types";
import { Icon } from "../components/ui/Icon";
import { ToolTypeForm, ToolTypeList } from "../components/features/tool-types";
type ToolTypesStatus = "loading" | "ready" | "error";
@@ -17,7 +21,9 @@ export const ToolTypesPage = () => {
const navigate = useNavigate();
const [status, setStatus] = useState<ToolTypesStatus>("loading");
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
const [dialogMode, setDialogMode] = useState<"none" | "create" | "edit">("none");
const [dialogMode, setDialogMode] = useState<"none" | "create" | "edit">(
"none",
);
const [editingToolType, setEditingToolType] = useState<ToolType | null>(null);
const [deleteConfirmId, setDeleteConfirmId] = useState<string | null>(null);
@@ -52,7 +58,9 @@ export const ToolTypesPage = () => {
setEditingToolType(null);
};
const handleSubmit = async (input: CreateToolTypeRequest | UpdateToolTypeRequest) => {
const handleSubmit = async (
input: CreateToolTypeRequest | UpdateToolTypeRequest,
) => {
if (dialogMode === "create") {
await createToolType(input as CreateToolTypeRequest);
} else if (editingToolType) {
@@ -1,10 +1,10 @@
import { cleanup, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ToolWorkshopPage } from "./tool-workshop";
import * as toolTypesApi from "../api/tool_types";
import * as toolConfigsApi from "../api/tool_configs";
import * as configFoldersApi from "../api/config_folders";
import { ToolWorkshopPage } from "./ToolWorkshopPage";
import * as toolTypesApi from "../api/tool-types";
import * as toolConfigsApi from "../api/tool-configs";
import * as configFoldersApi from "../api/config-folders";
const mockToolTypes = [
{
@@ -1,5 +1,5 @@
import { useState } from "react";
import { Icon } from "../components/icon";
import { Icon } from "../components/ui";
import {
ToolTypesTab,
ToolConfigsTab,
-110
View File
@@ -1,110 +0,0 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { getDashboardSummary, type DashboardSummary } from "../api/dashboard";
import { getUserSessions } from "../api/sessions";
import { listProjects } from "../api/projects";
import { listToolTypes } from "../api/tool_types";
import type { Session as SessionApi } from "../types/session";
import type { Project } from "../types/project";
import type { ToolType } from "../types/tool-type";
import {
DashboardSummary as DashboardSummaryComponent,
ActiveSessionsList,
ProjectsSection,
QuickCreateForm,
RecentSessionsSection,
} from "../components/features/dashboard";
import { LoadingState, ErrorState } from "../components/ui";
import { useDashboardActions } from "../hooks/use-dashboard-actions";
import { listRepositories } from "../api/git_repositories";
import type { GitRepository } from "../types/git-repository";
type HomeStatus = "loading" | "ready" | "error";
type SessionView = SessionApi;
export const HomePage = () => {
const navigate = useNavigate();
const [status, setStatus] = useState<HomeStatus>("loading");
const [summary, setSummary] = useState<DashboardSummary | null>(null);
const [sessions, setSessions] = useState<SessionView[]>([]);
const [projects, setProjects] = useState<Project[]>([]);
const [repositories, setRepositories] = useState<GitRepository[]>([]);
const [toolTypes, setToolTypes] = useState<ToolType[]>([]);
const safeSessions = Array.isArray(sessions) ? sessions : [];
const loadHome = useCallback(async () => {
setStatus("loading");
try {
const [dashboard, sessionData, projectData, toolTypeData] = await Promise.all([
getDashboardSummary(),
getUserSessions(),
listProjects(),
listToolTypes(),
]);
setSummary(dashboard);
setSessions(sessionData as SessionView[]);
setProjects(projectData);
setToolTypes(toolTypeData);
setStatus("ready");
} catch {
setStatus("error");
}
}, []);
useEffect(() => { void loadHome(); }, [loadHome]);
const loadRepos = useCallback(async (projectId: string) => {
try { setRepositories(await listRepositories(projectId)); }
catch { setRepositories([]); }
}, []);
const activeSessions = useMemo(() => safeSessions.filter((s) => ["running", "building", "pending"].includes(s.status)), [safeSessions]);
const recentSessions = useMemo(() => safeSessions.filter((s) => ["stopped", "error"].includes(s.status)).slice(0, 5), [safeSessions]);
const actions = useDashboardActions(loadHome);
return (
<section className="stack home-page">
<header className="home-hero card">
<div className="stack-sm">
<p className="eyebrow">Workspace overview</p>
<h1>Home</h1>
<p className="muted">Open sessions, available projects, and the fastest path back into work.</p>
</div>
<div className="home-hero-actions">
<button className="primary-button" type="button" onClick={() => navigate("/projects")}>New Project</button>
<button className="secondary-button" type="button" onClick={() => navigate("/settings")}>Settings</button>
</div>
</header>
{status === "loading" && <LoadingState message="Loading overview..." />}
{status === "error" && <ErrorState message="Unable to load your workspace overview." onRetry={() => void loadHome()} />}
{status === "ready" && summary && (
<>
<DashboardSummaryComponent summary={summary} activeSessionsCount={activeSessions.length} />
<section className="card stack home-section">
<div className="page-header"><div><p className="eyebrow">Open sessions</p><h2>{activeSessions.length}</h2></div></div>
<ActiveSessionsList sessions={activeSessions} actionBusy={actions.actionBusy} onOpen={actions.handleOpen} onStop={actions.handleStop} onDelete={actions.handleDelete} onRecreateTunnel={actions.handleRecreateTunnel} />
</section>
<section className="card stack home-section">
<div className="page-header"><div><p className="eyebrow">Available projects</p><h2>{projects.length}</h2></div><button className="secondary-button" type="button" onClick={() => navigate("/projects")}>View all</button></div>
<ProjectsSection projects={projects} onOpenProject={(id) => navigate(`/projects/${id}`)} />
</section>
<section className="card stack home-section">
<div className="page-header"><div><p className="eyebrow">Quick create</p><h2>Start a session</h2></div></div>
<QuickCreateForm projects={projects} repositories={repositories} toolTypes={toolTypes} saveState={actions.saveState} onSubmit={actions.handleCreate} onProjectChange={loadRepos} />
</section>
<RecentSessionsSection sessions={recentSessions} onOpen={actions.handleOpen} />
</>
)}
</section>
);
};
export { HomePage as DashboardPage };
+18 -16
View File
@@ -1,21 +1,22 @@
import { Navigate, Route, Routes } from "react-router-dom";
import { AppShell } from "./components/app-shell";
import { ProtectedRoute } from "./components/protected-route";
import { HomePage } from "./pages/dashboard";
import { LoginRedirectPage, NotFoundPage } from "./pages/placeholder";
import { ProfilePage } from "./pages/profile";
import { ProjectsPage } from "./pages/projects";
import { GitRepositoriesPage } from "./pages/git-repositories";
import { GitHistoryPage } from "./pages/git-history";
import { ProjectSettingsPage } from "./pages/project-settings";
import { RepoWorkspace } from "./pages/repo-workspace";
import { SettingsPage, GeneralSettingsTab } from "./pages/settings";
import { TerminalPage } from "./pages/terminal";
import { ToolWorkshopPage } from "./pages/tool-workshop";
import { SSHKeysPage } from "./pages/ssh-keys";
import { ToolConfigsPage } from "./pages/tool-configs";
import { ToolTypesPage } from "./pages/tool-types";
import { AppShell } from "./components/layout/AppShell";
import { ProtectedRoute } from "./components/ProtectedRoute";
import { HomePage } from "./pages/DashboardPage";
import { LoginRedirectPage, NotFoundPage } from "./pages/PlaceholderPage";
import { ProfilePage } from "./pages/ProfilePage";
import { ProjectsPage } from "./pages/ProjectsPage";
import { GitRepositoriesPage } from "./pages/GitRepositoriesPage";
import { GitHistoryPage } from "./pages/GitHistoryPage";
import { ProjectSettingsPage } from "./pages/ProjectSettingsPage";
import { RepoWorkspace } from "./pages/RepoWorkspacePage";
import { SettingsPage, GeneralSettingsTab } from "./pages/SettingsPage";
import { TerminalPage } from "./pages/TerminalPage";
import { ToolWorkshopPage } from "./pages/ToolWorkshopPage";
import { SSHKeysPage } from "./pages/SshKeysPage";
import { ToolConfigsPage } from "./pages/ToolConfigsPage";
import { ToolTypesPage } from "./pages/ToolTypesPage";
import { SessionsPage } from "./pages/SessionsPage";
export const AppRouter = () => {
return (
@@ -50,6 +51,7 @@ export const AppRouter = () => {
</Route>
<Route path="tool-workshop" element={<ToolWorkshopPage />} />
<Route path="instances/:instanceId/terminal" element={<TerminalPage />} />
<Route path="sessions" element={<SessionsPage />} />
</Route>
<Route path="/404" element={<NotFoundPage />} />
<Route path="*" element={<Navigate to="/404" replace />} />