nca-ai-cms-astro-plugin 1.0.6 → 1.0.7

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nca-ai-cms-astro-plugin",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./src/index.ts",
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect, useCallback } from 'react';
2
- import type { Prompt, SettingsSubTab } from './types';
2
+ import type { Prompt, Setting, SettingsSubTab } from './types';
3
3
  import { styles } from './styles';
4
4
 
5
5
  const SUB_TABS: { key: SettingsSubTab; label: string }[] = [
@@ -10,11 +10,30 @@ const SUB_TABS: { key: SettingsSubTab; label: string }[] = [
10
10
  { key: 'website', label: 'Website' },
11
11
  ];
12
12
 
13
+ const SETTINGS_TABS: SettingsSubTab[] = ['homepage', 'website'];
14
+
15
+ const SETTINGS_FIELDS: Record<string, { key: string; label: string; type: 'input' | 'textarea' }[]> = {
16
+ homepage: [
17
+ { key: 'hero_headline', label: 'Hero Ueberschrift', type: 'input' },
18
+ { key: 'hero_text', label: 'Hero Text', type: 'textarea' },
19
+ { key: 'target_audience', label: 'Zielgruppe', type: 'input' },
20
+ { key: 'tone', label: 'Tonalitaet', type: 'input' },
21
+ { key: 'core_message', label: 'Kernbotschaft', type: 'textarea' },
22
+ ],
23
+ website: [
24
+ { key: 'cta_url', label: 'CTA Link', type: 'input' },
25
+ { key: 'cta_style', label: 'CTA Stil', type: 'input' },
26
+ { key: 'cta_prompt', label: 'CTA Prompt', type: 'textarea' },
27
+ { key: 'core_tags', label: 'Core Tags (kommagetrennt)', type: 'input' },
28
+ { key: 'brand_guidelines', label: 'Markenrichtlinien', type: 'textarea' },
29
+ ],
30
+ };
31
+
13
32
  const CATEGORY_GUIDES: Record<SettingsSubTab, { title: string; description: string; example: string }> = {
14
33
  'homepage': {
15
34
  title: 'Homepage-Einstellungen',
16
- description: 'Definiere hier Prompts, die das Erscheinungsbild und den Inhalt deiner Startseite steuern. Gute Prompts beschreiben Zielgruppe, Tonalitaet und zentrale Botschaft.',
17
- example: 'Beispiel: "Schreibe einen Hero-Text fuer eine Webdesign-Agentur. Zielgruppe: KMU in Deutschland. Ton: professionell, nahbar. Kernbotschaft: Barrierefreies Webdesign als Wettbewerbsvorteil."',
35
+ description: 'Konfiguriere hier die zentralen Inhalte deiner Startseite: Hero-Text, Zielgruppe, Tonalitaet und Kernbotschaft.',
36
+ example: '',
18
37
  },
19
38
  'content-ai': {
20
39
  title: 'Content-KI Prompts',
@@ -33,11 +52,15 @@ const CATEGORY_GUIDES: Record<SettingsSubTab, { title: string; description: stri
33
52
  },
34
53
  'website': {
35
54
  title: 'Website-Einstellungen',
36
- description: 'Allgemeine Einstellungen fuer deine Website: CTA-Texte, Standard-Tags, Markenrichtlinien und wiederkehrende Textbausteine.',
37
- example: 'Beispiel: "Standard-CTA: Jetzt kostenlos beraten lassen. Core-Tags: Barrierefreiheit, Webdesign, SEO, WCAG. Markensprache: Sie-Anrede, professionell, loesungsorientiert."',
55
+ description: 'Allgemeine Einstellungen fuer deine Website: CTA-Texte, Standard-Tags und Markenrichtlinien.',
56
+ example: '',
38
57
  },
39
58
  };
40
59
 
60
+ function isSettingsTab(tab: SettingsSubTab): boolean {
61
+ return SETTINGS_TABS.includes(tab);
62
+ }
63
+
41
64
  interface EditState {
42
65
  id: string;
43
66
  value: string;
@@ -52,6 +75,9 @@ export function SettingsTab() {
52
75
  const [activeSubTab, setActiveSubTab] =
53
76
  useState<SettingsSubTab>('homepage');
54
77
  const [prompts, setPrompts] = useState<Prompt[]>([]);
78
+ const [_settings, setSettings] = useState<Setting[]>([]);
79
+ const [settingsForm, setSettingsForm] = useState<Record<string, string>>({});
80
+ const [settingsSaved, setSettingsSaved] = useState(false);
55
81
  const [loading, setLoading] = useState(true);
56
82
  const [error, setError] = useState<string | null>(null);
57
83
  const [editing, setEditing] = useState<EditState | null>(null);
@@ -60,7 +86,7 @@ export function SettingsTab() {
60
86
  const [addForm, setAddForm] = useState<AddFormState>({ name: '', promptText: '' });
61
87
  const [deleting, setDeleting] = useState<string | null>(null);
62
88
 
63
- const loadPrompts = useCallback(async () => {
89
+ const loadData = useCallback(async () => {
64
90
  setLoading(true);
65
91
  setError(null);
66
92
  try {
@@ -68,6 +94,13 @@ export function SettingsTab() {
68
94
  if (!res.ok) throw new Error('Fehler beim Laden der Einstellungen');
69
95
  const data = await res.json();
70
96
  setPrompts(Array.isArray(data.prompts) ? data.prompts : []);
97
+ const loadedSettings: Setting[] = Array.isArray(data.settings) ? data.settings : [];
98
+ setSettings(loadedSettings);
99
+ const formValues: Record<string, string> = {};
100
+ for (const s of loadedSettings) {
101
+ formValues[s.key] = s.value;
102
+ }
103
+ setSettingsForm(formValues);
71
104
  } catch (err) {
72
105
  setError(
73
106
  err instanceof Error ? err.message : 'Ein Fehler ist aufgetreten',
@@ -78,8 +111,8 @@ export function SettingsTab() {
78
111
  }, []);
79
112
 
80
113
  useEffect(() => {
81
- loadPrompts();
82
- }, [loadPrompts]);
114
+ loadData();
115
+ }, [loadData]);
83
116
 
84
117
  const filteredPrompts = prompts.filter(
85
118
  (p) => p.category === activeSubTab,
@@ -122,6 +155,32 @@ export function SettingsTab() {
122
155
  }
123
156
  };
124
157
 
158
+ const handleSettingsSave = async () => {
159
+ setSaving(true);
160
+ setError(null);
161
+ setSettingsSaved(false);
162
+ try {
163
+ const fields = SETTINGS_FIELDS[activeSubTab] ?? [];
164
+ for (const field of fields) {
165
+ const value = settingsForm[field.key] ?? '';
166
+ const res = await fetch('/api/prompts', {
167
+ method: 'POST',
168
+ headers: { 'Content-Type': 'application/json' },
169
+ body: JSON.stringify({ type: 'setting', key: field.key, value }),
170
+ });
171
+ if (!res.ok) throw new Error(`Fehler beim Speichern von "${field.label}"`);
172
+ }
173
+ setSettingsSaved(true);
174
+ setTimeout(() => setSettingsSaved(false), 3000);
175
+ } catch (err) {
176
+ setError(
177
+ err instanceof Error ? err.message : 'Fehler beim Speichern',
178
+ );
179
+ } finally {
180
+ setSaving(false);
181
+ }
182
+ };
183
+
125
184
  const handleAdd = async () => {
126
185
  if (!addForm.name.trim() || !addForm.promptText.trim()) return;
127
186
  setSaving(true);
@@ -140,7 +199,7 @@ export function SettingsTab() {
140
199
  if (!res.ok) throw new Error('Fehler beim Erstellen');
141
200
  setAddForm({ name: '', promptText: '' });
142
201
  setShowAddForm(false);
143
- await loadPrompts();
202
+ await loadData();
144
203
  } catch (err) {
145
204
  setError(
146
205
  err instanceof Error ? err.message : 'Fehler beim Erstellen',
@@ -191,6 +250,7 @@ export function SettingsTab() {
191
250
  setActiveSubTab(tab.key);
192
251
  setEditing(null);
193
252
  setShowAddForm(false);
253
+ setSettingsSaved(false);
194
254
  }}
195
255
  >
196
256
  {tab.label}
@@ -204,136 +264,189 @@ export function SettingsTab() {
204
264
 
205
265
  {error && <div style={styles.error}>{error}</div>}
206
266
 
207
- {!loading && filteredPrompts.length === 0 && !showAddForm && (
208
- <div style={styles.emptyGuide}>
209
- <div style={styles.emptyGuideTitle}>{guide.title}</div>
210
- <div style={styles.emptyGuideText}>{guide.description}</div>
211
- <div style={styles.emptyGuideExample}>{guide.example}</div>
212
- </div>
213
- )}
214
-
215
- {!loading && filteredPrompts.length > 0 && (
216
- <div style={styles.cardGrid}>
217
- {filteredPrompts.map((prompt) => (
218
- <div key={prompt.id} style={styles.settingsCard}>
219
- <div style={styles.settingsCardHeader}>
220
- <div style={styles.settingsItemLabel}>{prompt.name}</div>
221
- <div style={styles.settingsItemCategory}>
222
- {prompt.category}
223
- </div>
224
- </div>
225
-
226
- {editing?.id === prompt.id ? (
227
- <div style={styles.editArea}>
228
- <textarea
229
- style={styles.textarea}
230
- value={editing.value}
231
- onChange={(e) =>
232
- setEditing({ ...editing, value: e.target.value })
233
- }
234
- />
235
- <div style={styles.editButtons}>
236
- <button
237
- type="button"
238
- style={styles.saveButton}
239
- onClick={handleSave}
240
- disabled={saving}
241
- >
242
- {saving ? 'Speichere...' : 'Speichern'}
243
- </button>
244
- <button
245
- type="button"
246
- style={styles.cancelButton}
247
- onClick={handleCancel}
248
- >
249
- Abbrechen
250
- </button>
251
- </div>
252
- </div>
267
+ {/* Settings tabs: Homepage, Website key-value form */}
268
+ {!loading && isSettingsTab(activeSubTab) && (
269
+ <div style={styles.addForm}>
270
+ <div style={styles.addFormTitle}>{guide.title}</div>
271
+ <div style={{ ...styles.emptyGuideText, marginBottom: '1rem' }}>{guide.description}</div>
272
+ {(SETTINGS_FIELDS[activeSubTab] ?? []).map((field) => (
273
+ <div key={field.key} style={styles.addFormField}>
274
+ <label style={styles.addFormLabel}>{field.label}</label>
275
+ {field.type === 'textarea' ? (
276
+ <textarea
277
+ style={styles.textarea}
278
+ value={settingsForm[field.key] ?? ''}
279
+ onChange={(e) =>
280
+ setSettingsForm({ ...settingsForm, [field.key]: e.target.value })
281
+ }
282
+ />
253
283
  ) : (
254
- <>
255
- <div style={styles.settingsItemValue}>
256
- {prompt.promptText}
257
- </div>
258
- <div style={{ padding: '0 1rem 1rem', display: 'flex', gap: '0.5rem' }}>
259
- <button
260
- type="button"
261
- style={styles.editButton}
262
- onClick={() => handleEdit(prompt)}
263
- >
264
- Bearbeiten
265
- </button>
266
- <button
267
- type="button"
268
- style={styles.deleteButton}
269
- onClick={() => handleDelete(prompt.id)}
270
- disabled={deleting === prompt.id}
271
- >
272
- {deleting === prompt.id ? 'Loesche...' : 'Loeschen'}
273
- </button>
274
- </div>
275
- </>
284
+ <input
285
+ style={styles.addFormInput}
286
+ type="text"
287
+ value={settingsForm[field.key] ?? ''}
288
+ onChange={(e) =>
289
+ setSettingsForm({ ...settingsForm, [field.key]: e.target.value })
290
+ }
291
+ />
276
292
  )}
277
293
  </div>
278
294
  ))}
279
- </div>
280
- )}
281
-
282
- {!loading && showAddForm && (
283
- <div style={styles.addForm}>
284
- <div style={styles.addFormTitle}>Neuen Prompt hinzufuegen ({guide.title})</div>
285
- <div style={styles.addFormField}>
286
- <label style={styles.addFormLabel}>Name</label>
287
- <input
288
- style={styles.addFormInput}
289
- type="text"
290
- placeholder="z.B. Blog-Artikel Prompt, SEO-Analyse, Hero-Text"
291
- value={addForm.name}
292
- onChange={(e) => setAddForm({ ...addForm, name: e.target.value })}
293
- />
294
- </div>
295
- <div style={styles.addFormField}>
296
- <label style={styles.addFormLabel}>Prompt-Text</label>
297
- <textarea
298
- style={styles.textarea}
299
- placeholder={guide.example}
300
- value={addForm.promptText}
301
- onChange={(e) => setAddForm({ ...addForm, promptText: e.target.value })}
302
- />
303
- </div>
304
295
  <div style={styles.editButtons}>
305
296
  <button
306
297
  type="button"
307
298
  style={styles.addButton}
308
- onClick={handleAdd}
309
- disabled={saving || !addForm.name.trim() || !addForm.promptText.trim()}
310
- >
311
- {saving ? 'Erstelle...' : 'Erstellen'}
312
- </button>
313
- <button
314
- type="button"
315
- style={styles.cancelButton}
316
- onClick={() => {
317
- setShowAddForm(false);
318
- setAddForm({ name: '', promptText: '' });
319
- }}
299
+ onClick={handleSettingsSave}
300
+ disabled={saving}
320
301
  >
321
- Abbrechen
302
+ {saving ? 'Speichere...' : 'Einstellungen speichern'}
322
303
  </button>
304
+ {settingsSaved && (
305
+ <span style={{ color: '#4ade80', fontSize: '0.85rem', alignSelf: 'center' }}>
306
+ Gespeichert
307
+ </span>
308
+ )}
323
309
  </div>
324
310
  </div>
325
311
  )}
326
312
 
327
- {!loading && !showAddForm && (
328
- <div style={{ textAlign: 'center' as const, padding: '1rem 0' }}>
329
- <button
330
- type="button"
331
- style={styles.addButton}
332
- onClick={() => setShowAddForm(true)}
333
- >
334
- + Neuen Prompt hinzufuegen
335
- </button>
336
- </div>
313
+ {/* Prompt tabs: Content-KI, Analyse-KI, Bild-KI — prompt cards */}
314
+ {!loading && !isSettingsTab(activeSubTab) && (
315
+ <>
316
+ {filteredPrompts.length === 0 && !showAddForm && (
317
+ <div style={styles.emptyGuide}>
318
+ <div style={styles.emptyGuideTitle}>{guide.title}</div>
319
+ <div style={styles.emptyGuideText}>{guide.description}</div>
320
+ {guide.example && (
321
+ <div style={styles.emptyGuideExample}>{guide.example}</div>
322
+ )}
323
+ </div>
324
+ )}
325
+
326
+ {filteredPrompts.length > 0 && (
327
+ <div style={styles.cardGrid}>
328
+ {filteredPrompts.map((prompt) => (
329
+ <div key={prompt.id} style={styles.settingsCard}>
330
+ <div style={styles.settingsCardHeader}>
331
+ <div style={styles.settingsItemLabel}>{prompt.name}</div>
332
+ <div style={styles.settingsItemCategory}>
333
+ {prompt.category}
334
+ </div>
335
+ </div>
336
+
337
+ {editing?.id === prompt.id ? (
338
+ <div style={styles.editArea}>
339
+ <textarea
340
+ style={styles.textarea}
341
+ value={editing.value}
342
+ onChange={(e) =>
343
+ setEditing({ ...editing, value: e.target.value })
344
+ }
345
+ />
346
+ <div style={styles.editButtons}>
347
+ <button
348
+ type="button"
349
+ style={styles.saveButton}
350
+ onClick={handleSave}
351
+ disabled={saving}
352
+ >
353
+ {saving ? 'Speichere...' : 'Speichern'}
354
+ </button>
355
+ <button
356
+ type="button"
357
+ style={styles.cancelButton}
358
+ onClick={handleCancel}
359
+ >
360
+ Abbrechen
361
+ </button>
362
+ </div>
363
+ </div>
364
+ ) : (
365
+ <>
366
+ <div style={styles.settingsItemValue}>
367
+ {prompt.promptText}
368
+ </div>
369
+ <div style={{ padding: '0 1rem 1rem', display: 'flex', gap: '0.5rem' }}>
370
+ <button
371
+ type="button"
372
+ style={styles.editButton}
373
+ onClick={() => handleEdit(prompt)}
374
+ >
375
+ Bearbeiten
376
+ </button>
377
+ <button
378
+ type="button"
379
+ style={styles.deleteButton}
380
+ onClick={() => handleDelete(prompt.id)}
381
+ disabled={deleting === prompt.id}
382
+ >
383
+ {deleting === prompt.id ? 'Loesche...' : 'Loeschen'}
384
+ </button>
385
+ </div>
386
+ </>
387
+ )}
388
+ </div>
389
+ ))}
390
+ </div>
391
+ )}
392
+
393
+ {showAddForm && (
394
+ <div style={styles.addForm}>
395
+ <div style={styles.addFormTitle}>Neuen Prompt hinzufuegen ({guide.title})</div>
396
+ <div style={styles.addFormField}>
397
+ <label style={styles.addFormLabel}>Name</label>
398
+ <input
399
+ style={styles.addFormInput}
400
+ type="text"
401
+ placeholder="z.B. Blog-Artikel Prompt, SEO-Analyse, Hero-Text"
402
+ value={addForm.name}
403
+ onChange={(e) => setAddForm({ ...addForm, name: e.target.value })}
404
+ />
405
+ </div>
406
+ <div style={styles.addFormField}>
407
+ <label style={styles.addFormLabel}>Prompt-Text</label>
408
+ <textarea
409
+ style={styles.textarea}
410
+ placeholder={guide.example}
411
+ value={addForm.promptText}
412
+ onChange={(e) => setAddForm({ ...addForm, promptText: e.target.value })}
413
+ />
414
+ </div>
415
+ <div style={styles.editButtons}>
416
+ <button
417
+ type="button"
418
+ style={styles.addButton}
419
+ onClick={handleAdd}
420
+ disabled={saving || !addForm.name.trim() || !addForm.promptText.trim()}
421
+ >
422
+ {saving ? 'Erstelle...' : 'Erstellen'}
423
+ </button>
424
+ <button
425
+ type="button"
426
+ style={styles.cancelButton}
427
+ onClick={() => {
428
+ setShowAddForm(false);
429
+ setAddForm({ name: '', promptText: '' });
430
+ }}
431
+ >
432
+ Abbrechen
433
+ </button>
434
+ </div>
435
+ </div>
436
+ )}
437
+
438
+ {!showAddForm && (
439
+ <div style={{ textAlign: 'center' as const, padding: '1rem 0' }}>
440
+ <button
441
+ type="button"
442
+ style={styles.addButton}
443
+ onClick={() => setShowAddForm(true)}
444
+ >
445
+ + Neuen Prompt hinzufuegen
446
+ </button>
447
+ </div>
448
+ )}
449
+ </>
337
450
  )}
338
451
  </div>
339
452
  );
package/task.md CHANGED
@@ -13,7 +13,80 @@ Line 33-34: API `/api/prompts` returns `{ prompts, settings }` but the component
13
13
  + setPrompts(Array.isArray(data.prompts) ? data.prompts : []);
14
14
  ```
15
15
 
16
- ## 2. Auto-register dependencies in index.ts
16
+ ## 2. Separate Settings from Prompts in SettingsTab
17
+
18
+ **File:** `src/components/editor/SettingsTab.tsx`
19
+
20
+ The SettingsTab currently treats all 5 sub-tabs identically, showing the "Neuen Prompt hinzufuegen" (prompt CRUD) UI for every tab. But **Homepage** and **Website** are **site settings** (key-value pairs in `SiteSettings` table), not AI prompts. Only **Content-KI**, **Analyse-KI**, and **Bild-KI** should show the prompt CRUD interface.
21
+
22
+ ### Changes required:
23
+
24
+ 1. **Import `Setting` type** from `./types` (already defined there)
25
+
26
+ 2. **Add constants** at the top of the file:
27
+
28
+ ```ts
29
+ const SETTINGS_TABS: SettingsSubTab[] = ['homepage', 'website'];
30
+
31
+ const SETTINGS_FIELDS: Record<string, { key: string; label: string; type: 'input' | 'textarea' }[]> = {
32
+ homepage: [
33
+ { key: 'hero_headline', label: 'Hero Ueberschrift', type: 'input' },
34
+ { key: 'hero_text', label: 'Hero Text', type: 'textarea' },
35
+ { key: 'target_audience', label: 'Zielgruppe', type: 'input' },
36
+ { key: 'tone', label: 'Tonalitaet', type: 'input' },
37
+ { key: 'core_message', label: 'Kernbotschaft', type: 'textarea' },
38
+ ],
39
+ website: [
40
+ { key: 'cta_url', label: 'CTA Link', type: 'input' },
41
+ { key: 'cta_style', label: 'CTA Stil', type: 'input' },
42
+ { key: 'cta_prompt', label: 'CTA Prompt', type: 'textarea' },
43
+ { key: 'core_tags', label: 'Core Tags (kommagetrennt)', type: 'input' },
44
+ { key: 'brand_guidelines', label: 'Markenrichtlinien', type: 'textarea' },
45
+ ],
46
+ };
47
+ ```
48
+
49
+ 3. **Add helper function:**
50
+
51
+ ```ts
52
+ function isSettingsTab(tab: SettingsSubTab): boolean {
53
+ return SETTINGS_TABS.includes(tab);
54
+ }
55
+ ```
56
+
57
+ 4. **Add new state** in the component:
58
+
59
+ ```ts
60
+ const [settings, setSettings] = useState<Setting[]>([]);
61
+ const [settingsForm, setSettingsForm] = useState<Record<string, string>>({});
62
+ const [settingsSaved, setSettingsSaved] = useState(false);
63
+ ```
64
+
65
+ 5. **Update `loadPrompts`** to also load settings:
66
+
67
+ ```ts
68
+ const loadedSettings: Setting[] = Array.isArray(data.settings) ? data.settings : [];
69
+ setSettings(loadedSettings);
70
+ const formValues: Record<string, string> = {};
71
+ for (const s of loadedSettings) {
72
+ formValues[s.key] = s.value;
73
+ }
74
+ setSettingsForm(formValues);
75
+ ```
76
+
77
+ 6. **Add `handleSettingsSave`** function that POSTs each field with `{ type: 'setting', key, value }` to `/api/prompts`
78
+
79
+ 7. **Update the render/return** to branch on `isSettingsTab(activeSubTab)`:
80
+ - **Settings tabs** (homepage, website): render a key-value form with text inputs/textareas for each field defined in `SETTINGS_FIELDS`, plus a "Einstellungen speichern" button
81
+ - **Prompt tabs** (content-ai, analysis-ai, image-ai): keep existing prompt card grid + "Neuen Prompt hinzufuegen" button (unchanged)
82
+
83
+ 8. **Update `CATEGORY_GUIDES`** for homepage and website — change descriptions to reflect settings (not prompts), remove example text
84
+
85
+ ### API usage for settings
86
+ - Read: from `data.settings` array returned by `GET /api/prompts`, find by `key`
87
+ - Write: `POST /api/prompts` with `{ type: 'setting', key, value }`
88
+
89
+ ## 3. Auto-register dependencies in index.ts
17
90
 
18
91
  **File:** `src/index.ts`
19
92
 
@@ -45,13 +118,13 @@ The consumer currently must manually add `react()`, `db()`, `output: 'server'`,
45
118
  }
46
119
  ```
47
120
 
48
- ## 3. Bump version to 1.0.5
121
+ ## 4. Bump version to 1.0.5
49
122
 
50
123
  **File:** `package.json`
51
124
 
52
125
  Change `"version": "1.0.4"` to `"version": "1.0.5"`.
53
126
 
54
- ## 4. Update README with simplified setup
127
+ ## 5. Update README with simplified setup
55
128
 
56
129
  **File:** `README.md`
57
130
 
@@ -74,4 +147,8 @@ Note that `prerender: false` is already present on all 18 `injectRoute()` calls
74
147
  1. In a test project, use only `integrations: [ncaAiCms()]` in astro config
75
148
  2. Run `npx astro dev`
76
149
  3. Open `/login` — should render without errors
77
- 4. Log in, open `/editor` settings tab should load without crash
150
+ 4. Log in, open `/editor` Settings tab → Homepage: should show key-value form fields, no "Neuen Prompt hinzufuegen"
151
+ 5. Homepage tab → edit hero text → save → reload → value persists
152
+ 6. Website tab → edit CTA URL, core tags → save → reload → values persist
153
+ 7. Content-KI tab → still shows prompt cards with create/edit/delete
154
+ 8. Analyse-KI and Bild-KI → same prompt UI as before
package/update.md CHANGED
@@ -1,8 +1,14 @@
1
1
  # v1.0.6
2
2
 
3
- ## SettingsTab: category guides and custom prompts
4
- - Empty categories now show content marketing guidance with concrete examples per tab (Homepage, Content-KI, Analyse-KI, Bild-KI, Website)
5
- - New "+ Neuen Prompt hinzufuegen" button to create custom prompts directly in the UI
3
+ ## Separate settings from prompts in SettingsTab
4
+ - Homepage and Website tabs now show key-value settings forms (hero text, zielgruppe, CTA, core tags, etc.)
5
+ - Content-KI, Analyse-KI, and Bild-KI tabs show prompt card UI with create/edit/delete
6
+ - Settings are saved via `POST /api/prompts` with `type: setting`
7
+ - Each settings tab has defined fields: homepage (hero, zielgruppe, ton, kernbotschaft), website (CTA, tags, markenrichtlinien)
8
+
9
+ ## Category guides and custom prompts
10
+ - Empty prompt categories show content marketing guidance with concrete examples
11
+ - New "+ Neuen Prompt hinzufuegen" button to create custom prompts
6
12
  - Each prompt card now has a delete button
7
13
  - API: added POST with `action: create` and DELETE endpoint for prompts
8
14
  - PromptService: added `createPrompt()` and `deletePrompt()` methods