Spaces:
Running
Running
dynamic json schema
Browse files
frontend/src/pages/AdminPage/AdminPage.tsx
CHANGED
@@ -36,6 +36,14 @@ interface ImageTypeData {
|
|
36 |
label: string;
|
37 |
}
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
export default function AdminPage() {
|
40 |
const { isAuthenticated, isLoading, login, logout } = useAdmin();
|
41 |
const [password, setPassword] = useState('');
|
@@ -51,6 +59,17 @@ export default function AdminPage() {
|
|
51 |
|
52 |
const [imageTypes, setImageTypes] = useState<ImageTypeData[]>([]);
|
53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
// Prompt management state
|
55 |
const [showEditPromptForm, setShowEditPromptForm] = useState(false);
|
56 |
const [showAddPromptForm, setShowAddPromptForm] = useState(false);
|
@@ -161,13 +180,32 @@ export default function AdminPage() {
|
|
161 |
});
|
162 |
}, []);
|
163 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
useEffect(() => {
|
165 |
if (isAuthenticated) {
|
166 |
fetchModels();
|
167 |
fetchPrompts();
|
168 |
fetchImageTypes();
|
|
|
169 |
}
|
170 |
-
}, [isAuthenticated, fetchModels, fetchPrompts, fetchImageTypes]);
|
171 |
|
172 |
|
173 |
|
@@ -282,6 +320,66 @@ export default function AdminPage() {
|
|
282 |
}
|
283 |
};
|
284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
285 |
const toggleModelAvailability = async (modelCode: string, currentStatus: boolean) => {
|
286 |
try {
|
287 |
const response = await fetch(`/api/models/${modelCode}/toggle`, {
|
@@ -1080,6 +1178,64 @@ Model "${newModelData.label}" added successfully!
|
|
1080 |
</div>
|
1081 |
</Container>
|
1082 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1083 |
{/* Utilities Section */}
|
1084 |
<Container
|
1085 |
heading="Utilities"
|
@@ -1117,50 +1273,6 @@ Model "${newModelData.label}" added successfully!
|
|
1117 |
Test Connection
|
1118 |
</Button>
|
1119 |
|
1120 |
-
<Button
|
1121 |
-
name="view-schemas"
|
1122 |
-
variant="secondary"
|
1123 |
-
onClick={() => {
|
1124 |
-
fetch('/api/schemas', {
|
1125 |
-
headers: {
|
1126 |
-
'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
|
1127 |
-
}
|
1128 |
-
})
|
1129 |
-
.then(r => r.json())
|
1130 |
-
.then(data => {
|
1131 |
-
console.log('Schemas Response:', data);
|
1132 |
-
|
1133 |
-
let results = '';
|
1134 |
-
let title = 'Schemas Response';
|
1135 |
-
|
1136 |
-
if (data && Array.isArray(data)) {
|
1137 |
-
results = `Found ${data.length} schemas:\n\n`;
|
1138 |
-
data.forEach((schema, index) => {
|
1139 |
-
results += `=== Schema ${index + 1} ===\n`;
|
1140 |
-
results += JSON.stringify(schema, null, 2);
|
1141 |
-
results += '\n\n';
|
1142 |
-
});
|
1143 |
-
} else if (data && typeof data === 'object') {
|
1144 |
-
results = `Prompts Response:\n\nResponse type: ${typeof data}\nKeys: ${Object.keys(data).join(', ')}\n\nRaw data:\n${JSON.stringify(data, null, 2)}`;
|
1145 |
-
} else {
|
1146 |
-
results = `Prompts Response:\n\nUnexpected data type: ${typeof data}\nValue: ${data}`;
|
1147 |
-
}
|
1148 |
-
|
1149 |
-
setTestResults(results);
|
1150 |
-
setTestResultsTitle(title);
|
1151 |
-
setShowTestResultsModal(true);
|
1152 |
-
})
|
1153 |
-
.catch((error) => {
|
1154 |
-
console.error('Schemas Error:', error);
|
1155 |
-
const results = `Failed to fetch prompts: ${error.message || 'Unknown error'}`;
|
1156 |
-
setTestResults(results);
|
1157 |
-
setTestResultsTitle('Schemas Error');
|
1158 |
-
setShowTestResultsModal(true);
|
1159 |
-
});
|
1160 |
-
}}
|
1161 |
-
>
|
1162 |
-
View Schemas
|
1163 |
-
</Button>
|
1164 |
</div>
|
1165 |
</Container>
|
1166 |
</div>
|
@@ -1416,6 +1528,63 @@ Model "${newModelData.label}" added successfully!
|
|
1416 |
</div>
|
1417 |
</div>
|
1418 |
)}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1419 |
</PageContainer>
|
1420 |
);
|
1421 |
}
|
|
|
36 |
label: string;
|
37 |
}
|
38 |
|
39 |
+
interface SchemaData {
|
40 |
+
schema_id: string;
|
41 |
+
title: string;
|
42 |
+
version: string;
|
43 |
+
created_at?: string;
|
44 |
+
schema: any;
|
45 |
+
}
|
46 |
+
|
47 |
export default function AdminPage() {
|
48 |
const { isAuthenticated, isLoading, login, logout } = useAdmin();
|
49 |
const [password, setPassword] = useState('');
|
|
|
59 |
|
60 |
const [imageTypes, setImageTypes] = useState<ImageTypeData[]>([]);
|
61 |
|
62 |
+
// Schema management state
|
63 |
+
const [availableSchemas, setAvailableSchemas] = useState<SchemaData[]>([]);
|
64 |
+
const [showEditSchemaForm, setShowEditSchemaForm] = useState(false);
|
65 |
+
const [editingSchema, setEditingSchema] = useState<SchemaData | null>(null);
|
66 |
+
const [newSchemaData, setNewSchemaData] = useState<SchemaData>({
|
67 |
+
schema_id: '',
|
68 |
+
title: '',
|
69 |
+
version: '',
|
70 |
+
schema: {}
|
71 |
+
});
|
72 |
+
|
73 |
// Prompt management state
|
74 |
const [showEditPromptForm, setShowEditPromptForm] = useState(false);
|
75 |
const [showAddPromptForm, setShowAddPromptForm] = useState(false);
|
|
|
180 |
});
|
181 |
}, []);
|
182 |
|
183 |
+
const fetchSchemas = useCallback(() => {
|
184 |
+
console.log('=== fetchSchemas called ===');
|
185 |
+
fetch('/api/schemas', {
|
186 |
+
headers: {
|
187 |
+
'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
|
188 |
+
}
|
189 |
+
})
|
190 |
+
.then(r => r.json())
|
191 |
+
.then(schemasData => {
|
192 |
+
console.log('Schemas data received:', schemasData);
|
193 |
+
setAvailableSchemas(schemasData || []);
|
194 |
+
})
|
195 |
+
.catch((error) => {
|
196 |
+
console.error('Error fetching schemas:', error);
|
197 |
+
// Handle error silently
|
198 |
+
});
|
199 |
+
}, []);
|
200 |
+
|
201 |
useEffect(() => {
|
202 |
if (isAuthenticated) {
|
203 |
fetchModels();
|
204 |
fetchPrompts();
|
205 |
fetchImageTypes();
|
206 |
+
fetchSchemas();
|
207 |
}
|
208 |
+
}, [isAuthenticated, fetchModels, fetchPrompts, fetchImageTypes, fetchSchemas]);
|
209 |
|
210 |
|
211 |
|
|
|
320 |
}
|
321 |
};
|
322 |
|
323 |
+
// Schema management handlers
|
324 |
+
const handleEditSchema = (schema: SchemaData) => {
|
325 |
+
setEditingSchema(schema);
|
326 |
+
setNewSchemaData({
|
327 |
+
schema_id: schema.schema_id,
|
328 |
+
title: schema.title,
|
329 |
+
version: schema.version,
|
330 |
+
schema: schema.schema
|
331 |
+
});
|
332 |
+
setShowEditSchemaForm(true);
|
333 |
+
};
|
334 |
+
|
335 |
+
const handleSaveSchema = async () => {
|
336 |
+
try {
|
337 |
+
if (!editingSchema) {
|
338 |
+
alert('No schema selected for editing');
|
339 |
+
return;
|
340 |
+
}
|
341 |
+
|
342 |
+
const response = await fetch(`/api/schemas/${editingSchema.schema_id}`, {
|
343 |
+
method: 'PUT',
|
344 |
+
headers: {
|
345 |
+
'Content-Type': 'application/json',
|
346 |
+
'Authorization': `Bearer ${localStorage.getItem('adminToken')}`
|
347 |
+
},
|
348 |
+
body: JSON.stringify(newSchemaData),
|
349 |
+
});
|
350 |
+
|
351 |
+
if (response.ok) {
|
352 |
+
// Refresh schemas and close form
|
353 |
+
fetchSchemas();
|
354 |
+
setShowEditSchemaForm(false);
|
355 |
+
setEditingSchema(null);
|
356 |
+
setNewSchemaData({
|
357 |
+
schema_id: '',
|
358 |
+
title: '',
|
359 |
+
version: '',
|
360 |
+
schema: {}
|
361 |
+
});
|
362 |
+
} else {
|
363 |
+
const errorData = await response.json();
|
364 |
+
alert(`Failed to save schema: ${errorData.detail || 'Unknown error'}`);
|
365 |
+
}
|
366 |
+
} catch (error) {
|
367 |
+
console.error('Error saving schema:', error);
|
368 |
+
alert('Error saving schema');
|
369 |
+
}
|
370 |
+
};
|
371 |
+
|
372 |
+
const handleCancelSchema = () => {
|
373 |
+
setShowEditSchemaForm(false);
|
374 |
+
setEditingSchema(null);
|
375 |
+
setNewSchemaData({
|
376 |
+
schema_id: '',
|
377 |
+
title: '',
|
378 |
+
version: '',
|
379 |
+
schema: {}
|
380 |
+
});
|
381 |
+
};
|
382 |
+
|
383 |
const toggleModelAvailability = async (modelCode: string, currentStatus: boolean) => {
|
384 |
try {
|
385 |
const response = await fetch(`/api/models/${modelCode}/toggle`, {
|
|
|
1178 |
</div>
|
1179 |
</Container>
|
1180 |
|
1181 |
+
{/* Schema Management Section */}
|
1182 |
+
<Container
|
1183 |
+
heading="Schema Management"
|
1184 |
+
headingLevel={2}
|
1185 |
+
withHeaderBorder
|
1186 |
+
withInternalPadding
|
1187 |
+
>
|
1188 |
+
<div className={styles.modelManagementArea}>
|
1189 |
+
<div className={styles.modelsTable}>
|
1190 |
+
<table>
|
1191 |
+
<thead>
|
1192 |
+
<tr>
|
1193 |
+
<th>Schema ID</th>
|
1194 |
+
<th>Schema Content</th>
|
1195 |
+
<th>Actions</th>
|
1196 |
+
</tr>
|
1197 |
+
</thead>
|
1198 |
+
<tbody>
|
1199 |
+
{availableSchemas
|
1200 |
+
.sort((a, b) => a.schema_id.localeCompare(b.schema_id))
|
1201 |
+
.map(schema => (
|
1202 |
+
<tr key={schema.schema_id}>
|
1203 |
+
<td className={styles.modelCode}>{schema.schema_id}</td>
|
1204 |
+
<td className={styles.promptLabel} style={{ maxWidth: '400px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
|
1205 |
+
{JSON.stringify(schema.schema)}
|
1206 |
+
</td>
|
1207 |
+
<td>
|
1208 |
+
<div className={styles.modelActions}>
|
1209 |
+
<Button
|
1210 |
+
name={`view-schema-${schema.schema_id}`}
|
1211 |
+
variant="secondary"
|
1212 |
+
size={1}
|
1213 |
+
onClick={() => {
|
1214 |
+
setTestResults(`=== Schema Details ===\nSchema ID: ${schema.schema_id}\n\nSchema Definition:\n${JSON.stringify(schema.schema, null, 2)}`);
|
1215 |
+
setTestResultsTitle(`Schema: ${schema.schema_id}`);
|
1216 |
+
setShowTestResultsModal(true);
|
1217 |
+
}}
|
1218 |
+
>
|
1219 |
+
View
|
1220 |
+
</Button>
|
1221 |
+
<Button
|
1222 |
+
name={`edit-schema-${schema.schema_id}`}
|
1223 |
+
variant="secondary"
|
1224 |
+
size={1}
|
1225 |
+
onClick={() => handleEditSchema(schema)}
|
1226 |
+
>
|
1227 |
+
Edit
|
1228 |
+
</Button>
|
1229 |
+
</div>
|
1230 |
+
</td>
|
1231 |
+
</tr>
|
1232 |
+
))}
|
1233 |
+
</tbody>
|
1234 |
+
</table>
|
1235 |
+
</div>
|
1236 |
+
</div>
|
1237 |
+
</Container>
|
1238 |
+
|
1239 |
{/* Utilities Section */}
|
1240 |
<Container
|
1241 |
heading="Utilities"
|
|
|
1273 |
Test Connection
|
1274 |
</Button>
|
1275 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1276 |
</div>
|
1277 |
</Container>
|
1278 |
</div>
|
|
|
1528 |
</div>
|
1529 |
</div>
|
1530 |
)}
|
1531 |
+
|
1532 |
+
{/* Edit Schema Form Modal */}
|
1533 |
+
{showEditSchemaForm && (
|
1534 |
+
<div className={styles.modalOverlay} onClick={() => setShowEditSchemaForm(false)}>
|
1535 |
+
<div className={styles.modalContent} onClick={(e) => e.stopPropagation()}>
|
1536 |
+
<div className={styles.modalBody}>
|
1537 |
+
<h3 className={styles.modalTitle}>Edit Schema: {editingSchema?.schema_id}</h3>
|
1538 |
+
<div className={styles.modalForm}>
|
1539 |
+
<div className={styles.formField}>
|
1540 |
+
<label className={styles.formLabel}>Schema ID:</label>
|
1541 |
+
<TextInput
|
1542 |
+
name="schema-id"
|
1543 |
+
value={newSchemaData.schema_id}
|
1544 |
+
onChange={(value) => setNewSchemaData(prev => ({ ...prev, schema_id: value || '' }))}
|
1545 |
+
className={styles.formInput}
|
1546 |
+
disabled
|
1547 |
+
/>
|
1548 |
+
</div>
|
1549 |
+
<div className={styles.formField}>
|
1550 |
+
<label className={styles.formLabel}>Schema Definition (JSON):</label>
|
1551 |
+
<textarea
|
1552 |
+
name="schema-definition"
|
1553 |
+
value={JSON.stringify(newSchemaData.schema, null, 2)}
|
1554 |
+
onChange={(e) => {
|
1555 |
+
try {
|
1556 |
+
const parsedSchema = JSON.parse(e.target.value);
|
1557 |
+
setNewSchemaData(prev => ({ ...prev, schema: parsedSchema }));
|
1558 |
+
} catch (error) {
|
1559 |
+
// Invalid JSON, don't update
|
1560 |
+
}
|
1561 |
+
}}
|
1562 |
+
className={`${styles.formInput} ${styles.textarea}`}
|
1563 |
+
rows={20}
|
1564 |
+
style={{ fontFamily: 'monospace' }}
|
1565 |
+
/>
|
1566 |
+
</div>
|
1567 |
+
</div>
|
1568 |
+
<div className={styles.modalButtons}>
|
1569 |
+
<Button
|
1570 |
+
name="cancel-edit-schema"
|
1571 |
+
variant="tertiary"
|
1572 |
+
onClick={handleCancelSchema}
|
1573 |
+
>
|
1574 |
+
Cancel
|
1575 |
+
</Button>
|
1576 |
+
<Button
|
1577 |
+
name="save-schema"
|
1578 |
+
variant="primary"
|
1579 |
+
onClick={handleSaveSchema}
|
1580 |
+
>
|
1581 |
+
Save Changes
|
1582 |
+
</Button>
|
1583 |
+
</div>
|
1584 |
+
</div>
|
1585 |
+
</div>
|
1586 |
+
</div>
|
1587 |
+
)}
|
1588 |
</PageContainer>
|
1589 |
);
|
1590 |
}
|