Files
energymedia_content_manager/.github/copilot-instructions.md
2026-01-10 21:12:17 -08:00

258 lines
9.2 KiB
Markdown

# EnergyMedia Content Manager - AI Coding Agent Instructions
## Project Overview
**EnergyMedia Content Manager** is a Flutter web application for managing multimedia content (videos, posters, categories) for the EnergyMedia platform. Single-organization focused system with video upload, categorization, playback, and analytics dashboard.
**Tech Stack:** Flutter 3.1.4+, Supabase (backend/auth/storage), Provider (state), GoRouter (navigation), PlutoGrid (tables), Video Players (appinio_video_player/video_player)
## Architecture & Key Patterns
### Dual Supabase Clients
- `supabase` (default): Standard auth schema (`public.users`) for authentication ONLY
- `supabaseML`: Custom `media_library` schema for all media content management
- **Critical:** Always use `supabaseML` for media data, `supabase` for auth only
- **Organization Filter:** ALL queries to `media_files` MUST filter by `organization_fk = 17`
- See [lib/main.dart](lib/main.dart#L35) and [lib/helpers/globals.dart](lib/helpers/globals.dart)
### State Management (Provider)
All providers declared in [lib/main.dart](lib/main.dart):
- `UserState`: Auth state and current user
- `VisualStateProvider`: Theme/visual preferences (light/dark mode)
- `VideosProvider`: Media files CRUD, upload/download, metadata management
- **Pattern:** Use `context.read<T>()` for one-time actions, `context.watch<T>()` for reactive UI
### Navigation Structure
```
/login → /dashboard (stats: reproducciones, videos, categorías)
└── Sidemenu:
├── Dashboard (default)
├── Gestor de Videos (PlutoGrid con CRUD)
└── Configuración (placeholder - work in progress)
```
- **Simplified:** No empresa/negocio selection - single organization (EnergyMedia)
- See [lib/router/router.dart](lib/router/router.dart)
## Database Schema & Critical Rules
### Media Library Schema (`media_library`)
**Tables:**
- `media_files`: Main video records (file_name, title, file_url, storage_path, metadata_json, media_category_fk, organization_fk)
- `media_categories`: Video categories (category_name, category_description)
- `media_posters`: Poster/thumbnail associations (media_file_id, poster_file_id)
- View: `vw_media_files_with_posters` - Complete media info with category and poster
### Organization Filter Rule
**CRITICAL:** ALL operations on `media_files` MUST include `organization_fk = 17` filter:
```dart
// CORRECT - filtered by organization
final response = await supabaseML
.from('media_files')
.select()
.eq('organization_fk', 17);
// WRONG - missing organization filter
final response = await supabaseML
.from('media_files') // ❌ Returns all organizations!
.select();
```
**Always:** Insert/update operations must set `organization_fk: 17`
### metadata_json Structure
Standard fields in `media_files.metadata_json`:
```json
{
"uploaded_at": "2026-01-10T10:30:00Z",
"reproducciones": 150,
"categorias": ["tutorial", "energía"],
"original_file_name": "video_original.mp4",
"duration_seconds": 320,
"resolution": "1920x1080",
"last_viewed_at": "2026-01-10T12:00:00Z"
}
```
### Supabase Storage
**Bucket:** `energymedia`
- `energymedia/videos/` - Video files
- `energymedia/imagenes/` - Poster/thumbnail images
## Responsive Design Standards
**Breakpoints:** Mobile ≤800px, Tablet 801-1200px, Desktop >1200px
**Common pattern:**
```dart
final isMobile = MediaQuery.of(context).size.width <= 800;
// Desktop: PlutoGrid tables with video thumbnails
// Mobile: Card-based lists with posters
```
**Key files:**
- Desktop layouts: `lib/pages/videos/*_page.dart`
- Mobile adaptations: Check for conditional rendering in same files
- Reference constant: `mobileSize = 800` in [lib/helpers/constants.dart](lib/helpers/constants.dart#L22)
## Critical Files & Workflows
### Global State (`lib/helpers/globals.dart`)
- `currentUser`: Authenticated user model (nullable)
- `supabaseML`: Media Library-specific Supabase client (schema: `media_library`)
- `plutoGridScrollbarConfig()`, `plutoGridStyleConfig()`: Consistent table styling
- **Always check `currentUser != null` before auth-dependent operations**
### Models (Domain-Driven)
- Media models: `lib/models/media/*.dart`
- Pattern: `fromMap()` for Supabase JSON deserialization
- Key models: `MediaFileModel`, `MediaCategoryModel`, `MediaPosterModel`
### Video Players
**Libraries:** `appinio_video_player`, `video_player`, `chewie`
**Usage patterns:**
- `VideoPlayerLive`: Full-screen player with controls (chewie-based)
- `VideoScreenNew`: Embedded player (appinio-based)
- `VideoScreenThumbnail`: Generate thumbnails from video
- See [lib/pages/widgets/video_player_*.dart](lib/pages/widgets/)
### Theme & Styling
**Color Scheme (EnergyMedia):**
- **Primary Gradient:** Cyan→Yellow (linear-gradient(135deg, #4EC9F5, #FFB733))
- **Accents:** Purple (#6B2F8A), Cyan (#4EC9F5), Yellow (#FFB733), Red (#FF2D2D), Orange (#FF7A3D)
- **Dark Mode Backgrounds:** #0B0B0D (main), #121214 (surface1), #1A1A1D (surface2)
- **Light Mode Backgrounds:** #FFFFFF (main), #F7F7F7 (surface1), #EFEFEF (surface2)
- **Typography:** Google Fonts Poppins (Regular 400, Bold 700)
- Use `AppTheme.of(context)` for colors, not hardcoded values
- See [lib/theme/theme.dart](lib/theme/theme.dart)
## Development Commands
```bash
# Run (web dev)
flutter run -d chrome
# Build web (production)
flutter build web
# Dependencies
flutter pub get
# Clean build
flutter clean && flutter pub get
```
## Common Tasks
### Adding New Provider
1. Create in `lib/providers/`
2. Extend `ChangeNotifier`
3. Register in [lib/main.dart](lib/main.dart) `MultiProvider.providers`
4. Export in `lib/providers/providers.dart` if needed
### Creating Responsive Pages
1. Check screen width: `MediaQuery.of(context).size.width`
2. Desktop: Use PlutoGrid for tables, full layouts
3. Mobile: Use ListView with Cards, simplified forms
4. Reference [lib/pages/videos/gestor_videos_page.dart](lib/pages/videos/gestor_videos_page.dart) for pattern
### Querying Media Data
```dart
// CORRECT - uses media_library schema + organization filter
final response = await supabaseML
.from('media_files')
.select()
.eq('organization_fk', 17)
.order('created_at_timestamp', ascending: false);
// WRONG - uses default schema
final response = await supabase
.from('media_files') // ❌ Table not found!
.select();
// WRONG - missing organization filter
final response = await supabaseML
.from('media_files')
.select(); // ❌ Returns data from ALL organizations!
```
### Uploading Media Files
```dart
// 1. Upload video to storage
final videoPath = await supabaseML.storage
.from('energymedia')
.upload('videos/$fileName', videoBytes);
// 2. Insert record with organization filter
await supabaseML.from('media_files').insert({
'file_name': fileName,
'title': title,
'file_url': publicUrl,
'storage_path': 'videos/$fileName',
'organization_fk': 17, // ⚠️ REQUIRED!
'metadata_json': {
'uploaded_at': DateTime.now().toIso8601String(),
'reproducciones': 0,
'original_file_name': originalName,
}
});
```
### Working with Video Thumbnails
```dart
// Generate thumbnail from video (VideoScreenThumbnail widget)
VideoScreenThumbnail(video: videoUrl)
// Display poster from media_posters
Image.network(posterUrl, fit: BoxFit.cover)
// Fallback: Show placeholder if no poster
if (posterUrl != null)
Image.network(posterUrl)
else
Icon(Icons.video_library, size: 48)
```
## Project Conventions
### Naming
- Files: `snake_case.dart` (e.g., `videos_provider.dart`)
- Classes: `PascalCase` (e.g., `VideosProvider`)
- Private members: `_underscorePrefixed` (e.g., `_selectedVideo`)
- Models suffix: `*Model` (e.g., `MediaFileModel`)
### File Organization
```
lib/
pages/ # Full pages
videos/ # Video management pages
gestor_videos_page.dart
dashboard_page.dart
widgets/ # Video-specific widgets
widgets/ # Shared widgets (video players, etc.)
providers/ # State management
videos_provider.dart
models/ # Data models
media/ # Media domain models
helpers/ # Utilities, globals, extensions
services/ # External service integrations
```
### Import Style
- Absolute imports: `import 'package:nethive_neo/...'`
- Barrel exports: Use `lib/models/models.dart`, `lib/providers/providers.dart`
## Testing & Debugging
- **No formal tests yet** - manual testing in browser/device
- Dev mode: Hot reload enabled (Flutter devtools)
- Check browser console for Supabase RPC errors
- Useful: Flutter Inspector for widget tree debugging
## Key Reference Documents
- [assets/referencia/tablas_energymedia.txt](assets/referencia/tablas_energymedia.txt): Database schema reference
- [pubspec.yaml](pubspec.yaml): All dependencies and versions
- [lib/helpers/constants.dart](lib/helpers/constants.dart): Environment config, API keys, constants
- GitHub repo: https://github.com/CB-Luna/energymedia_content_manager
## Known Quirks
- PlutoGrid requires `dependency_override` for `intl: ^0.19.0` compatibility
- Always call `initGlobals()` in main before app initialization
- GoRouter `optionURLReflectsImperativeAPIs` must be `true` for proper routing
- Mobile forms use full-screen modals, not dialogs (better UX on small screens)
- Video thumbnails: Use `VideoScreenThumbnail` widget or fallback to category/poster image