>(new Set());
const lowerQuery = query.toLowerCase();
const filteredColumns = columns.map((col) => ({
@@ -253,12 +295,75 @@ export default function Pipelines() {
+
+
+
+
-
- {filteredColumns.map((col) => (
-
- ))}
-
+
+ {view === "columns" ? (
+
+ {filteredColumns.map((col) => (
+
+ ))}
+
+ ) : (
+
+ {filteredColumns.map((col) => {
+ const isCollapsed = collapsed.has(col.id);
+ return (
+
+
+ {!isCollapsed && (col.items.length > 0 ? (
+
+ {col.items.map((item) => (
+
+ ))}
+
+ ) : (
+
No runs
+ ))}
+
+ );
+ })}
+
+ )}
);
}
diff --git a/apps/arc-web/app/routes/settings.tsx b/apps/arc-web/app/routes/settings.tsx
index 5f68c2730..776ba234e 100644
--- a/apps/arc-web/app/routes/settings.tsx
+++ b/apps/arc-web/app/routes/settings.tsx
@@ -4,6 +4,157 @@ export function meta({}: Route.MetaArgs) {
return [{ title: "Settings — Arc" }];
}
-export default function Settings() {
- return null;
+export const handle = { hideHeader: true };
+
+interface SettingField {
+ key: string;
+ label: string;
+ value: string;
+ type: "text" | "select" | "toggle";
+ options?: string[];
+ description?: string;
+}
+
+interface SettingGroup {
+ id: string;
+ name: string;
+ description: string;
+ fields: SettingField[];
+}
+
+const settingGroups: SettingGroup[] = [
+ {
+ id: "general",
+ name: "General",
+ description: "Core platform settings and defaults.",
+ fields: [
+ { key: "org_name", label: "Organization name", value: "Acme Corp", type: "text" },
+ { key: "default_branch", label: "Default branch", value: "main", type: "text" },
+ { key: "timezone", label: "Timezone", value: "America/New_York", type: "select", options: ["America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles", "UTC", "Europe/London", "Europe/Berlin", "Asia/Tokyo"] },
+ { key: "auto_cancel", label: "Auto-cancel superseded runs", value: "true", type: "toggle" },
+ ],
+ },
+ {
+ id: "git",
+ name: "Git & VCS",
+ description: "Version control integration and repository settings.",
+ fields: [
+ { key: "github_org", label: "GitHub organization", value: "acme-corp", type: "text" },
+ { key: "clone_protocol", label: "Clone protocol", value: "SSH", type: "select", options: ["SSH", "HTTPS"] },
+ { key: "auto_merge", label: "Auto-merge when checks pass", value: "false", type: "toggle" },
+ { key: "delete_branch", label: "Delete branch after merge", value: "true", type: "toggle" },
+ { key: "commit_signing", label: "Require commit signing", value: "false", type: "toggle" },
+ ],
+ },
+ {
+ id: "compute",
+ name: "Compute",
+ description: "Resource allocation and execution environment.",
+ fields: [
+ { key: "default_cpu", label: "Default CPU", value: "4", type: "select", options: ["2", "4", "8", "16"] },
+ { key: "default_memory", label: "Default memory", value: "8 GB", type: "select", options: ["4 GB", "8 GB", "16 GB", "32 GB"] },
+ { key: "max_parallel", label: "Max parallel runs", value: "10", type: "text" },
+ { key: "timeout_minutes", label: "Run timeout (minutes)", value: "120", type: "text" },
+ { key: "gpu_enabled", label: "GPU acceleration", value: "false", type: "toggle" },
+ ],
+ },
+ {
+ id: "notifications",
+ name: "Notifications",
+ description: "Alerts and notification delivery preferences.",
+ fields: [
+ { key: "slack_webhook", label: "Slack webhook URL", value: "https://hooks.slack.com/services/T00/B00/xxxx", type: "text" },
+ { key: "notify_on_failure", label: "Notify on failure", value: "true", type: "toggle" },
+ { key: "notify_on_success", label: "Notify on success", value: "false", type: "toggle" },
+ { key: "notify_on_approval", label: "Notify on approval needed", value: "true", type: "toggle" },
+ { key: "email_digest", label: "Daily email digest", value: "false", type: "toggle" },
+ ],
+ },
+ {
+ id: "security",
+ name: "Security",
+ description: "Access control and security policies.",
+ fields: [
+ { key: "sso_provider", label: "SSO provider", value: "Okta", type: "select", options: ["None", "Okta", "Azure AD", "Google Workspace", "OneLogin"] },
+ { key: "mfa_required", label: "Require MFA", value: "true", type: "toggle" },
+ { key: "session_timeout", label: "Session timeout", value: "8 hours", type: "select", options: ["1 hour", "4 hours", "8 hours", "24 hours", "7 days"] },
+ { key: "audit_log", label: "Audit logging", value: "true", type: "toggle" },
+ { key: "ip_allowlist", label: "IP allowlist", value: "", type: "text", description: "Comma-separated CIDRs. Leave empty to allow all." },
+ ],
+ },
+];
+
+function ToggleSwitch({ enabled }: { enabled: boolean }) {
+ return (
+
+ );
+}
+
+function SettingRow({ field }: { field: SettingField }) {
+ return (
+
+
+
{field.label}
+ {field.description != null && (
+
{field.description}
+ )}
+
+
+
+ {field.type === "toggle" ? (
+
+ ) : field.type === "select" ? (
+
+ ) : (
+
+ )}
+
+
+ );
+}
+
+function SettingsSection({ group }: { group: SettingGroup }) {
+ return (
+
+
+
{group.name}
+
{group.description}
+
+
+ {group.fields.map((field) => (
+
+ ))}
+
+
+ );
+}
+
+export default function Settings() {
+ return (
+
+ {settingGroups.map((group) => (
+
+ ))}
+
+ );
}