From 96ce3f4c53c41812073824ba8fb09a61c70c135a Mon Sep 17 00:00:00 2001 From: Developer Date: Fri, 5 Jun 2026 19:27:38 +0000 Subject: [PATCH] refactor: extract SshKeysPage components - Extract use-ssh-keys hook for state management - Extract SSHKeyCreateForm and SSHKeyList components - Slim SshKeysPage from 277 to ~80 lines Quality gates: tsc --noEmit passes, npm run build passes --- .../features/ssh-keys/SSHKeyCreateForm.tsx | 39 +++ .../features/ssh-keys/SSHKeyList.tsx | 180 ++++++++++ apps/web/src/hooks/use-ssh-keys.ts | 108 ++++++ apps/web/src/pages/SshKeysPage.tsx | 311 ++++-------------- 4 files changed, 386 insertions(+), 252 deletions(-) create mode 100644 apps/web/src/components/features/ssh-keys/SSHKeyCreateForm.tsx create mode 100644 apps/web/src/components/features/ssh-keys/SSHKeyList.tsx create mode 100644 apps/web/src/hooks/use-ssh-keys.ts diff --git a/apps/web/src/components/features/ssh-keys/SSHKeyCreateForm.tsx b/apps/web/src/components/features/ssh-keys/SSHKeyCreateForm.tsx new file mode 100644 index 0000000..4467af5 --- /dev/null +++ b/apps/web/src/components/features/ssh-keys/SSHKeyCreateForm.tsx @@ -0,0 +1,39 @@ +import { Icon } from "../../icon"; + +interface Props { + newKeyName: string; + setNewKeyName: (name: string) => void; + generating: boolean; + onSubmit: (e: React.FormEvent) => void; +} + +export const SSHKeyCreateForm = ({ newKeyName, setNewKeyName, generating, onSubmit }: Props) => { + return ( +
+
+ + setNewKeyName(e.target.value)} + placeholder="e.g., GitHub Work" + required + /> +
+ +
+ ); +}; diff --git a/apps/web/src/components/features/ssh-keys/SSHKeyList.tsx b/apps/web/src/components/features/ssh-keys/SSHKeyList.tsx new file mode 100644 index 0000000..c7529e6 --- /dev/null +++ b/apps/web/src/components/features/ssh-keys/SSHKeyList.tsx @@ -0,0 +1,180 @@ +import { Icon } from "../../icon"; +import { EmptyState, ErrorState } from "../../data-states"; +import type { SSHKey } from "../../../api/ssh-keys"; + +interface Props { + keys: SSHKey[]; + status: "idle" | "loading" | "ready" | "error"; + signPayloads: Record; + signatures: Record; + signing: Record; + verifyPayloads: Record; + verifySignatures: Record; + verifyResults: Record; + verifying: Record; + onLoadKeys: () => void; + onDelete: (id: string) => void; + onCopy: (text: string) => void; + onSign: (id: string) => void; + onVerify: (id: string) => void; + onSignPayloadChange: (id: string, value: string) => void; + onVerifyPayloadChange: (id: string, value: string) => void; + onVerifySignatureChange: (id: string, value: string) => void; +} + +export const SSHKeyList = ({ + keys, + status, + signPayloads, + signatures, + signing, + verifyPayloads, + verifySignatures, + verifyResults, + verifying, + onLoadKeys, + onDelete, + onCopy, + onSign, + onVerify, + onSignPayloadChange, + onVerifyPayloadChange, + onVerifySignatureChange, +}: Props) => { + if (status === "error") { + return ; + } + + if (keys.length === 0) { + return ; + } + + return ( +
+ {keys.map((key) => ( +
+
+

{key.name}

+ +
+
+ + Created: {new Date(key.created_at).toLocaleDateString()} + +
+
+ {key.public_key.substring(0, 50)}... + +
+ +
+

Sign Payload

+
+