File size: 3,885 Bytes
0b9dc2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { CircleAlert, Loader2, Save } from 'lucide-react';
import { useState, useEffect } from 'react';

import { credentialApi } from '@/api';
import type { CredentialRecord, CredentialSchema } from '@/api';
import { SchemaForm, type SchemaFormValue } from '@/components/form/SchemaForm';
import { Button } from '@/components/ui/button';
import {
	Dialog,
	DialogContent,
	DialogHeader,
	DialogTitle,
	DialogDescription,
	DialogFooter,
} from '@/components/ui/dialog';
import { useCredentials } from '@/hooks/useCredentials';
import { useTranslation } from '@/i18n/useI18n';

interface Props {
	open: boolean;
	onOpenChange: (open: boolean) => void;
	credential: CredentialRecord;
	onUpdated?: () => void;
}

export function EditCredentialDialog({ open, onOpenChange, credential, onUpdated }: Props) {
	const { update } = useCredentials();
	const { t } = useTranslation();
	const [schema, setSchema] = useState<CredentialSchema | null>(null);
	const [loadingSchema, setLoadingSchema] = useState(false);
	const [values, setValues] = useState<Record<string, SchemaFormValue>>({});
	const [submitting, setSubmitting] = useState(false);

	const type = credential.data.type as string | undefined;

	useEffect(() => {
		if (!open || !type) return;
		setLoadingSchema(true);
		credentialApi
			.schemas()
			.then((res) => {
				const matched = res.schemas.find(
					(s) => (s.properties.type?.const as string) === type,
				);
				setSchema(matched ?? null);
				// Pre-fill values from existing credential data (excluding writeOnly fields)
				if (matched) {
					const prefill: Record<string, SchemaFormValue> = {};
					for (const [key, prop] of Object.entries(matched.properties)) {
						if (key === 'id' || key === 'type' || prop.const !== undefined) continue;
						if (prop.writeOnly) continue; // don't pre-fill secrets like api_key
						const existing = credential.data[key];
						if (existing !== undefined) {
							prefill[key] = existing as SchemaFormValue;
						}
					}
					setValues(prefill);
				}
			})
			.finally(() => setLoadingSchema(false));
	}, [open, type, credential.data]);

	const handleSubmit = async () => {
		if (!schema) return;
		setSubmitting(true);
		try {
			const data: Record<string, unknown> = { ...credential.data };
			for (const [key, prop] of Object.entries(schema.properties)) {
				if (key === 'id' || key === 'type' || prop.const !== undefined) continue;
				const val = values[key];
				if (val !== undefined && val !== '') data[key] = val;
			}
			await update(credential.id, { data });
			onOpenChange(false);
			onUpdated?.();
		} finally {
			setSubmitting(false);
		}
	};

	return (
		<Dialog open={open} onOpenChange={onOpenChange}>

			<DialogContent className="!w-[500px] !max-w-[500px]">

				<DialogHeader>

					<DialogTitle>{t('dialog-credential-edit.title')}</DialogTitle>

					<DialogDescription>{t('dialog-credential-edit.description')}</DialogDescription>

				</DialogHeader>

				{loadingSchema ? (

					<p className="text-muted-foreground text-sm">{t('common.loading')}</p>

				) : schema ? (

					<SchemaForm

						schema={schema}

						values={values}

						onChange={(key, val) => setValues((prev) => ({ ...prev, [key]: val }))}

					/>

				) : null}

				<DialogFooter>

					<Button

						variant="ghost"

						onClick={() => onOpenChange(false)}

						disabled={submitting}

					>

						<CircleAlert className="size-3.5" />

						{t('common.cancel')}

					</Button>

					<Button onClick={handleSubmit} disabled={submitting || !schema}>

						{submitting ? (

							<Loader2 className="size-3.5 animate-spin" />

						) : (

							<Save className="size-3.5" />

						)}

						{submitting ? t('common.saving') : t('common.save')}

					</Button>

				</DialogFooter>

			</DialogContent>

		</Dialog>
	);
}