refactor: split CSS and JS into modular files (SRP)
This commit is contained in:
@@ -101,6 +101,94 @@ src/server/
|
||||
|
||||
Source: [src/server/](../src/server/)
|
||||
|
||||
### 2.2.1 Frontend Architecture (`src/server/web/static/`)
|
||||
|
||||
The frontend uses a modular architecture with no build step required. CSS and JavaScript files are organized by responsibility.
|
||||
|
||||
#### CSS Structure
|
||||
|
||||
```
|
||||
src/server/web/static/css/
|
||||
+-- styles.css # Entry point with @import statements
|
||||
+-- base/
|
||||
| +-- variables.css # CSS custom properties (colors, fonts, spacing)
|
||||
| +-- reset.css # CSS reset and normalize styles
|
||||
| +-- typography.css # Font styles, headings, text utilities
|
||||
+-- components/
|
||||
| +-- buttons.css # All button styles
|
||||
| +-- cards.css # Card and panel components
|
||||
| +-- forms.css # Form inputs, labels, validation styles
|
||||
| +-- modals.css # Modal and overlay styles
|
||||
| +-- navigation.css # Header, nav, sidebar styles
|
||||
| +-- progress.css # Progress bars, loading indicators
|
||||
| +-- notifications.css # Toast, alerts, messages
|
||||
| +-- tables.css # Table and list styles
|
||||
| +-- status.css # Status badges and indicators
|
||||
+-- pages/
|
||||
| +-- login.css # Login page specific styles
|
||||
| +-- index.css # Index/library page specific styles
|
||||
| +-- queue.css # Queue page specific styles
|
||||
+-- utilities/
|
||||
+-- animations.css # Keyframes and animation classes
|
||||
+-- responsive.css # Media queries and breakpoints
|
||||
+-- helpers.css # Utility classes (hidden, flex, spacing)
|
||||
```
|
||||
|
||||
#### JavaScript Structure
|
||||
|
||||
JavaScript uses the IIFE pattern with a shared `AniWorld` namespace for browser compatibility without build tools.
|
||||
|
||||
```
|
||||
src/server/web/static/js/
|
||||
+-- shared/ # Shared utilities used by all pages
|
||||
| +-- constants.js # API endpoints, localStorage keys, defaults
|
||||
| +-- auth.js # Token management (getToken, setToken, checkAuth)
|
||||
| +-- api-client.js # Fetch wrapper with auto-auth headers
|
||||
| +-- theme.js # Dark/light theme toggle
|
||||
| +-- ui-utils.js # Toast notifications, format helpers
|
||||
| +-- websocket-client.js # Socket.IO wrapper
|
||||
+-- index/ # Index page modules
|
||||
| +-- series-manager.js # Series list rendering and filtering
|
||||
| +-- selection-manager.js# Multi-select and bulk download
|
||||
| +-- search.js # Series search functionality
|
||||
| +-- scan-manager.js # Library rescan operations
|
||||
| +-- scheduler-config.js # Scheduler configuration
|
||||
| +-- logging-config.js # Logging configuration
|
||||
| +-- advanced-config.js # Advanced settings
|
||||
| +-- main-config.js # Main configuration and backup
|
||||
| +-- config-manager.js # Config modal orchestrator
|
||||
| +-- socket-handler.js # WebSocket event handlers
|
||||
| +-- app-init.js # Application initialization
|
||||
+-- queue/ # Queue page modules
|
||||
+-- queue-api.js # Queue API interactions
|
||||
+-- queue-renderer.js # Queue list rendering
|
||||
+-- progress-handler.js # Download progress updates
|
||||
+-- queue-socket-handler.js # WebSocket events for queue
|
||||
+-- queue-init.js # Queue page initialization
|
||||
```
|
||||
|
||||
#### Module Pattern
|
||||
|
||||
All JavaScript modules follow the IIFE pattern with namespace:
|
||||
|
||||
```javascript
|
||||
var AniWorld = window.AniWorld || {};
|
||||
|
||||
AniWorld.ModuleName = (function () {
|
||||
"use strict";
|
||||
|
||||
// Private variables and functions
|
||||
|
||||
// Public API
|
||||
return {
|
||||
init: init,
|
||||
publicMethod: publicMethod,
|
||||
};
|
||||
})();
|
||||
```
|
||||
|
||||
Source: [src/server/web/static/](../src/server/web/static/)
|
||||
|
||||
### 2.3 Core Layer (`src/core/`)
|
||||
|
||||
Domain logic for anime series management.
|
||||
|
||||
@@ -106,149 +106,785 @@ For each task completed:
|
||||
|
||||
---
|
||||
|
||||
## Task: Refactor CSS & JavaScript Files (Single Responsibility Principle) ✅ COMPLETED
|
||||
|
||||
### Status: COMPLETED
|
||||
|
||||
The CSS and JavaScript files have been successfully refactored into modular structures.
|
||||
|
||||
### Summary of Changes
|
||||
|
||||
**CSS Refactoring:**
|
||||
|
||||
- Created 17 modular CSS files organized into `base/`, `components/`, `pages/`, and `utilities/` directories
|
||||
- `styles.css` now serves as an entry point with @import statements
|
||||
- All CSS files under 500 lines (largest: helpers.css at 368 lines)
|
||||
- Total: 3,146 lines across 17 files
|
||||
|
||||
**JavaScript Refactoring:**
|
||||
|
||||
- Created 6 shared utility modules in `js/shared/`
|
||||
- Created 11 index page modules in `js/index/`
|
||||
- Created 5 queue page modules in `js/queue/`
|
||||
- Uses IIFE pattern with `AniWorld` namespace for browser compatibility
|
||||
- All JS files under 500 lines (largest: scan-manager.js at 439 lines)
|
||||
- Total: 4,795 lines across 22 modules
|
||||
|
||||
**Updated Files:**
|
||||
|
||||
- `index.html` - Updated script tags for modular JS
|
||||
- `queue.html` - Updated script tags for modular JS
|
||||
- `test_static_files.py` - Updated tests for modular architecture
|
||||
- `test_template_integration.py` - Updated tests for new JS structure
|
||||
- `ARCHITECTURE.md` - Added frontend architecture documentation
|
||||
|
||||
**Old Files (kept for reference):**
|
||||
|
||||
- `app.js` - Original monolithic file (can be deleted)
|
||||
- `queue.js` - Original monolithic file (can be deleted)
|
||||
|
||||
### Original Overview
|
||||
|
||||
Split monolithic `styles.css` (~2,135 lines), `app.js` (~2,305 lines), and `queue.js` (~993 lines) into smaller, focused files following the Single Responsibility Principle. Maximum 500 lines per file. All changes must maintain full backward compatibility with existing templates.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. Server is running: `conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload`
|
||||
2. Password: `Hallo123!`
|
||||
3. Login via browser at `http://127.0.0.1:8000/login`
|
||||
|
||||
### Notes
|
||||
|
||||
- This is a simplification that removes complexity while maintaining core functionality
|
||||
- Improves user experience with explicit manual control
|
||||
- Easier to understand, test, and maintain
|
||||
- Good foundation for future enhancements if needed
|
||||
- Server is running and functional before starting
|
||||
- All existing functionality works (login, index, queue pages)
|
||||
- Backup current files before making changes
|
||||
|
||||
---
|
||||
|
||||
## Task: Enhanced Anime Add Flow ✅ COMPLETED
|
||||
### Task 1: Analyze Current File Structure
|
||||
|
||||
### Overview
|
||||
**Objective**: Understand the current codebase before making changes.
|
||||
|
||||
Enhance the anime addition workflow to automatically persist anime to the database, scan for missing episodes immediately, and create folders using the anime display name instead of the internal key.
|
||||
**Steps**:
|
||||
|
||||
### Requirements
|
||||
1. Open and read `src/server/web/static/css/styles.css`
|
||||
2. Open and read `src/server/web/static/js/app.js`
|
||||
3. Open and read `src/server/web/static/js/queue.js`
|
||||
4. Open and read `src/server/web/templates/index.html`
|
||||
5. Open and read `src/server/web/templates/queue.html`
|
||||
6. Open and read `src/server/web/templates/login.html`
|
||||
7. Document all CSS sections (look for comment headers)
|
||||
8. Document all JavaScript functions and their dependencies
|
||||
9. Identify shared utilities vs page-specific code
|
||||
|
||||
1. **After anime add → Save to database**: Ensure the anime is persisted to the database via `AnimeDBService.create_series()` immediately after validation
|
||||
2. **After anime add → Scan for missing episodes**: Trigger a targeted episode scan for only the newly added anime (not the entire library)
|
||||
3. **After anime add → Create folder with anime name**: Use the anime display name (sanitized) for the folder, not the anime key
|
||||
|
||||
### Implementation Steps
|
||||
|
||||
#### Step 1: Examine Current Implementation
|
||||
|
||||
1. Open and read `src/server/routes/anime_routes.py` - find the `add_series` endpoint
|
||||
2. Open and read `src/core/SerieScanner.py` - understand how scanning works
|
||||
3. Open and read `src/core/entities/Serie.py` and `src/core/entities/SerieList.py` - understand folder handling
|
||||
4. Open and read `src/database/services/anime_db_service.py` - understand database operations
|
||||
5. Open and read `src/core/providers/AniWorldProvider.py` - understand how folders are created
|
||||
|
||||
#### Step 2: Create Utility Function for Folder Name Sanitization
|
||||
|
||||
1. Create or update utility module at `src/utils/filesystem.py`
|
||||
2. Implement `sanitize_folder_name(name: str) -> str` function that:
|
||||
- Removes/replaces characters invalid for filesystems: `< > : " / \ | ? *`
|
||||
- Trims leading/trailing whitespace and dots
|
||||
- Handles edge cases (empty string, only invalid chars)
|
||||
- Preserves Unicode characters (for Japanese titles, etc.)
|
||||
|
||||
#### Step 3: Update Serie Entity
|
||||
|
||||
1. Open `src/core/entities/Serie.py`
|
||||
2. Add a `folder` property that returns sanitized display name instead of key
|
||||
3. Ensure backward compatibility with existing series
|
||||
|
||||
#### Step 4: Update SerieList to Use Display Name for Folders
|
||||
|
||||
1. Open `src/core/entities/SerieList.py`
|
||||
2. In the `add()` method, use `serie.folder` (display name) instead of `serie.key` when creating directories
|
||||
3. Ensure the folder path is correctly stored in the Serie object
|
||||
|
||||
#### Step 5: Add Targeted Episode Scan Method to SerieScanner
|
||||
|
||||
1. Open `src/core/SerieScanner.py`
|
||||
2. Add new method `scan_single_series(self, key: str) -> List[Episode]`:
|
||||
- Fetches the specific anime from database/SerieList by key
|
||||
- Calls the provider to get available episodes
|
||||
- Compares with local files to find missing episodes
|
||||
- Returns list of missing episodes
|
||||
- Does NOT trigger a full library rescan
|
||||
|
||||
#### Step 6: Update add_series Endpoint
|
||||
|
||||
1. Open `src/server/routes/anime_routes.py`
|
||||
2. Modify the `add_series` endpoint to:
|
||||
- **Step A**: Validate the request (existing)
|
||||
- **Step B**: Create Serie object with sanitized folder name
|
||||
- **Step C**: Save to database via `AnimeDBService.create_series()`
|
||||
- **Step D**: Add to SerieList (which creates the folder)
|
||||
- **Step E**: Call `SerieScanner.scan_single_series(key)` for targeted scan
|
||||
- **Step F**: Return response including:
|
||||
- Success status
|
||||
- Created folder path
|
||||
- List of missing episodes found (if any)
|
||||
|
||||
#### Step 7: Update Provider Folder Handling
|
||||
|
||||
1. Open `src/core/providers/AniWorldProvider.py`
|
||||
2. Ensure download operations use `serie.folder` for filesystem paths
|
||||
3. If `EnhancedProvider.py` exists, update it similarly
|
||||
|
||||
### Acceptance Criteria
|
||||
|
||||
- [x] When adding a new anime, it is immediately saved to the database
|
||||
- [x] When adding a new anime, only that anime is scanned for missing episodes (not full library)
|
||||
- [x] Folder is created using the sanitized display name (e.g., "Attack on Titan" not "attack-on-titan")
|
||||
- [x] Special characters in anime names are properly handled (`:`, `?`, etc.)
|
||||
- [x] Existing anime entries continue to work (backward compatibility)
|
||||
- [x] API response includes the created folder path and missing episodes count
|
||||
- [x] Unit tests cover the new functionality
|
||||
- [x] No regressions in existing tests
|
||||
|
||||
### Implementation Summary (Completed)
|
||||
|
||||
**Files Created/Modified:**
|
||||
|
||||
- `src/server/utils/filesystem.py` - New file with `sanitize_folder_name()`, `is_safe_path()`, `create_safe_folder()`
|
||||
- `src/core/entities/series.py` - Added `sanitized_folder` property
|
||||
- `src/core/entities/SerieList.py` - Updated `add()` to use sanitized folder names
|
||||
- `src/core/SerieScanner.py` - Added `scan_single_series()` method
|
||||
- `src/server/api/anime.py` - Enhanced `add_series` endpoint with full flow
|
||||
|
||||
**Tests Added:**
|
||||
|
||||
- `tests/unit/test_filesystem_utils.py` - 43 tests for filesystem utilities
|
||||
- `tests/unit/test_serie_class.py` - 6 tests for `sanitized_folder` property
|
||||
- `tests/unit/test_serie_scanner.py` - 9 tests for `scan_single_series()`
|
||||
- `tests/api/test_anime_endpoints.py` - 6 integration tests for enhanced add flow
|
||||
|
||||
**All 97 related tests passing. No regressions in existing 848 unit tests and 60 API tests.**
|
||||
|
||||
### Testing Requirements
|
||||
|
||||
1. **Unit Tests**:
|
||||
|
||||
- Test `sanitize_folder_name()` with various inputs (special chars, Unicode, edge cases)
|
||||
- Test `Serie.folder` property returns sanitized name
|
||||
- Test `SerieScanner.scan_single_series()` only scans the specified anime
|
||||
- Test database persistence on anime add
|
||||
|
||||
2. **Integration Tests**:
|
||||
- Test full add flow: request → database → folder creation → scan
|
||||
- Test that folder is created with correct name
|
||||
- Test API response contains expected fields
|
||||
|
||||
### Error Handling
|
||||
|
||||
- If database save fails, return appropriate error and don't create folder
|
||||
- If folder creation fails (permissions, disk full), return error and rollback database entry
|
||||
- If scan fails, still return success for add but indicate scan failure in response
|
||||
- Log all operations with appropriate log levels
|
||||
|
||||
### Security Considerations
|
||||
|
||||
- Sanitize folder names to prevent path traversal attacks
|
||||
- Validate anime name length to prevent filesystem issues
|
||||
- Ensure folder is created within the configured library path only
|
||||
**Deliverable**: A mental map of all functions, styles, and their relationships.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Create CSS Directory Structure
|
||||
|
||||
**Objective**: Set up the new CSS file organization.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. Create directory: `src/server/web/static/css/base/`
|
||||
2. Create directory: `src/server/web/static/css/components/`
|
||||
3. Create directory: `src/server/web/static/css/pages/`
|
||||
4. Create directory: `src/server/web/static/css/utilities/`
|
||||
|
||||
**File Structure to Create**:
|
||||
|
||||
```
|
||||
src/server/web/static/css/
|
||||
├── styles.css # Main entry point with @import statements
|
||||
├── base/
|
||||
│ ├── variables.css # CSS custom properties (colors, fonts, spacing)
|
||||
│ ├── reset.css # CSS reset and normalize styles
|
||||
│ └── typography.css # Font styles, headings, text utilities
|
||||
├── components/
|
||||
│ ├── buttons.css # All button styles
|
||||
│ ├── cards.css # Card and panel components
|
||||
│ ├── forms.css # Form inputs, labels, validation styles
|
||||
│ ├── modals.css # Modal and overlay styles
|
||||
│ ├── navigation.css # Header, nav, sidebar styles
|
||||
│ ├── progress.css # Progress bars, loading indicators
|
||||
│ ├── notifications.css # Toast, alerts, messages
|
||||
│ └── tables.css # Table and list styles
|
||||
├── pages/
|
||||
│ ├── login.css # Login page specific styles
|
||||
│ ├── index.css # Index/library page specific styles
|
||||
│ └── queue.css # Queue page specific styles
|
||||
└── utilities/
|
||||
├── animations.css # Keyframes and animation classes
|
||||
├── responsive.css # Media queries and breakpoints
|
||||
└── helpers.css # Utility classes (hidden, flex, spacing)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Split styles.css into Modular Files
|
||||
|
||||
**Objective**: Extract styles from `styles.css` into appropriate module files.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Extract variables.css**:
|
||||
|
||||
- Find all `:root` CSS custom properties
|
||||
- Extract color variables, font variables, spacing variables
|
||||
- Include dark mode variables (`.dark-mode` or `[data-theme="dark"]`)
|
||||
|
||||
2. **Extract reset.css**:
|
||||
|
||||
- Extract `*`, `body`, `html` base resets
|
||||
- Extract box-sizing rules
|
||||
- Extract default margin/padding resets
|
||||
|
||||
3. **Extract typography.css**:
|
||||
|
||||
- Extract `h1-h6` styles
|
||||
- Extract paragraph, link, text styles
|
||||
- Extract font-related utility classes
|
||||
|
||||
4. **Extract buttons.css**:
|
||||
|
||||
- Find all `.btn`, `button`, `.button` related styles
|
||||
- Include hover, active, disabled states
|
||||
- Include button variants (primary, secondary, danger, etc.)
|
||||
|
||||
5. **Extract cards.css**:
|
||||
|
||||
- Extract `.card`, `.panel`, `.box` related styles
|
||||
- Include card headers, bodies, footers
|
||||
|
||||
6. **Extract forms.css**:
|
||||
|
||||
- Extract `input`, `select`, `textarea` styles
|
||||
- Extract `.form-group`, `.form-control` styles
|
||||
- Extract validation states (error, success)
|
||||
|
||||
7. **Extract modals.css**:
|
||||
|
||||
- Extract `.modal`, `.overlay`, `.dialog` styles
|
||||
- Include backdrop styles
|
||||
- Include modal animations
|
||||
|
||||
8. **Extract navigation.css**:
|
||||
|
||||
- Extract `header`, `nav`, `.navbar` styles
|
||||
- Extract menu and navigation link styles
|
||||
|
||||
9. **Extract progress.css**:
|
||||
|
||||
- Extract `.progress`, `.progress-bar` styles
|
||||
- Extract loading spinners and indicators
|
||||
|
||||
10. **Extract notifications.css**:
|
||||
|
||||
- Extract `.toast`, `.alert`, `.notification` styles
|
||||
- Include success, error, warning, info variants
|
||||
|
||||
11. **Extract tables.css**:
|
||||
|
||||
- Extract `table`, `.table` styles
|
||||
- Extract list styles if table-like
|
||||
|
||||
12. **Extract page-specific styles**:
|
||||
|
||||
- `login.css`: Styles only used on login page
|
||||
- `index.css`: Styles only used on index/library page (series cards, search)
|
||||
- `queue.css`: Styles only used on queue page (queue items, download status)
|
||||
|
||||
13. **Extract animations.css**:
|
||||
|
||||
- Extract all `@keyframes` rules
|
||||
- Extract animation utility classes
|
||||
|
||||
14. **Extract responsive.css**:
|
||||
|
||||
- Extract all `@media` queries
|
||||
- Organize by breakpoint
|
||||
|
||||
15. **Extract helpers.css**:
|
||||
|
||||
- Extract utility classes (.hidden, .flex, .text-center, etc.)
|
||||
- Extract spacing utilities
|
||||
|
||||
16. **Update main styles.css**:
|
||||
- Replace all content with `@import` statements
|
||||
- Order imports correctly (variables first, then reset, then components)
|
||||
|
||||
**Import Order in styles.css**:
|
||||
|
||||
```css
|
||||
/* Base */
|
||||
@import "base/variables.css";
|
||||
@import "base/reset.css";
|
||||
@import "base/typography.css";
|
||||
|
||||
/* Components */
|
||||
@import "components/buttons.css";
|
||||
@import "components/cards.css";
|
||||
@import "components/forms.css";
|
||||
@import "components/modals.css";
|
||||
@import "components/navigation.css";
|
||||
@import "components/progress.css";
|
||||
@import "components/notifications.css";
|
||||
@import "components/tables.css";
|
||||
|
||||
/* Pages */
|
||||
@import "pages/login.css";
|
||||
@import "pages/index.css";
|
||||
@import "pages/queue.css";
|
||||
|
||||
/* Utilities (load last to allow overrides) */
|
||||
@import "utilities/animations.css";
|
||||
@import "utilities/responsive.css";
|
||||
@import "utilities/helpers.css";
|
||||
```
|
||||
|
||||
**Verification**:
|
||||
|
||||
- Start the server
|
||||
- Check login page styling
|
||||
- Check index page styling
|
||||
- Check queue page styling
|
||||
- Verify dark mode toggle works
|
||||
- Verify responsive design works
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Create JavaScript Directory Structure
|
||||
|
||||
**Objective**: Set up the new JavaScript file organization.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. Create directory: `src/server/web/static/js/shared/`
|
||||
2. Create directory: `src/server/web/static/js/index/`
|
||||
3. Create directory: `src/server/web/static/js/queue/`
|
||||
|
||||
**File Structure to Create**:
|
||||
|
||||
```
|
||||
src/server/web/static/js/
|
||||
├── app.js # Main entry point for index page
|
||||
├── queue.js # Main entry point for queue page
|
||||
├── shared/
|
||||
│ ├── auth.js # Authentication utilities
|
||||
│ ├── api-client.js # HTTP request wrapper with auth
|
||||
│ ├── websocket-client.js # WebSocket connection management
|
||||
│ ├── theme.js # Dark/light mode management
|
||||
│ ├── ui-utils.js # Toast, loading overlay, formatters
|
||||
│ └── constants.js # Shared constants and config
|
||||
├── index/
|
||||
│ ├── series-manager.js # Series loading, filtering, rendering
|
||||
│ ├── search.js # Search functionality
|
||||
│ ├── scan-manager.js # Library scan operations
|
||||
│ ├── config-manager.js # Configuration modal handling
|
||||
│ └── selection.js # Series/episode selection logic
|
||||
└── queue/
|
||||
├── queue-api.js # Queue API operations
|
||||
├── queue-renderer.js # Render queue items (pending, active, etc.)
|
||||
└── progress-handler.js # Real-time progress updates
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Extract Shared JavaScript Utilities
|
||||
|
||||
**Objective**: Create reusable utility modules used by both index and queue pages.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Create constants.js**:
|
||||
|
||||
- Extract API endpoint URLs
|
||||
- Extract localStorage keys
|
||||
- Extract any magic strings or numbers
|
||||
|
||||
2. **Create auth.js**:
|
||||
|
||||
- Extract `checkAuth()` function
|
||||
- Extract `logout()` function
|
||||
- Extract `getAuthHeaders()` or token retrieval logic
|
||||
- Extract token storage/retrieval from localStorage
|
||||
|
||||
3. **Create api-client.js**:
|
||||
|
||||
- Extract `fetchWithAuth()` wrapper function
|
||||
- Handle automatic token injection
|
||||
- Handle 401 redirect to login
|
||||
- Handle common error responses
|
||||
|
||||
4. **Create websocket-client.js**:
|
||||
|
||||
- Extract WebSocket connection setup
|
||||
- Extract message handling dispatcher
|
||||
- Extract reconnection logic
|
||||
- Extract connection state management
|
||||
|
||||
5. **Create theme.js**:
|
||||
|
||||
- Extract `initTheme()` function
|
||||
- Extract `toggleTheme()` function
|
||||
- Extract `setTheme()` function
|
||||
- Extract theme persistence to localStorage
|
||||
|
||||
6. **Create ui-utils.js**:
|
||||
- Extract `showToast()` function
|
||||
- Extract `showLoadingOverlay()` / `hideLoadingOverlay()`
|
||||
- Extract `formatBytes()` function
|
||||
- Extract `formatDuration()` function
|
||||
- Extract `formatDate()` function
|
||||
- Extract any other shared UI helpers
|
||||
|
||||
**Pattern to Use (IIFE with Global Namespace)**:
|
||||
|
||||
```javascript
|
||||
// Example: shared/auth.js
|
||||
var AniWorld = window.AniWorld || {};
|
||||
|
||||
AniWorld.Auth = (function () {
|
||||
"use strict";
|
||||
|
||||
const TOKEN_KEY = "auth_token";
|
||||
|
||||
function getToken() {
|
||||
return localStorage.getItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
function setToken(token) {
|
||||
localStorage.setItem(TOKEN_KEY, token);
|
||||
}
|
||||
|
||||
function removeToken() {
|
||||
localStorage.removeItem(TOKEN_KEY);
|
||||
}
|
||||
|
||||
function getAuthHeaders() {
|
||||
const token = getToken();
|
||||
return token ? { Authorization: "Bearer " + token } : {};
|
||||
}
|
||||
|
||||
async function checkAuth() {
|
||||
// Implementation
|
||||
}
|
||||
|
||||
function logout() {
|
||||
removeToken();
|
||||
window.location.href = "/login";
|
||||
}
|
||||
|
||||
// Public API
|
||||
return {
|
||||
getToken: getToken,
|
||||
setToken: setToken,
|
||||
getAuthHeaders: getAuthHeaders,
|
||||
checkAuth: checkAuth,
|
||||
logout: logout,
|
||||
};
|
||||
})();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Split app.js into Index Page Modules
|
||||
|
||||
**Objective**: Break down `app.js` into focused modules for the index/library page.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Create series-manager.js**:
|
||||
|
||||
- Extract series loading from API
|
||||
- Extract series filtering logic
|
||||
- Extract series rendering/DOM updates
|
||||
- Extract series card click handlers
|
||||
|
||||
2. **Create search.js**:
|
||||
|
||||
- Extract search input handling
|
||||
- Extract search API calls
|
||||
- Extract search results rendering
|
||||
- Extract search result selection
|
||||
|
||||
3. **Create scan-manager.js**:
|
||||
|
||||
- Extract scan initiation logic
|
||||
- Extract scan progress overlay
|
||||
- Extract scan progress updates (WebSocket)
|
||||
- Extract scan completion handling
|
||||
|
||||
4. **Create config-manager.js**:
|
||||
|
||||
- Extract config modal open/close
|
||||
- Extract config loading from API
|
||||
- Extract config form handling
|
||||
- Extract config save logic
|
||||
- Extract scheduler configuration
|
||||
- Extract backup management
|
||||
|
||||
5. **Create selection.js**:
|
||||
|
||||
- Extract episode selection logic
|
||||
- Extract "select all" functionality
|
||||
- Extract selection state management
|
||||
- Extract "add to queue" from selection
|
||||
|
||||
6. **Update main app.js**:
|
||||
- Import all modules via script tags
|
||||
- Initialize all modules on DOMContentLoaded
|
||||
- Wire up event listeners to module functions
|
||||
- Keep this file as thin as possible (orchestration only)
|
||||
|
||||
**Example main app.js structure**:
|
||||
|
||||
```javascript
|
||||
// filepath: src/server/web/static/js/app.js
|
||||
document.addEventListener("DOMContentLoaded", async function () {
|
||||
"use strict";
|
||||
|
||||
// Initialize shared modules
|
||||
AniWorld.Theme.init();
|
||||
|
||||
// Check authentication
|
||||
const isAuth = await AniWorld.Auth.checkAuth();
|
||||
if (!isAuth) return;
|
||||
|
||||
// Initialize page-specific modules
|
||||
AniWorld.SeriesManager.init();
|
||||
AniWorld.Search.init();
|
||||
AniWorld.ScanManager.init();
|
||||
AniWorld.ConfigManager.init();
|
||||
AniWorld.Selection.init();
|
||||
|
||||
// Initialize WebSocket for real-time updates
|
||||
AniWorld.WebSocketClient.init();
|
||||
|
||||
// Load initial data
|
||||
AniWorld.SeriesManager.loadSeries();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Split queue.js into Queue Page Modules
|
||||
|
||||
**Objective**: Break down `queue.js` into focused modules for the queue page.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Create queue-api.js**:
|
||||
|
||||
- Extract `loadQueueStatus()` API call
|
||||
- Extract `startDownload()` API call
|
||||
- Extract `stopDownload()` API call
|
||||
- Extract `removeFromQueue()` API call
|
||||
- Extract `clearCompleted()` API call
|
||||
- Extract `clearFailed()` API call
|
||||
- Extract `retryFailed()` API call
|
||||
|
||||
2. **Create queue-renderer.js**:
|
||||
|
||||
- Extract `renderActiveDownload()` function
|
||||
- Extract `renderPendingQueue()` function
|
||||
- Extract `renderCompletedList()` function
|
||||
- Extract `renderFailedList()` function
|
||||
- Extract `updateQueueCounts()` function
|
||||
- Extract queue item template generation
|
||||
|
||||
3. **Create progress-handler.js**:
|
||||
|
||||
- Extract WebSocket message handling for queue
|
||||
- Extract progress bar updates
|
||||
- Extract status text updates
|
||||
- Extract ETA calculations
|
||||
- Extract speed display formatting
|
||||
|
||||
4. **Update main queue.js**:
|
||||
- Import all modules via script tags
|
||||
- Initialize all modules on DOMContentLoaded
|
||||
- Wire up button click handlers to API functions
|
||||
- Set up WebSocket handlers for progress
|
||||
- Keep this file as thin as possible
|
||||
|
||||
**Example main queue.js structure**:
|
||||
|
||||
```javascript
|
||||
// filepath: src/server/web/static/js/queue.js
|
||||
document.addEventListener("DOMContentLoaded", async function () {
|
||||
"use strict";
|
||||
|
||||
// Initialize shared modules
|
||||
AniWorld.Theme.init();
|
||||
|
||||
// Check authentication
|
||||
const isAuth = await AniWorld.Auth.checkAuth();
|
||||
if (!isAuth) return;
|
||||
|
||||
// Initialize queue modules
|
||||
AniWorld.QueueApi.init();
|
||||
AniWorld.QueueRenderer.init();
|
||||
AniWorld.ProgressHandler.init();
|
||||
|
||||
// Initialize WebSocket with queue-specific handlers
|
||||
AniWorld.WebSocketClient.init({
|
||||
onProgress: AniWorld.ProgressHandler.handleProgress,
|
||||
onQueueUpdate: AniWorld.QueueRenderer.refresh,
|
||||
});
|
||||
|
||||
// Load initial queue status
|
||||
await AniWorld.QueueApi.loadStatus();
|
||||
AniWorld.QueueRenderer.refresh();
|
||||
|
||||
// Wire up UI buttons
|
||||
document
|
||||
.getElementById("start-btn")
|
||||
?.addEventListener("click", AniWorld.QueueApi.startDownload);
|
||||
document
|
||||
.getElementById("stop-btn")
|
||||
?.addEventListener("click", AniWorld.QueueApi.stopDownload);
|
||||
document
|
||||
.getElementById("clear-completed-btn")
|
||||
?.addEventListener("click", AniWorld.QueueApi.clearCompleted);
|
||||
document
|
||||
.getElementById("clear-failed-btn")
|
||||
?.addEventListener("click", AniWorld.QueueApi.clearFailed);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Update HTML Templates
|
||||
|
||||
**Objective**: Update templates to load the new modular JavaScript files.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Update index.html**:
|
||||
|
||||
- Add script tags for shared modules (in order)
|
||||
- Add script tags for index-specific modules (in order)
|
||||
- Keep main app.js as the last script
|
||||
- Ensure correct load order (dependencies first)
|
||||
|
||||
```html
|
||||
<!-- Shared Modules -->
|
||||
<script src="/static/js/shared/constants.js"></script>
|
||||
<script src="/static/js/shared/auth.js"></script>
|
||||
<script src="/static/js/shared/api-client.js"></script>
|
||||
<script src="/static/js/shared/websocket-client.js"></script>
|
||||
<script src="/static/js/shared/theme.js"></script>
|
||||
<script src="/static/js/shared/ui-utils.js"></script>
|
||||
|
||||
<!-- Index Page Modules -->
|
||||
<script src="/static/js/index/series-manager.js"></script>
|
||||
<script src="/static/js/index/search.js"></script>
|
||||
<script src="/static/js/index/scan-manager.js"></script>
|
||||
<script src="/static/js/index/config-manager.js"></script>
|
||||
<script src="/static/js/index/selection.js"></script>
|
||||
|
||||
<!-- Main Entry Point -->
|
||||
<script src="/static/js/app.js"></script>
|
||||
```
|
||||
|
||||
2. **Update queue.html**:
|
||||
|
||||
- Add script tags for shared modules (in order)
|
||||
- Add script tags for queue-specific modules (in order)
|
||||
- Keep main queue.js as the last script
|
||||
|
||||
```html
|
||||
<!-- Shared Modules -->
|
||||
<script src="/static/js/shared/constants.js"></script>
|
||||
<script src="/static/js/shared/auth.js"></script>
|
||||
<script src="/static/js/shared/api-client.js"></script>
|
||||
<script src="/static/js/shared/websocket-client.js"></script>
|
||||
<script src="/static/js/shared/theme.js"></script>
|
||||
<script src="/static/js/shared/ui-utils.js"></script>
|
||||
|
||||
<!-- Queue Page Modules -->
|
||||
<script src="/static/js/queue/queue-api.js"></script>
|
||||
<script src="/static/js/queue/queue-renderer.js"></script>
|
||||
<script src="/static/js/queue/progress-handler.js"></script>
|
||||
|
||||
<!-- Main Entry Point -->
|
||||
<script src="/static/js/queue.js"></script>
|
||||
```
|
||||
|
||||
3. **Update login.html** (if applicable):
|
||||
- Only include shared modules needed for login
|
||||
- Likely just theme.js and minimal utilities
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Verification and Testing
|
||||
|
||||
**Objective**: Ensure all functionality works after refactoring.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Start the server**:
|
||||
|
||||
```bash
|
||||
conda run -n AniWorld python -m uvicorn src.server.fastapi_app:app --host 127.0.0.1 --port 8000 --reload
|
||||
```
|
||||
|
||||
2. **Test Login Page**:
|
||||
|
||||
- [ ] Page loads with correct styling
|
||||
- [ ] Dark/light mode toggle works
|
||||
- [ ] Login form submits correctly
|
||||
- [ ] Error messages display correctly
|
||||
- [ ] Successful login redirects to index
|
||||
|
||||
3. **Test Index Page**:
|
||||
|
||||
- [ ] Page loads with correct styling
|
||||
- [ ] Series list loads and displays
|
||||
- [ ] Series filtering works
|
||||
- [ ] Search functionality works
|
||||
- [ ] Series selection works
|
||||
- [ ] Episode selection works
|
||||
- [ ] Add to queue works
|
||||
- [ ] Scan library works
|
||||
- [ ] Scan progress displays
|
||||
- [ ] Config modal opens/closes
|
||||
- [ ] Config saves correctly
|
||||
- [ ] Dark/light mode toggle works
|
||||
- [ ] Logout works
|
||||
- [ ] WebSocket connection established
|
||||
|
||||
4. **Test Queue Page**:
|
||||
|
||||
- [ ] Page loads with correct styling
|
||||
- [ ] Queue status loads
|
||||
- [ ] Pending items display
|
||||
- [ ] Active download displays
|
||||
- [ ] Completed items display
|
||||
- [ ] Failed items display
|
||||
- [ ] Start download works
|
||||
- [ ] Stop download works
|
||||
- [ ] Remove from queue works
|
||||
- [ ] Clear completed works
|
||||
- [ ] Clear failed works
|
||||
- [ ] Retry failed works
|
||||
- [ ] Progress updates in real-time
|
||||
- [ ] Dark/light mode toggle works
|
||||
- [ ] WebSocket connection established
|
||||
|
||||
5. **Test Responsive Design**:
|
||||
|
||||
- [ ] All pages work on mobile viewport
|
||||
- [ ] All pages work on tablet viewport
|
||||
- [ ] All pages work on desktop viewport
|
||||
|
||||
6. **Browser Console Check**:
|
||||
- [ ] No JavaScript errors in console
|
||||
- [ ] No 404 errors for static files
|
||||
- [ ] No CSS loading errors
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Cleanup and Documentation
|
||||
|
||||
**Objective**: Finalize the refactoring with cleanup and documentation.
|
||||
|
||||
**Steps**:
|
||||
|
||||
1. **Remove backup files** (if any were created)
|
||||
|
||||
2. **Verify file sizes**:
|
||||
|
||||
- No file should exceed 500 lines
|
||||
- If any file exceeds, split further
|
||||
|
||||
3. **Add file headers**:
|
||||
|
||||
- Add comment header to each new file explaining its purpose
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* AniWorld - Series Manager Module
|
||||
*
|
||||
* Handles loading, filtering, and rendering of anime series
|
||||
* on the index/library page.
|
||||
*
|
||||
* Dependencies: auth.js, api-client.js, ui-utils.js
|
||||
*/
|
||||
```
|
||||
|
||||
```css
|
||||
/**
|
||||
* AniWorld - Button Styles
|
||||
*
|
||||
* All button-related styles including variants,
|
||||
* states, and sizes.
|
||||
*/
|
||||
```
|
||||
|
||||
4. **Update infrastructure.md** (if exists):
|
||||
|
||||
- Document new file structure
|
||||
- Document module dependencies
|
||||
|
||||
5. **Commit changes**:
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "refactor: split CSS and JS into modular files (SRP)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Summary of New Files
|
||||
|
||||
**CSS Files (14 files)**:
|
||||
|
||||
- `css/styles.css` (entry point with imports)
|
||||
- `css/base/variables.css`
|
||||
- `css/base/reset.css`
|
||||
- `css/base/typography.css`
|
||||
- `css/components/buttons.css`
|
||||
- `css/components/cards.css`
|
||||
- `css/components/forms.css`
|
||||
- `css/components/modals.css`
|
||||
- `css/components/navigation.css`
|
||||
- `css/components/progress.css`
|
||||
- `css/components/notifications.css`
|
||||
- `css/components/tables.css`
|
||||
- `css/pages/login.css`
|
||||
- `css/pages/index.css`
|
||||
- `css/pages/queue.css`
|
||||
- `css/utilities/animations.css`
|
||||
- `css/utilities/responsive.css`
|
||||
- `css/utilities/helpers.css`
|
||||
|
||||
**JavaScript Files (15 files)**:
|
||||
|
||||
- `js/app.js` (entry point for index)
|
||||
- `js/queue.js` (entry point for queue)
|
||||
- `js/shared/constants.js`
|
||||
- `js/shared/auth.js`
|
||||
- `js/shared/api-client.js`
|
||||
- `js/shared/websocket-client.js`
|
||||
- `js/shared/theme.js`
|
||||
- `js/shared/ui-utils.js`
|
||||
- `js/index/series-manager.js`
|
||||
- `js/index/search.js`
|
||||
- `js/index/scan-manager.js`
|
||||
- `js/index/config-manager.js`
|
||||
- `js/index/selection.js`
|
||||
- `js/queue/queue-api.js`
|
||||
- `js/queue/queue-renderer.js`
|
||||
- `js/queue/progress-handler.js`
|
||||
|
||||
---
|
||||
|
||||
### Important Notes
|
||||
|
||||
1. **IIFE Pattern**: Use the IIFE (Immediately Invoked Function Expression) pattern with a global namespace (`AniWorld`) for browser compatibility without requiring a build step.
|
||||
|
||||
2. **No Build Tools Required**: This approach uses native CSS `@import` and multiple `<script>` tags, avoiding the need for bundlers like Webpack or Vite.
|
||||
|
||||
3. **Load Order Matters**: Scripts must be loaded in dependency order. Shared modules first, then page-specific modules, then the main entry point.
|
||||
|
||||
4. **Backward Compatibility**: All existing HTML element IDs and class names must be preserved. Only the JavaScript and CSS organization changes, not the API.
|
||||
|
||||
5. **Incremental Approach**: Complete one task fully before moving to the next. Verify functionality after each major step.
|
||||
|
||||
6. **Rollback Plan**: Keep the original files until all verification is complete. Only delete originals after confirming everything works.
|
||||
|
||||
Reference in New Issue
Block a user