Implement atomic collection item creation API with unified content engine approach

Updates collection creation to use database-first atomic operations for reliable collection item management. Replaces manual database calls with unified content engine methods that handle content extraction, storage, and structural template generation consistently.

Key changes:
- Replace manual database operations in CreateCollectionItem handler with DatabaseClient.CreateCollectionItemAtomic()
- Implement unified content engine approach for API-based collection item creation
- Add atomic collection item creation methods across all content clients
- Enhance reconstruction to use stored structural templates with content ID hydration
- Add comprehensive collection management API methods in JavaScript client
- Implement collection manager UI with create, delete, and reorder functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-23 18:39:37 +02:00
parent 5f494b8aa8
commit 1ae4176f23
8 changed files with 755 additions and 202 deletions

View File

@@ -141,6 +141,172 @@ export class ApiClient {
}
}
// =============================================================================
// Collection API Methods
// =============================================================================
/**
* Create a new collection item
* @param {string} collectionId - Collection ID
* @param {number} templateId - Template ID to use (defaults to 1)
* @param {string} htmlContent - Optional initial HTML content
* @returns {Promise<Object>} Created collection item
*/
async createCollectionItem(collectionId, templateId = 1, htmlContent = '') {
try {
const collectionsUrl = this.baseUrl.replace('/api/content', '/api/collections');
const payload = {
site_id: this.siteId,
template_id: templateId,
html_content: htmlContent
};
const response = await fetch(`${collectionsUrl}/${collectionId}/items`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getAuthToken()}`
},
body: JSON.stringify(payload)
});
if (response.ok) {
const result = await response.json();
console.log(`✅ Collection item created: ${result.item_id}`);
return result;
} else {
const errorText = await response.text();
console.error(`❌ Failed to create collection item (${response.status}): ${errorText}`);
throw new Error(`Failed to create collection item: ${response.status} ${errorText}`);
}
} catch (error) {
console.error('❌ Error creating collection item:', error);
throw error;
}
}
/**
* Delete a collection item
* @param {string} collectionId - Collection ID
* @param {string} itemId - Item ID to delete
* @returns {Promise<boolean>} Success status
*/
async deleteCollectionItem(collectionId, itemId) {
try {
const collectionsUrl = this.baseUrl.replace('/api/content', '/api/collections');
const response = await fetch(`${collectionsUrl}/${collectionId}/items/${itemId}?site_id=${this.siteId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.getAuthToken()}`
}
});
if (response.ok) {
console.log(`✅ Collection item deleted: ${itemId}`);
return true;
} else {
const errorText = await response.text();
console.error(`❌ Failed to delete collection item (${response.status}): ${errorText}`);
return false;
}
} catch (error) {
console.error('❌ Error deleting collection item:', error);
return false;
}
}
/**
* Get all collection items
* @param {string} collectionId - Collection ID
* @returns {Promise<Array>} Array of collection items
*/
async getCollectionItems(collectionId) {
try {
const collectionsUrl = this.baseUrl.replace('/api/content', '/api/collections');
const response = await fetch(`${collectionsUrl}/${collectionId}/items?site_id=${this.siteId}`);
if (response.ok) {
const result = await response.json();
return result.items || [];
} else {
console.warn(`⚠️ Failed to fetch collection items (${response.status}): ${collectionId}`);
return [];
}
} catch (error) {
console.error('Failed to fetch collection items:', collectionId, error);
return [];
}
}
/**
* Update collection item position (for reordering)
* @param {string} collectionId - Collection ID
* @param {string} itemId - Item ID to update
* @param {number} newPosition - New position index
* @returns {Promise<boolean>} Success status
*/
async updateCollectionItemPosition(collectionId, itemId, newPosition) {
try {
const collectionsUrl = this.baseUrl.replace('/api/content', '/api/collections');
const payload = {
site_id: this.siteId,
position: newPosition
};
const response = await fetch(`${collectionsUrl}/${collectionId}/items/${itemId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.getAuthToken()}`
},
body: JSON.stringify(payload)
});
if (response.ok) {
console.log(`✅ Collection item position updated: ${itemId} → position ${newPosition}`);
return true;
} else {
const errorText = await response.text();
console.error(`❌ Failed to update collection item position (${response.status}): ${errorText}`);
return false;
}
} catch (error) {
console.error('❌ Error updating collection item position:', error);
return false;
}
}
/**
* Trigger site enhancement after collection changes
* @returns {Promise<boolean>} Success status
*/
async enhanceSite() {
try {
const enhanceUrl = this.baseUrl.replace('/api/content', '/api/enhance');
const response = await fetch(`${enhanceUrl}?site_id=${this.siteId}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.getAuthToken()}`
}
});
if (response.ok) {
const result = await response.json();
console.log('✅ Files enhanced successfully:', result);
return true;
} else {
console.error(`❌ Failed to enhance files (${response.status})`);
return false;
}
} catch (error) {
console.error('❌ Error enhancing files:', error);
return false;
}
}
// =============================================================================
/**
* Get authentication token for API requests
* @returns {string} JWT token or mock token for development