diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 119a2f1c..6e86c76d 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -31,7 +31,6 @@ import { CompanySettings } from "./pages/CompanySettings"; import { CompanyEnvironments } from "./pages/CompanyEnvironments"; import { CompanyAccess } from "./pages/CompanyAccess"; import { CompanyInvites } from "./pages/CompanyInvites"; -import { CompanySecrets } from "./pages/CompanySecrets"; import { CompanySkills } from "./pages/CompanySkills"; import { Secrets } from "./pages/Secrets"; import { CompanyExport } from "./pages/CompanyExport"; @@ -71,7 +70,6 @@ function boardRoutes() { } /> } /> } /> - } /> } /> } /> } /> diff --git a/ui/src/components/CompanySettingsSidebar.tsx b/ui/src/components/CompanySettingsSidebar.tsx index 4adeb24d..027f2741 100644 --- a/ui/src/components/CompanySettingsSidebar.tsx +++ b/ui/src/components/CompanySettingsSidebar.tsx @@ -69,7 +69,6 @@ export function CompanySettingsSidebar() { end /> - diff --git a/ui/src/pages/CompanySecrets.tsx b/ui/src/pages/CompanySecrets.tsx deleted file mode 100644 index 65834276..00000000 --- a/ui/src/pages/CompanySecrets.tsx +++ /dev/null @@ -1,528 +0,0 @@ -import { useEffect, useState } from "react"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import type { CompanySecret } from "@paperclipai/shared"; -import { KeyRound, Pencil, RefreshCw, Trash2 } from "lucide-react"; -import { ApiError } from "@/api/client"; -import { secretsApi } from "@/api/secrets"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { useBreadcrumbs } from "@/context/BreadcrumbContext"; -import { useCompany } from "@/context/CompanyContext"; -import { useToast } from "@/context/ToastContext"; -import { cn, relativeTime } from "@/lib/utils"; - -type DialogMode = - | { kind: "closed" } - | { kind: "create" } - | { kind: "rotate"; secret: CompanySecret } - | { kind: "edit"; secret: CompanySecret } - | { kind: "delete"; secret: CompanySecret }; - -const COMPANY_SECRETS_QUERY_KEY = "company-secrets"; - -export function CompanySecrets() { - const { selectedCompany, selectedCompanyId } = useCompany(); - const { setBreadcrumbs } = useBreadcrumbs(); - const { pushToast } = useToast(); - const queryClient = useQueryClient(); - const [dialog, setDialog] = useState({ kind: "closed" }); - - useEffect(() => { - setBreadcrumbs([ - { label: selectedCompany?.name ?? "Company", href: "/dashboard" }, - { label: "Settings", href: "/company/settings" }, - { label: "Secrets" }, - ]); - }, [selectedCompany?.name, setBreadcrumbs]); - - const { data: secrets, isLoading } = useQuery({ - queryKey: selectedCompanyId - ? [COMPANY_SECRETS_QUERY_KEY, selectedCompanyId] - : [COMPANY_SECRETS_QUERY_KEY, "none"], - queryFn: () => secretsApi.list(selectedCompanyId!), - enabled: Boolean(selectedCompanyId), - }); - - function invalidateList() { - if (!selectedCompanyId) return; - queryClient.invalidateQueries({ - queryKey: [COMPANY_SECRETS_QUERY_KEY, selectedCompanyId], - }); - } - - function handleApiError(error: unknown, fallback: string) { - const message = error instanceof Error ? error.message : fallback; - pushToast({ tone: "error", title: fallback, body: message }); - } - - return ( -
-
-
- -

Company Secrets

-
-

- Encrypted values that agents can reference from environment variables. - Rotate to change a secret's value; delete to remove it from the company - library. -

-
- -
-

- {secrets?.length ?? 0} secret{secrets?.length === 1 ? "" : "s"} -

- -
- -
- {isLoading ? ( -
Loading…
- ) : !secrets || secrets.length === 0 ? ( -
- No secrets yet. Use New secret to - create one, or seal a plain env var from the agent configuration page. -
- ) : ( -
    - {secrets.map((secret) => ( -
  • -
    -
    {secret.name}
    - {secret.description ? ( -

    - {secret.description} -

    - ) : null} -

    - v{secret.latestVersion} · last rotated{" "} - {relativeTime(secret.updatedAt)} -

    -
    -
    - - - -
    -
  • - ))} -
- )} -
- - {dialog.kind === "create" && selectedCompanyId ? ( - setDialog({ kind: "closed" })} - onCreated={(name) => { - pushToast({ tone: "success", title: `Secret "${name}" created` }); - invalidateList(); - setDialog({ kind: "closed" }); - }} - onError={(error) => handleApiError(error, "Failed to create secret")} - /> - ) : null} - - {dialog.kind === "rotate" ? ( - setDialog({ kind: "closed" })} - onRotated={() => { - pushToast({ - tone: "success", - title: `Secret "${dialog.secret.name}" rotated`, - }); - invalidateList(); - setDialog({ kind: "closed" }); - }} - onError={(error) => handleApiError(error, "Failed to rotate secret")} - /> - ) : null} - - {dialog.kind === "edit" ? ( - setDialog({ kind: "closed" })} - onSaved={(name) => { - pushToast({ tone: "success", title: `Secret "${name}" updated` }); - invalidateList(); - setDialog({ kind: "closed" }); - }} - onError={(error) => handleApiError(error, "Failed to update secret")} - /> - ) : null} - - {dialog.kind === "delete" ? ( - setDialog({ kind: "closed" })} - onDeleted={() => { - pushToast({ - tone: "success", - title: `Secret "${dialog.secret.name}" deleted`, - }); - invalidateList(); - setDialog({ kind: "closed" }); - }} - /> - ) : null} -
- ); -} - -function CreateSecretDialog({ - companyId, - onClose, - onCreated, - onError, -}: { - companyId: string; - onClose: () => void; - onCreated: (name: string) => void; - onError: (error: unknown) => void; -}) { - const [name, setName] = useState(""); - const [value, setValue] = useState(""); - const [description, setDescription] = useState(""); - - const create = useMutation({ - mutationFn: () => - secretsApi.create(companyId, { - name: name.trim(), - value, - description: description.trim() || null, - }), - onSuccess: () => onCreated(name.trim()), - onError, - }); - - return ( - !open && onClose()}> - - - New secret - - Stored encrypted; visible to agents only when referenced from a - secret-typed environment variable. - - -
-
- - setName(event.target.value)} - placeholder="API_TOKEN" - autoFocus - /> -
-
- -