chore: initial project setup with documentation
- Add PHILOSOPHY.md with UI vision and principles - Add README.md with project overview and operational details - Add AGENTS.md with LLM agent instructions - Add PLAN.md with implementation roadmap - Add .gitignore for Flutter project 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,670 @@
|
||||
# Tatlock UI - Comprehensive Implementation Plan
|
||||
|
||||
> **Session Continuity Note**: This document is designed to be self-contained. If starting a new session, read this entire document to understand the project scope and decisions made.
|
||||
|
||||
## Project Summary
|
||||
|
||||
**Tatlock UI** is a Flutter application that will serve as the unified homelab dashboard for the Tower of Joy infrastructure, replacing Organizr at `home.schweitz.net`.
|
||||
|
||||
### Key Facts
|
||||
- **Project folder**: `/mnt/media/Projects/tatlock-ui` (renamed from tower-ui)
|
||||
- **Git remote**: `ssh://git@git.schweitz.net:2222/jpmschweitzer/tatlock-ui.git` (already created)
|
||||
- **Platforms**: web (primary), android, macos, linux, windows, ios
|
||||
- **Primary backend**: Core API (port 8083) + Tatlock API (port 8000)
|
||||
- **Authentication**: Authentik SSO via OIDC
|
||||
- **Web deployment**: Docker container on port 8092, proxied via NPM at `home.schweitz.net`
|
||||
|
||||
### What It Replaces
|
||||
- **Organizr** (current dashboard at home.schweitz.net) - See `/mnt/media/Projects/tower-ui/uploads/portainer-ui.png` for current UI
|
||||
- **Eventually Open WebUI** - Custom LLM chat interface for Tatlock agents
|
||||
|
||||
---
|
||||
|
||||
## Architecture Decisions (Final)
|
||||
|
||||
### 1. Dogmatic Clean Architecture
|
||||
**Decision**: Use strict Clean Architecture with documented requirements for consistency.
|
||||
|
||||
```
|
||||
lib/features/{feature}/
|
||||
├── presentation/ # UI Layer
|
||||
│ ├── pages/ # Full screen widgets
|
||||
│ ├── widgets/ # Feature-specific widgets
|
||||
│ └── providers/ # Riverpod providers/controllers
|
||||
├── domain/ # Business Logic Layer
|
||||
│ ├── entities/ # Business objects (immutable)
|
||||
│ ├── repositories/ # Abstract repository interfaces
|
||||
│ └── usecases/ # Single-purpose business operations
|
||||
└── data/ # Data Layer
|
||||
├── models/ # JSON serializable DTOs
|
||||
├── datasources/ # API clients, local storage
|
||||
└── repositories/ # Repository implementations
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- Domain layer has NO dependencies on Flutter or external packages
|
||||
- Data layer implements domain interfaces
|
||||
- Presentation layer only depends on domain layer
|
||||
- All cross-layer communication via dependency injection (Riverpod)
|
||||
|
||||
### 2. DataGrid Component System
|
||||
**Decision**: Adapt the fframe ListGrid pattern for consistent table UIs across all features.
|
||||
|
||||
Based on: `https://github.com/postmeridiem/fframe/blob/main/fframe/lib/screens/listgrid_screen/`
|
||||
|
||||
Key components:
|
||||
- `DataGridConfig<T>` - Declarative configuration
|
||||
- `DataGridColumn<T>` - Column definitions with builders
|
||||
- `DataGridAction<T>` - Per-row actions
|
||||
- `DataGridBulkAction<T>` - Multi-select actions
|
||||
- `DataGridSource<T>` - Abstract data source (Core API adapter)
|
||||
- `DataGridNotifier<T>` - Riverpod state management
|
||||
|
||||
### 3. Theming
|
||||
**Decision**: System preference + manual override, using Material3 colorScheme exclusively.
|
||||
|
||||
- Follow system dark/light mode by default
|
||||
- Allow manual override that persists to SharedPreferences
|
||||
- Never use raw colors in widgets - always `Theme.of(context).colorScheme.*`
|
||||
- Use automatic theme generator (flex_color_scheme or similar)
|
||||
|
||||
### 4. State Management
|
||||
**Decision**: Riverpod 2.x with code generation.
|
||||
|
||||
Packages: `flutter_riverpod`, `riverpod_annotation`, `riverpod_generator`
|
||||
|
||||
---
|
||||
|
||||
## Implementation Phases
|
||||
|
||||
### Phase 0: Documentation
|
||||
**Goal**: Establish project documentation before any code.
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
tatlock-ui/
|
||||
├── README.md # Project overview, setup instructions
|
||||
├── AGENTS.md # LLM agent instructions (copy pattern from Tatlock)
|
||||
├── CHANGELOG.md # Version history (start with 0.0.1)
|
||||
├── docs/
|
||||
│ ├── ARCHITECTURE.md # Clean Architecture rules and patterns
|
||||
│ ├── API_INTEGRATION.md # Core API and Tatlock API endpoints
|
||||
│ ├── DEPLOYMENT.md # Docker, NPM, Portainer setup
|
||||
│ ├── DATAGRID.md # DataGrid component API specification
|
||||
│ └── THEMING.md # Theme system documentation
|
||||
```
|
||||
|
||||
**Content for ARCHITECTURE.md** - Document:
|
||||
- Clean Architecture layer rules
|
||||
- File naming conventions
|
||||
- Dependency injection patterns
|
||||
- Testing requirements per layer
|
||||
|
||||
**Content for API_INTEGRATION.md** - Document:
|
||||
- Core API endpoints (from `/mnt/media/Projects/portainer-core/CONTAINERS.md`)
|
||||
- Tatlock API endpoints (chat completions, responses API)
|
||||
- Authentication flow with Authentik
|
||||
- Error handling patterns
|
||||
|
||||
**Content for DATAGRID.md** - Document:
|
||||
- Full DataGrid API spec (see below)
|
||||
- Usage examples for each feature
|
||||
- Customization patterns
|
||||
|
||||
### Phase 1: Foundation
|
||||
**Goal**: Project setup, core infrastructure, basic shell.
|
||||
|
||||
**Tasks**:
|
||||
1. Initialize Flutter project with all platforms
|
||||
2. Initialize git and push to Gitea
|
||||
3. Set up project structure (Clean Architecture folders)
|
||||
4. Configure dependencies (pubspec.yaml)
|
||||
5. Implement core infrastructure:
|
||||
- `lib/core/api/api_client.dart` - Dio HTTP client
|
||||
- `lib/core/api/api_interceptors.dart` - Auth, logging, error handling
|
||||
- `lib/core/auth/auth_service.dart` - Authentik OIDC
|
||||
- `lib/core/auth/auth_provider.dart` - Riverpod auth state
|
||||
- `lib/core/config/app_config.dart` - Environment configuration
|
||||
- `lib/core/config/service_locator.dart` - Dependency setup
|
||||
- `lib/core/theme/app_theme.dart` - Material3 theme
|
||||
- `lib/core/theme/theme_provider.dart` - Theme state (system + override)
|
||||
6. Create app shell:
|
||||
- `lib/app.dart` - MaterialApp.router setup
|
||||
- `lib/routing/app_router.dart` - go_router configuration
|
||||
- `lib/shared/layouts/app_scaffold.dart` - Main layout with sidebar
|
||||
- `lib/shared/layouts/adaptive_navigation.dart` - Responsive nav
|
||||
7. Set up CI/CD:
|
||||
- `.gitea/workflows/test.yml`
|
||||
- `.gitea/workflows/build-web.yml`
|
||||
- `Dockerfile`
|
||||
|
||||
### Phase 2: DataGrid Component
|
||||
**Goal**: Build the reusable DataGrid system before features.
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
lib/shared/components/data_grid/
|
||||
├── data_grid.dart # Main widget
|
||||
├── data_grid_config.dart # Configuration classes
|
||||
├── data_grid_column.dart # Column definition
|
||||
├── data_grid_action.dart # Action definitions
|
||||
├── data_grid_source.dart # Abstract data source
|
||||
├── data_grid_provider.dart # Riverpod state
|
||||
├── data_grid_state.dart # Freezed state class
|
||||
├── widgets/
|
||||
│ ├── data_grid_header.dart # Header row with sort indicators
|
||||
│ ├── data_grid_row.dart # Data row
|
||||
│ ├── data_grid_cell.dart # Cell wrapper
|
||||
│ ├── data_grid_checkbox.dart # Selection checkbox
|
||||
│ ├── data_grid_actions_menu.dart # Row actions popup
|
||||
│ ├── data_grid_bulk_actions.dart # Bulk action bar
|
||||
│ ├── data_grid_search_bar.dart # Search input
|
||||
│ ├── data_grid_footer.dart # Footer with count/pagination
|
||||
│ └── data_grid_empty_state.dart # Empty state display
|
||||
└── adapters/
|
||||
└── core_api_source.dart # Core API data source adapter
|
||||
```
|
||||
|
||||
### Phase 3: Dashboard (Organizr Replacement)
|
||||
**Goal**: Replace Organizr homepage with Tatlock UI dashboard.
|
||||
|
||||
**Features to match** (from portainer-ui.png):
|
||||
- Sidebar navigation with grouped items
|
||||
- System metrics gauges (CPU, Memory, Disk)
|
||||
- Weather widget (optional - requires API)
|
||||
- Air quality widget (optional)
|
||||
- Service quick links grid
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
lib/features/dashboard/
|
||||
├── presentation/
|
||||
│ ├── pages/dashboard_page.dart
|
||||
│ └── widgets/
|
||||
│ ├── metrics_gauge.dart
|
||||
│ ├── service_card.dart
|
||||
│ ├── service_grid.dart
|
||||
│ └── quick_actions.dart
|
||||
├── domain/
|
||||
│ ├── entities/system_metrics.dart
|
||||
│ └── repositories/dashboard_repository.dart
|
||||
└── data/
|
||||
├── datasources/core_api_dashboard_source.dart
|
||||
└── repositories/dashboard_repository_impl.dart
|
||||
```
|
||||
|
||||
**Core API endpoints used**:
|
||||
- `GET /infrastructure/resources/system` - System metrics
|
||||
- `GET /infrastructure/widget-data` - Services + groups
|
||||
- `GET /health` - Overall health status
|
||||
|
||||
### Phase 4: Container Management
|
||||
**Goal**: Full container CRUD and monitoring.
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
lib/features/containers/
|
||||
├── presentation/
|
||||
│ ├── pages/
|
||||
│ │ ├── containers_list_page.dart
|
||||
│ │ └── container_detail_page.dart
|
||||
│ ├── widgets/
|
||||
│ │ ├── container_status_badge.dart
|
||||
│ │ ├── container_logs_viewer.dart
|
||||
│ │ └── container_resource_chart.dart
|
||||
│ └── providers/
|
||||
│ └── containers_provider.dart
|
||||
├── domain/
|
||||
│ ├── entities/container.dart
|
||||
│ ├── repositories/container_repository.dart
|
||||
│ └── usecases/
|
||||
│ ├── get_containers.dart
|
||||
│ ├── start_container.dart
|
||||
│ ├── stop_container.dart
|
||||
│ ├── restart_container.dart
|
||||
│ └── get_container_logs.dart
|
||||
└── data/
|
||||
├── models/container_model.dart
|
||||
├── datasources/containers_datasource.dart
|
||||
└── repositories/container_repository_impl.dart
|
||||
```
|
||||
|
||||
**Core API endpoints used**:
|
||||
- `GET /infrastructure/containers`
|
||||
- `GET /infrastructure/containers/{id}`
|
||||
- `GET /infrastructure/containers/{id}/logs`
|
||||
- `POST /infrastructure/containers/{id}/{action}`
|
||||
- `GET /infrastructure/resources/containers`
|
||||
|
||||
### Phase 5: LLM Chat Interface
|
||||
**Goal**: Custom chat UI for Tatlock agents with SSE streaming.
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
lib/core/api/
|
||||
└── sse_client.dart # Platform-aware SSE streaming
|
||||
|
||||
lib/features/chat/
|
||||
├── presentation/
|
||||
│ ├── pages/chat_page.dart
|
||||
│ └── widgets/
|
||||
│ ├── message_list.dart
|
||||
│ ├── message_bubble.dart
|
||||
│ ├── reasoning_block.dart # Collapsible think blocks
|
||||
│ ├── streaming_text.dart
|
||||
│ ├── chat_input.dart
|
||||
│ ├── model_selector.dart
|
||||
│ └── agent_indicator.dart # Shows which agent is responding
|
||||
├── domain/
|
||||
│ ├── entities/
|
||||
│ │ ├── message.dart
|
||||
│ │ ├── conversation.dart
|
||||
│ │ └── chat_completion.dart
|
||||
│ └── repositories/chat_repository.dart
|
||||
└── data/
|
||||
├── models/
|
||||
│ ├── chat_completion_request.dart
|
||||
│ ├── chat_completion_response.dart
|
||||
│ └── chat_completion_chunk.dart
|
||||
├── datasources/tatlock_api_datasource.dart
|
||||
└── repositories/chat_repository_impl.dart
|
||||
```
|
||||
|
||||
**Tatlock API endpoints used**:
|
||||
- `POST /v1/chat/completions` (with `stream: true`)
|
||||
- `GET /v1/models`
|
||||
|
||||
**SSE Implementation Notes**:
|
||||
- Use platform-conditional imports
|
||||
- Web: XMLHttpRequest or fetch API
|
||||
- Mobile/Desktop: eventsource or http package with stream parsing
|
||||
- Handle `reasoning_content` field for think blocks (DeepSeek R1 format)
|
||||
|
||||
### Phase 6: Home Automation
|
||||
**Goal**: Control Home Assistant via Core API.
|
||||
|
||||
**Files to create**:
|
||||
```
|
||||
lib/features/housekeeping/
|
||||
├── presentation/
|
||||
│ ├── pages/
|
||||
│ │ ├── housekeeping_page.dart
|
||||
│ │ └── area_detail_page.dart
|
||||
│ └── widgets/
|
||||
│ ├── device_tile.dart
|
||||
│ ├── device_control_sheet.dart
|
||||
│ ├── scene_card.dart
|
||||
│ └── area_grid.dart
|
||||
├── domain/
|
||||
│ ├── entities/
|
||||
│ │ ├── device.dart
|
||||
│ │ ├── area.dart
|
||||
│ │ └── scene.dart
|
||||
│ └── repositories/housekeeping_repository.dart
|
||||
└── data/
|
||||
├── models/
|
||||
│ ├── device_model.dart
|
||||
│ └── device_control_request.dart
|
||||
├── datasources/housekeeping_datasource.dart
|
||||
└── repositories/housekeeping_repository_impl.dart
|
||||
```
|
||||
|
||||
**Core API endpoints used**:
|
||||
- `GET /housekeeping/devices`
|
||||
- `GET /housekeeping/areas`
|
||||
- `GET /housekeeping/scenes`
|
||||
- `POST /housekeeping/devices/{entity_id}/control`
|
||||
- `POST /housekeeping/scenes/{scene_id}/activate`
|
||||
|
||||
### Phase 7: Polish & Deployment
|
||||
**Goal**: Production-ready release.
|
||||
|
||||
**Tasks**:
|
||||
1. Flutter web optimization (deferred loading, tree shaking)
|
||||
2. Desktop platform testing and fixes
|
||||
3. Offline resilience patterns
|
||||
4. Error handling and user feedback
|
||||
5. Performance profiling
|
||||
6. Docker build and Portainer stack deployment
|
||||
7. NPM proxy configuration update (home.schweitz.net → tatlock-ui)
|
||||
|
||||
---
|
||||
|
||||
## DataGrid API Specification
|
||||
|
||||
### Configuration Classes
|
||||
|
||||
```dart
|
||||
/// Main configuration for a data grid
|
||||
class DataGridConfig<T> {
|
||||
const DataGridConfig({
|
||||
required this.columns,
|
||||
this.actions = const [],
|
||||
this.bulkActions = const [],
|
||||
this.rowsSelectable = false,
|
||||
this.showHeader = true,
|
||||
this.showFooter = true,
|
||||
this.enableSearch = false,
|
||||
this.searchableColumns = const [],
|
||||
this.defaultSortColumn,
|
||||
this.defaultSortDescending = false,
|
||||
this.emptyStateBuilder,
|
||||
this.loadingBuilder,
|
||||
this.errorBuilder,
|
||||
this.onRowTap,
|
||||
this.cellPadding = const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
this.headerHeight = 48.0,
|
||||
this.rowHeight,
|
||||
this.dataMode = const DataGridDataMode.all(),
|
||||
});
|
||||
|
||||
final List<DataGridColumn<T>> columns;
|
||||
final List<DataGridAction<T>> actions;
|
||||
final List<DataGridBulkAction<T>> bulkActions;
|
||||
final bool rowsSelectable;
|
||||
final bool showHeader;
|
||||
final bool showFooter;
|
||||
final bool enableSearch;
|
||||
final List<int> searchableColumns;
|
||||
final int? defaultSortColumn;
|
||||
final bool defaultSortDescending;
|
||||
final Widget Function(BuildContext)? emptyStateBuilder;
|
||||
final Widget Function(BuildContext)? loadingBuilder;
|
||||
final Widget Function(BuildContext, Object error)? errorBuilder;
|
||||
final void Function(T item)? onRowTap;
|
||||
final EdgeInsetsGeometry cellPadding;
|
||||
final double headerHeight;
|
||||
final double? rowHeight;
|
||||
final DataGridDataMode dataMode;
|
||||
}
|
||||
|
||||
/// Column definition
|
||||
class DataGridColumn<T> {
|
||||
const DataGridColumn({
|
||||
required this.header,
|
||||
required this.valueBuilder,
|
||||
this.cellBuilder,
|
||||
this.cellControlsBuilder,
|
||||
this.width = const DataGridColumnWidth.flex(1),
|
||||
this.alignment = DataGridColumnAlignment.start,
|
||||
this.sortable = false,
|
||||
this.sortField,
|
||||
this.searchable = false,
|
||||
this.visible = true,
|
||||
this.tooltip,
|
||||
});
|
||||
|
||||
final String header;
|
||||
final String Function(T item) valueBuilder;
|
||||
final Widget Function(BuildContext, T item)? cellBuilder;
|
||||
final Widget Function(BuildContext, T item)? cellControlsBuilder;
|
||||
final DataGridColumnWidth width;
|
||||
final DataGridColumnAlignment alignment;
|
||||
final bool sortable;
|
||||
final String? sortField;
|
||||
final bool searchable;
|
||||
final bool visible;
|
||||
final String Function(T item)? tooltip;
|
||||
}
|
||||
|
||||
/// Column width - sealed class with variants
|
||||
sealed class DataGridColumnWidth {
|
||||
const DataGridColumnWidth._();
|
||||
const factory DataGridColumnWidth.fixed(double width) = _FixedWidth;
|
||||
const factory DataGridColumnWidth.flex(int flex) = _FlexWidth;
|
||||
const factory DataGridColumnWidth.fraction(double fraction) = _FractionWidth;
|
||||
}
|
||||
|
||||
/// Per-row action
|
||||
class DataGridAction<T> {
|
||||
const DataGridAction({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
this.showWhen,
|
||||
this.destructive = false,
|
||||
this.requiresConfirmation = false,
|
||||
this.confirmationMessage,
|
||||
});
|
||||
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final Future<void> Function(T item) onTap;
|
||||
final bool Function(T item)? showWhen;
|
||||
final bool destructive;
|
||||
final bool requiresConfirmation;
|
||||
final String? confirmationMessage;
|
||||
}
|
||||
|
||||
/// Bulk action on selected rows
|
||||
class DataGridBulkAction<T> {
|
||||
const DataGridBulkAction({
|
||||
required this.icon,
|
||||
required this.label,
|
||||
required this.onTap,
|
||||
this.minSelected = 1,
|
||||
this.maxSelected,
|
||||
this.destructive = false,
|
||||
this.requiresConfirmation = false,
|
||||
});
|
||||
|
||||
final IconData icon;
|
||||
final String label;
|
||||
final Future<void> Function(List<T> items) onTap;
|
||||
final int minSelected;
|
||||
final int? maxSelected;
|
||||
final bool destructive;
|
||||
final bool requiresConfirmation;
|
||||
}
|
||||
|
||||
/// Data loading mode
|
||||
sealed class DataGridDataMode {
|
||||
const DataGridDataMode._();
|
||||
const factory DataGridDataMode.all() = _AllDataMode;
|
||||
const factory DataGridDataMode.paginated({int pageSize}) = _PaginatedDataMode;
|
||||
const factory DataGridDataMode.infinite({int initialLoad, int loadMoreThreshold}) = _InfiniteDataMode;
|
||||
}
|
||||
```
|
||||
|
||||
### Data Source
|
||||
|
||||
```dart
|
||||
/// Abstract data source
|
||||
abstract class DataGridSource<T> {
|
||||
Future<DataGridResult<T>> fetch({
|
||||
String? searchQuery,
|
||||
String? sortField,
|
||||
bool sortDescending = false,
|
||||
int? offset,
|
||||
int? limit,
|
||||
});
|
||||
|
||||
Future<int> count({String? searchQuery});
|
||||
}
|
||||
|
||||
/// Result wrapper
|
||||
class DataGridResult<T> {
|
||||
const DataGridResult({
|
||||
required this.items,
|
||||
required this.totalCount,
|
||||
this.hasMore = false,
|
||||
});
|
||||
|
||||
final List<T> items;
|
||||
final int totalCount;
|
||||
final bool hasMore;
|
||||
}
|
||||
|
||||
/// Core API adapter
|
||||
class CoreApiDataSource<T> extends DataGridSource<T> {
|
||||
CoreApiDataSource({
|
||||
required this.endpoint,
|
||||
required this.fromJson,
|
||||
this.searchParam = 'search',
|
||||
this.sortParam = 'sort',
|
||||
this.orderParam = 'order',
|
||||
});
|
||||
|
||||
final String endpoint;
|
||||
final T Function(Map<String, dynamic>) fromJson;
|
||||
final String searchParam;
|
||||
final String sortParam;
|
||||
final String orderParam;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Web Deployment Architecture
|
||||
|
||||
```
|
||||
Internet → NPM (home.schweitz.net:443) → tatlock-ui container (port 8092) → Flutter web
|
||||
```
|
||||
|
||||
**Dockerfile**:
|
||||
```dockerfile
|
||||
FROM ghcr.io/cirruslabs/flutter:3.27.0 AS build
|
||||
WORKDIR /app
|
||||
COPY pubspec.* ./
|
||||
RUN flutter pub get
|
||||
COPY . .
|
||||
RUN flutter build web --release
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=build /app/build/web /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
```
|
||||
|
||||
**Portainer Stack (docker-compose.yml)**:
|
||||
```yaml
|
||||
version: '3.8'
|
||||
services:
|
||||
tatlock-ui:
|
||||
image: git.schweitz.net/jpmschweitzer/tatlock-ui:latest
|
||||
container_name: tatlock-ui
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "8092:80"
|
||||
networks:
|
||||
- docker-dataplane
|
||||
|
||||
networks:
|
||||
docker-dataplane:
|
||||
external: true
|
||||
```
|
||||
|
||||
**NPM Proxy Configuration**:
|
||||
- Domain: `home.schweitz.net`
|
||||
- Forward to: `localhost:8092`
|
||||
- SSL: Let's Encrypt, Force SSL
|
||||
|
||||
---
|
||||
|
||||
## Key Dependencies (pubspec.yaml)
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
# State Management
|
||||
flutter_riverpod: ^2.5.0
|
||||
riverpod_annotation: ^2.3.0
|
||||
hooks_riverpod: ^2.5.0
|
||||
flutter_hooks: ^0.20.0
|
||||
|
||||
# Code Generation
|
||||
freezed_annotation: ^2.4.0
|
||||
json_annotation: ^4.8.0
|
||||
|
||||
# Networking
|
||||
dio: ^5.4.0
|
||||
retrofit: ^4.1.0
|
||||
|
||||
# Authentication
|
||||
flutter_appauth: ^6.0.0
|
||||
flutter_secure_storage: ^9.0.0
|
||||
|
||||
# Routing
|
||||
go_router: ^13.0.0
|
||||
|
||||
# UI
|
||||
flex_color_scheme: ^7.3.0
|
||||
flutter_adaptive_scaffold: ^0.1.0
|
||||
flutter_markdown: ^0.6.0
|
||||
flutter_highlight: ^0.7.0
|
||||
fl_chart: ^0.65.0
|
||||
|
||||
dev_dependencies:
|
||||
build_runner: ^2.4.0
|
||||
freezed: ^2.4.0
|
||||
json_serializable: ^6.7.0
|
||||
riverpod_generator: ^2.4.0
|
||||
retrofit_generator: ^8.1.0
|
||||
mocktail: ^1.0.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reference Documents
|
||||
|
||||
- **Infrastructure**: `/mnt/media/Projects/portainer-core/CONTAINERS.md`
|
||||
- **Tatlock Philosophy**: `/mnt/media/Projects/tatlock/PHILOSOPHY.md`
|
||||
- **Core API**: `http://192.168.86.149:8083/docs`
|
||||
- **Tatlock API**: `http://192.168.86.149:8000/docs`
|
||||
- **fframe ListGrid**: `https://github.com/postmeridiem/fframe/tree/main/fframe/lib/screens/listgrid_screen`
|
||||
- **Current Organizr UI**: `/mnt/media/Projects/tower-ui/uploads/portainer-ui.png`
|
||||
- **Tatlock Logo**: `/mnt/media/Projects/tower-ui/uploads/logo-tatlock.png`
|
||||
|
||||
---
|
||||
|
||||
## Navigation Structure (matching Organizr)
|
||||
|
||||
```
|
||||
├── Homepage (Dashboard)
|
||||
├── Tatlock (Chat)
|
||||
├── Jellyfin (external link)
|
||||
├── SearXNG (external link)
|
||||
├── AMP
|
||||
│ └── AMP Home
|
||||
├── Coding
|
||||
│ ├── Cloud IDE (external)
|
||||
│ └── Gitea (external)
|
||||
├── Infrastructure
|
||||
│ ├── Containers (main feature)
|
||||
│ ├── Netdata (external link)
|
||||
│ ├── Portainer (external link)
|
||||
│ └── Proxy Manager (external link)
|
||||
├── Housekeeping (Home Automation)
|
||||
├── API Docs (external link to Core API)
|
||||
└── Settings
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Authentik Configuration
|
||||
|
||||
Create application in Authentik admin:
|
||||
- **Name**: Tatlock UI
|
||||
- **Slug**: `tatlock-ui`
|
||||
- **Provider**: OAuth2/OIDC
|
||||
- **Client Type**: Public
|
||||
- **Redirect URIs**:
|
||||
- `https://home.schweitz.net/callback`
|
||||
- `net.schweitz.tatlock://callback`
|
||||
- `http://localhost:*/callback`
|
||||
|
||||
---
|
||||
|
||||
## Next Steps After Session Resume
|
||||
|
||||
1. **Verify folder rename**: Confirm `/mnt/media/Projects/tatlock-ui` exists
|
||||
2. **Start Phase 0**: Create documentation files
|
||||
3. **Initialize Flutter**: `flutter create . --project-name tatlock_ui --org net.schweitz --platforms=web,android,ios,macos,linux,windows`
|
||||
4. **Initialize Git**: `git init && git remote add origin ssh://git@git.schweitz.net:2222/jpmschweitzer/tatlock-ui.git`
|
||||
5. **Continue with Phase 1**: Core infrastructure setup
|
||||
|
||||
---
|
||||
|
||||
*Last updated: 2024-12-30*
|
||||
*Plan version: 2.0*
|
||||
Reference in New Issue
Block a user