đī¸ CreatorContent.net
U
User
!
-
!
-
Manage Subscription
Manage Tokens
Storage
Media Library â
Documentation
User Dashboard
Podcasts
Podcasts
Episodes
Transcriptions
Contributors
Studio
Public Profile
Public Profiles
Blog
Event Lists
Surveys
Contact Forms
Subscribers
Notifications & Shoutouts
Development
React Test
Media Library
Help Center
Admin Dashboard
Logout
Back to Documentation
Frontend Architecture
Frontend Architecture
File: 05-frontend-architecture.md
Documentation Index
Loading documentation...
# Frontend Architecture ## Overview CreatorContent.net uses a hybrid frontend approach: - **Server-side**: Blade templates for initial page rendering - **Client-side**: Vanilla JavaScript with API calls for dynamic interactions - **Styling**: Tailwind CSS utility-first framework - **Build Tool**: Vite for asset compilation ## Core Layout ### Main Layout File **File:** `resources/views/layouts/app.blade.php` This file provides: 1. Global navigation and user menu 2. API helper methods (available globally via `window`) 3. App cache system 4. UI helper functions (notifications, messages) 5. User authentication check and menu updates ## API Helper Methods All API methods are defined in `app.blade.php` and automatically include: - `credentials: 'include'` for cookie-based authentication - JSON headers for request/response - Error handling and logging - Automatic token management via cookies ### Base API Method #### `apiCall(url, options = {})` Core API method used by all other helpers. ```javascript async function apiCall(url, options = {}) { const response = await fetch(url, { headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', ...options.headers }, credentials: 'include', ...options }); return await response.json(); } ``` **Usage:** ```javascript const data = await apiCall('/api/endpoint', { method: 'POST', body: JSON.stringify({ key: 'value' }) }); ``` ### HTTP Method Helpers #### `apiGet(url, options = {})` GET request helper. ```javascript async function apiGet(url, options = {}) { return apiCall(url, { method: 'GET', ...options }); } ``` **Usage:** ```javascript const podcasts = await apiGet('/api/podcasts'); ``` #### `apiPost(url, data = {}, options = {})` POST request helper with automatic JSON stringification. ```javascript async function apiPost(url, data = {}, options = {}) { return apiCall(url, { method: 'POST', body: JSON.stringify(data), ...options }); } ``` **Usage:** ```javascript const response = await apiPost('/api/podcasts', { title: 'My Podcast', description: 'Podcast description' }); ``` #### `apiPut(url, data = {}, options = {})` PUT request helper (full resource update). ```javascript async function apiPut(url, data = {}, options = {}) { return apiCall(url, { method: 'PUT', body: JSON.stringify(data), ...options }); } ``` **Usage:** ```javascript const response = await apiPut('/api/podcasts/1', { title: 'Updated Podcast Title' }); ``` #### `apiPatch(url, data = {}, options = {})` PATCH request helper (partial resource update). ```javascript async function apiPatch(url, data = {}, options = {}) { return apiCall(url, { method: 'PATCH', body: JSON.stringify(data), ...options }); } ``` **Usage:** ```javascript const response = await apiPatch('/api/podcasts/1', { status: 'published' }); ``` #### `apiDelete(url, options = {})` DELETE request helper. ```javascript async function apiDelete(url, options = {}) { return apiCall(url, { method: 'DELETE', ...options }); } ``` **Usage:** ```javascript const response = await apiDelete('/api/podcasts/1'); ``` ### File Upload Methods #### `apiFileUpload(url, formData, options = {})` File upload with progress tracking support. **Features:** - Automatic progress tracking via `onProgress` callback - Uses XMLHttpRequest for progress when callback provided - Falls back to fetch for simple uploads - Proper FormData handling (no Content-Type header) **Usage with Progress:** ```javascript const formData = new FormData(); formData.append('file', file); const response = await apiFileUpload('/api/media', formData, { onProgress: (progress) => { console.log(`Upload: ${progress}%`); updateProgressBar(progress); } }); ``` **Usage without Progress:** ```javascript const formData = new FormData(); formData.append('file', file); const response = await apiFileUpload('/api/media', formData); ``` #### `uploadFileAndCreateMedia(file, options = {}, onProgress = null)` Convenience method for uploading files and creating media records. **Parameters:** - `file`: File object to upload - `options`: Object with `title`, `description`, `alt_text`, `is_active` - `onProgress`: Optional callback function **Usage:** ```javascript const media = await uploadFileAndCreateMedia(file, { title: 'My Image', description: 'Image description', alt_text: 'Alt text for accessibility', is_active: true }, (progress) => { console.log(`Upload: ${progress}%`); }); ``` ## App Cache System ### Cache Object **Variable:** `appCache` Simple in-memory cache for reducing redundant API calls. ### Methods #### `appCache.set(key, data)` Store data in cache. ```javascript appCache.set('podcasts', podcastsData); appCache.set('user', userData); ``` #### `appCache.get(key)` Retrieve cached data. ```javascript const podcasts = appCache.get('podcasts'); ``` #### `appCache.has(key)` Check if cache has data. ```javascript if (appCache.has('podcasts')) { return appCache.get('podcasts'); } ``` #### `appCache.clear()` Clear all cached data. ```javascript appCache.clear(); ``` ### Cache Keys **Standard keys:** - `user`: Current authenticated user - `publicProfile`: Current public profile data **Usage Pattern:** ```javascript async function loadPodcasts() { // Check cache first if (appCache.has('podcasts')) { return appCache.get('podcasts'); } // Fetch from API const response = await apiGet('/api/podcasts'); if (response.success) { appCache.set('podcasts', response.podcasts); return response.podcasts; } return []; } ``` ## UI Helper Functions ### Message Display Functions #### `showSuccess(message)` Display success notification. ```javascript showSuccess('Operation completed successfully'); ``` #### `showError(message)` Display error notification. ```javascript showError('An error occurred'); ``` #### `showMessage(message, type)` Display custom message with type. ```javascript showMessage('Custom message', 'info'); showMessage('Warning message', 'warning'); ``` **Types:** `success`, `error`, `info`, `warning` #### `hideMessage()` Hide currently displayed message. ```javascript hideMessage(); ``` ## User Management ### `loadUser()` Load current authenticated user. **Behavior:** - Checks cache first - Fetches from `/api/user` if not cached - Updates user menu automatically - Redirects to login if unauthenticated **Usage:** ```javascript const user = await loadUser(); console.log(user.name, user.email); ``` ### `updateUserMenu(user)` Update navigation user menu with user data. **Called automatically by `loadUser()`**, but can be called manually: ```javascript updateUserMenu({ name: 'John Doe', email: 'john@example.com', is_admin: false }); ``` ## Navigation & UI ### Dropdown Toggle #### `toggleDropdown()` Toggle user dropdown menu. Called automatically by dropdown button click handler. ### Window Click Handler Automatically closes dropdowns when clicking outside: ```javascript window.onclick = function(event) { // Closes dropdowns when clicking outside }; ``` ## Authentication Flow ### Initialization On every page load (`DOMContentLoaded`): 1. **Load User**: Calls `loadUser()` 2. **Store User**: Sets `window.currentUser` for global access 3. **Trigger Callbacks**: Calls `window.displayUserInfo()` if defined 4. **Handle Errors**: Redirects to login if authentication fails ### Redirect Logic If user cannot be loaded: - Redirects to `/login` - Logs error to console ### User Object Structure ```javascript { id: 1, username: 'testuser', name: 'Test User', email: 'test@example.com', phone: '+1234567890', role: 'creator', is_admin: false, email_verified_at: '2024-06-27T22:00:00.000000Z', phone_verified_at: '2024-06-27T22:00:00.000000Z' } ``` ## JavaScript Patterns ### Error Handling **Standard Pattern:** ```javascript try { const response = await apiPost('/api/endpoint', data); if (response.success) { showSuccess(response.message || 'Operation successful'); return response.data; } else { showError(response.message || 'Operation failed'); return null; } } catch (error) { console.error('Error:', error); showError('An unexpected error occurred'); return null; } ``` ### Loading Data Pattern **Cache-First Pattern:** ```javascript async function loadData() { // Check cache if (appCache.has('data')) { return appCache.get('data'); } try { // Fetch from API const response = await apiGet('/api/data'); if (response.success) { appCache.set('data', response.data); return response.data; } } catch (error) { console.error('Failed to load data:', error); showError('Failed to load data'); } return []; } ``` ### Form Submission Pattern ```javascript async function submitForm(formData) { try { const response = await apiPost('/api/submit', formData); if (response.success) { showSuccess('Saved successfully'); // Reload data or redirect window.location.reload(); } else { showError(response.message || 'Failed to save'); } } catch (error) { console.error('Error:', error); showError('An error occurred'); } } ``` ### Delete Confirmation Pattern ```javascript async function deleteItem(id) { if (!confirm('Are you sure you want to delete this item?')) { return; } try { const response = await apiDelete(`/api/items/${id}`); if (response.success) { showSuccess('Item deleted successfully'); // Reload list loadItems(); } else { showError(response.message || 'Failed to delete'); } } catch (error) { console.error('Error:', error); showError('An error occurred'); } } ``` ### File Upload Pattern ```javascript async function uploadFile(inputElement) { const file = inputElement.files[0]; if (!file) return; const formData = new FormData(); formData.append('file', file); try { const response = await apiFileUpload('/api/media', formData, { onProgress: (progress) => { console.log(`Upload: ${progress}%`); } }); if (response.success) { showSuccess('File uploaded successfully'); return response.media; } else { showError(response.message || 'Upload failed'); } } catch (error) { console.error('Upload error:', error); showError('Upload failed'); } } ``` ## Global Variables ### `window.currentUser` Stored on page load by `loadUser()`. Available globally: ```javascript if (window.currentUser) { console.log(window.currentUser.name); } ``` ### `window.displayUserInfo` Optional callback function called after user loads. Define in page-specific scripts: ```javascript window.displayUserInfo = function() { // Custom user info display logic console.log('User loaded:', window.currentUser); }; ``` ## Console Logging All API methods include console logging for debugging: - `đ API Call`: Request initiation - `đĻ API Response`: Response received - `đ File Upload`: File upload start - `â Success`: Operation success - `â Error`: Operation failure - `đĻ Cached`: Cache hit - `đī¸ Cache cleared`: Cache cleared - `đ¤ User`: User-related operations ## Response Format All API responses follow this structure: ```javascript { success: true|false, message: "Optional message", data: { /* Response data */ } // Or direct properties for convenience } ``` **Success Example:** ```javascript { success: true, message: "Podcast created successfully", podcast: { /* podcast data */ } } ``` **Error Example:** ```javascript { success: false, message: "Validation failed", errors: { title: ["The title field is required."] } } ``` ## Best Practices ### 1. Always Check `success` Property ```javascript const response = await apiPost('/api/endpoint', data); if (response.success) { // Handle success } else { // Handle error showError(response.message); } ``` ### 2. Use Cache for Read Operations ```javascript if (appCache.has('data')) { return appCache.get('data'); } ``` ### 3. Clear Cache on Updates ```javascript await apiPut('/api/data/1', updates); appCache.clear(); // Or clear specific keys loadData(); // Reload fresh data ``` ### 4. Handle Errors Gracefully ```javascript try { const response = await apiGet('/api/data'); // Process response } catch (error) { console.error('Error:', error); showError('Failed to load data'); // Provide fallback behavior } ``` ### 5. Use Progress Callbacks for Large Uploads ```javascript apiFileUpload('/api/media', formData, { onProgress: (progress) => { updateProgressBar(progress); } }); ``` ## Blade Integration ### Using API Methods in Blade Templates ```blade <script> async function loadPodcasts() { const response = await apiGet('/api/podcasts'); if (response.success) { console.log(response.podcasts); } } // Load on page ready document.addEventListener('DOMContentLoaded', loadPodcasts); </script> ``` ### Accessing Current User ```blade <script> // Wait for user to load document.addEventListener('DOMContentLoaded', async function() { const user = await loadUser(); if (user) { console.log('Current user:', user.name); } }); // Or use global variable (after load) if (window.currentUser) { console.log(window.currentUser); } </script> ``` ## Data Binding System See `docs/data-binding-system.md` for documentation on the reactive JavaScript framework used for automatic data-DOM synchronization. ## Related Documentation - **API Reference**: See `docs/03-api-reference.md` for all available endpoints - **Authentication**: See `docs/02-authentication.md` for authentication details - **Data Binding**: See `docs/data-binding-system.md` for reactive framework
0
đ Page Notes
+ Add New
Add New Note
Type
âšī¸ Info
đ Bug
⨠Feature Request
đĄ Improvement
â Missing Feature
đ¨ Design Changes
Title (optional)
Note Content
đ Add Note