@mdguggenbichler/slugbase-core 0.0.19 → 0.0.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import api from '../../api/client';
|
|
4
4
|
import { useToast } from '../ui/Toast';
|
|
5
5
|
import { useAppConfig } from '../../contexts/AppConfigContext';
|
|
6
|
-
import {
|
|
7
|
-
import Button from '../ui/Button';
|
|
6
|
+
import { Sparkles } from 'lucide-react';
|
|
8
7
|
import { PageLoadingSkeleton } from '../ui/PageLoadingSkeleton';
|
|
9
8
|
import { Switch } from '../ui/switch';
|
|
10
9
|
import { Label } from '../ui/label';
|
|
@@ -20,7 +19,6 @@ export default function AdminAI() {
|
|
|
20
19
|
const { showToast } = useToast();
|
|
21
20
|
const { adminAiOnlyToggle } = useAppConfig();
|
|
22
21
|
const [loading, setLoading] = useState(true);
|
|
23
|
-
const [saving, setSaving] = useState(false);
|
|
24
22
|
const [settings, setSettings] = useState({
|
|
25
23
|
ai_enabled: false,
|
|
26
24
|
ai_provider: 'openai',
|
|
@@ -107,28 +105,29 @@ export default function AdminAI() {
|
|
|
107
105
|
? modelOptions
|
|
108
106
|
: [{ value: settings.ai_model, label: settings.ai_model }, ...modelOptions];
|
|
109
107
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
108
|
+
const saveSettings = useCallback(
|
|
109
|
+
async (payload: { ai_enabled?: boolean; ai_provider?: string; ai_model?: string; ai_api_key?: string }) => {
|
|
110
|
+
try {
|
|
111
|
+
const body = adminAiOnlyToggle
|
|
112
|
+
? { ai_enabled: payload.ai_enabled ?? settings.ai_enabled }
|
|
113
|
+
: {
|
|
114
|
+
...(payload.ai_enabled !== undefined && { ai_enabled: payload.ai_enabled }),
|
|
115
|
+
...(payload.ai_provider !== undefined && { ai_provider: payload.ai_provider }),
|
|
116
|
+
...(payload.ai_model !== undefined && { ai_model: payload.ai_model }),
|
|
117
|
+
...(payload.ai_api_key !== undefined && payload.ai_api_key !== '' && { ai_api_key: payload.ai_api_key }),
|
|
118
|
+
};
|
|
119
|
+
if (adminAiOnlyToggle && payload.ai_enabled === undefined) return;
|
|
120
|
+
if (!adminAiOnlyToggle && Object.keys(body).length === 0) return;
|
|
121
|
+
await api.post('/admin/settings/ai', body);
|
|
122
|
+
showToast(t('common.success'), 'success');
|
|
123
|
+
await loadSettings();
|
|
124
|
+
} catch (err: unknown) {
|
|
125
|
+
const e = err as { response?: { data?: { error?: string } } };
|
|
126
|
+
showToast(e?.response?.data?.error || t('common.error'), 'error');
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[adminAiOnlyToggle, settings.ai_enabled, showToast, t, loadSettings]
|
|
130
|
+
);
|
|
132
131
|
|
|
133
132
|
if (loading) {
|
|
134
133
|
return <PageLoadingSkeleton lines={8} />;
|
|
@@ -149,14 +148,15 @@ export default function AdminAI() {
|
|
|
149
148
|
</div>
|
|
150
149
|
</div>
|
|
151
150
|
|
|
152
|
-
<
|
|
151
|
+
<div className="space-y-4">
|
|
153
152
|
<div className="flex items-center gap-3">
|
|
154
153
|
<Switch
|
|
155
154
|
id="ai-enabled"
|
|
156
155
|
checked={settings.ai_enabled}
|
|
157
|
-
onCheckedChange={(checked) =>
|
|
158
|
-
setSettings({ ...
|
|
159
|
-
|
|
156
|
+
onCheckedChange={(checked) => {
|
|
157
|
+
setSettings((s) => ({ ...s, ai_enabled: checked }));
|
|
158
|
+
saveSettings({ ai_enabled: checked });
|
|
159
|
+
}}
|
|
160
160
|
/>
|
|
161
161
|
<Label htmlFor="ai-enabled" className="text-sm font-medium text-gray-900 dark:text-white cursor-pointer">
|
|
162
162
|
{t('admin.ai.enabled')}
|
|
@@ -171,7 +171,10 @@ export default function AdminAI() {
|
|
|
171
171
|
</Label>
|
|
172
172
|
<Select
|
|
173
173
|
value={settings.ai_provider}
|
|
174
|
-
onChange={(value) =>
|
|
174
|
+
onChange={(value) => {
|
|
175
|
+
setSettings((s) => ({ ...s, ai_provider: value }));
|
|
176
|
+
saveSettings({ ai_provider: value });
|
|
177
|
+
}}
|
|
175
178
|
options={providerOptions}
|
|
176
179
|
className="max-w-xs"
|
|
177
180
|
/>
|
|
@@ -184,7 +187,12 @@ export default function AdminAI() {
|
|
|
184
187
|
<Input
|
|
185
188
|
type="password"
|
|
186
189
|
value={settings.ai_api_key}
|
|
187
|
-
onChange={(e) => setSettings({ ...
|
|
190
|
+
onChange={(e) => setSettings((s) => ({ ...s, ai_api_key: e.target.value }))}
|
|
191
|
+
onBlur={() => {
|
|
192
|
+
if (settings.ai_api_key.trim() !== '') {
|
|
193
|
+
saveSettings({ ai_api_key: settings.ai_api_key });
|
|
194
|
+
}
|
|
195
|
+
}}
|
|
188
196
|
placeholder={settings.ai_api_key_set ? t('admin.ai.apiKeyPlaceholder') : 'sk-...'}
|
|
189
197
|
className="max-w-md font-mono text-sm"
|
|
190
198
|
/>
|
|
@@ -199,7 +207,10 @@ export default function AdminAI() {
|
|
|
199
207
|
</Label>
|
|
200
208
|
<Select
|
|
201
209
|
value={settings.ai_model}
|
|
202
|
-
onChange={(value) =>
|
|
210
|
+
onChange={(value) => {
|
|
211
|
+
setSettings((s) => ({ ...s, ai_model: value }));
|
|
212
|
+
saveSettings({ ai_model: value });
|
|
213
|
+
}}
|
|
203
214
|
options={modelOptionsWithCurrent}
|
|
204
215
|
placeholder={
|
|
205
216
|
!settings.ai_api_key_set
|
|
@@ -214,18 +225,7 @@ export default function AdminAI() {
|
|
|
214
225
|
</div>
|
|
215
226
|
</>
|
|
216
227
|
)}
|
|
217
|
-
|
|
218
|
-
<div className="pt-2">
|
|
219
|
-
<Button type="submit" variant="primary" disabled={saving}>
|
|
220
|
-
{saving ? t('common.loading') : (
|
|
221
|
-
<>
|
|
222
|
-
<Save className="h-4 w-4 mr-2 inline" />
|
|
223
|
-
{t('common.save')}
|
|
224
|
-
</>
|
|
225
|
-
)}
|
|
226
|
-
</Button>
|
|
227
|
-
</div>
|
|
228
|
-
</form>
|
|
228
|
+
</div>
|
|
229
229
|
</div>
|
|
230
230
|
</div>
|
|
231
231
|
);
|