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 |
}
|