Skip to content
Source
Sign inSign up
Open History to see the last commit

SourceControlHostSettings.tsx

77 lines2.7 KB

1import type { EnvironmentId } from "@t3tools/contracts";
2import type { SourceControlDefinition } from "@t3tools/client-runtime/source-control-clients";
3import * as Equal from "effect/Equal";
4import { useState } from "react";
5​
6import { useEnvironmentSettings } from "../../hooks/useSettings";
7import { serverEnvironment } from "../../state/server";
8import { useAtomCommand } from "../../state/use-atom-command";
9import { Button } from "../ui/button";
10import { ProviderSettingsForm } from "./ProviderSettingsForm";
11​
12export function SourceControlHostSettings({
13 environmentId,
14 definition,
15 onSaved,
16}: {
17 readonly environmentId: EnvironmentId;
18 readonly definition: SourceControlDefinition & {
19 readonly settings: NonNullable<SourceControlDefinition["settings"]>;
20 };
21 readonly onSaved: () => void;
22}) {
23 const saved = useEnvironmentSettings(
24 environmentId,
25 (settings) => settings.sourceControlHosts[definition.kind],
26 );
27 const updateSettings = useAtomCommand(serverEnvironment.updateSettings, {
28 label: `save ${definition.label} settings`,
29 });
30 const [saving, setSaving] = useState(false);
31 const formDefinition = { settingsSchema: definition.settings };
32 const hasSaved = Object.values(saved ?? {}).some((value) => value !== "");
33​
34 const save = async (fields: Record<string, unknown>) => {
35 setSaving(true);
36 const result = await updateSettings({
37 environmentId,
38 input: { patch: { sourceControlHosts: { [definition.kind]: fields } } },
39 });
40 setSaving(false);
41 if (result._tag === "Success") onSaved();
42 };
43​
44 return (
45 // Locked while saving, so an edit made mid-request is not overwritten by the saved value.
46 <fieldset disabled={saving} className="contents">
47 <ProviderSettingsForm
48 definition={formDefinition}
49 value={saved ?? {}}
50 idPrefix={`source-control-${definition.kind}-${environmentId}`}
51 variant="settings"
52 environmentId={environmentId}
53 onChange={(next) => {
54 const fields = next ?? {};
55 const changed = Object.fromEntries(
56 Object.entries(fields).filter(([key, value]) => !Equal.equals(saved?.[key], value)),
57 );
58 const cleared = Object.keys(saved ?? {}).filter((key) => !(key in fields));
59 void save({ ...changed, ...Object.fromEntries(cleared.map((key) => [key, ""])) });
60 }}
61 />
62 {hasSaved ? (
63 <div className="flex justify-end pt-2">
64 <Button
65 size="xs"
66 variant="outline"
67 onClick={() =>
68 void save(Object.fromEntries(Object.keys(saved ?? {}).map((key) => [key, ""])))
69 }
70 >
71 Remove
72 </Button>
73 </div>
74 ) : null}
75 </fieldset>
76 );