Compare commits

...

196 Commits

Author SHA1 Message Date
Nils-Johan Gynther e917b2965c docs(filanalys): expand security analysis section with NestJS/Prisma/Next.js specifics and automated detection
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m30s
Test Suite / flutter-quality (push) Failing after 1m21s
- Added detailed security risk table with examples for SQL injection, XSS, secrets exposure, CSRF, and insecure deserialization
- Included NestJS/Prisma/Next.js-specific detection patterns and PowerShell scripts for automated scanning
- Added tool integration instructions for npm audit and eslint-plugin-security
- Expanded security analysis to focus on framework-specific vulnerabilities rather than generic risks
2026-05-25 11:52:25 +02:00
Nils-Johan Gynther f6b9af2f38 feat(filanalys): add TypeScript syntax analysis section with automated error detection
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m36s
Test Suite / flutter-quality (push) Failing after 1m21s
2026-05-25 11:40:02 +02:00
Nils-Johan Gynther 9e513c2f5e chore(docs): consolidate legacy documentation into new structure
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m57s
Test Suite / flutter-quality (push) Failing after 1m16s
- Removed outdated documentation files (MVP_CHECKLISTA.md, NEXT_STEPS.md, README.md, TEKNISK_BESKRIVNING.md, filanalys.md, flyerimporter.md, kilo.json, plan-dokumentation.md)
- Added new centralized documentation structure under docs/ directory
- Added .kilo/ directory for Kilo AI agent configuration and plans

BREAKING CHANGE: Legacy documentation files removed and replaced with new centralized structure
2026-05-25 08:14:35 +02:00
Nils-Johan Gynther 2a87a18edd fix(flyer-import): handle null displayNameDetailed in fallback assignment
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m27s
Test Suite / flutter-quality (push) Failing after 1m33s
- Changed fallback assignment from direct value to null coalescing
- Ensures consistent null handling when displayNameDetailed is falsy
2026-05-24 22:15:37 +02:00
Nils-Johan Gynther 451d04cf39 refactor(flyer-import): improve displayNameDetailed handling
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 2m37s
Test Suite / flutter-quality (push) Failing after 1m11s
- Simplified conditional logic for displayNameDetailed assignment
- Added fallback to preserve existing value when displayNameDetailed is falsy
- Reformatted multi-line object initialization for better readability
2026-05-24 22:09:44 +02:00
Nils-Johan Gynther e492ea9a2e test(admin-panel): add product context to AI trace warnings
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 2m19s
Test Suite / flutter-quality (push) Failing after 1m11s
- Added `productName` field to `AdminAiWarning` in test data
- Updated warning test cases to include product context for better traceability
2026-05-24 21:55:45 +02:00
Nils-Johan Gynther 27d622bfe6 feat(inventory): add multi-country origin tracking
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 3m32s
Test Suite / flutter-quality (push) Failing after 1m0s
- Added `originCountries` field to `InventoryItem` model for multi-country origin support
- Updated `CreateInventoryDto` and `UpdateInventoryDto` with `originCountries` array field
- Modified `InventoryService` to handle `originCountries` in create and update operations
- Added `origin` field to `FlyerImportItem` response type for consistency
- Added `categoryId` field to `ParsedReceiptItem` DTO for improved receipt parsing
- Created database migration `20260524_add_origin_countries` for schema changes
2026-05-24 21:31:53 +02:00
Nils-Johan Gynther ca1eed5061 feat(ai): enhance AI trace warnings with product context
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m31s
Test Suite / flutter-quality (push) Failing after 1m12s
- Added `productName` field to `AdminAiWarning` to include product context in warnings
- Updated `collectWarnings` to extract and include `rawName` as `productName` in AI trace warnings
- Added `signals` field to `FlyerParseItem` type for detailed product signals
- Enhanced Flutter admin panel to display product names in AI trace warnings
- Added new `AdminAiTraceResponse` DTO for AI trace data structure
2026-05-24 20:55:14 +02:00
Nils-Johan Gynther 7713eb2fa7 ci(deploy): standardize docker compose env file usage across scripts
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m33s
Test Suite / flutter-quality (push) Failing after 1m16s
- Added --env-file .env to all docker compose commands for consistent environment variable loading
- Added MISTRAL_API_KEY validation in deploy.sh with fatal error if missing
- Added --force-recreate flag to recipe-api restarts in disable/enable receipt trace scripts
- Added .env file existence check in rebuild_flutter.sh
- Added API service recreation logic in deploy.sh when backend/importer services are updated
2026-05-24 20:30:28 +02:00
Nils-Johan Gynther 26c217e0eb chore(backend): address deprecated dependencies in test stack
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 6m16s
Test Suite / flutter-quality (push) Failing after 1m15s
- Updated jest ecosystem to v30.x in backend/package.json
- Added controlled overrides for test-exclude to remove inflight@1.0.6 and glob@7.2.3
- Updated README.md with deprecation handling procedures and debugging steps
- Modified .kilo/plans/1779643125894-lucky-island.md to document systematic approach for dependency management
2026-05-24 19:47:24 +02:00
Nils-Johan Gynther e6e9e11b18 chore(deps): enforce npm package release age policy
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m40s
Test Suite / flutter-quality (push) Failing after 1m9s
- Added `min-release-age=1` to backend/.npmrc to require packages to be at least 1 day old
- GitHub Actions backend jobs already use `npm ci` in backend/ directory, automatically enforcing the policy
- Updated README.md with supply-chain protection policy and operational procedures

BREAKING CHANGE: npm install/npm ci will now fail for packages younger than 1 day unless policy is temporarily overridden
2026-05-24 19:40:48 +02:00
Nils-Johan Gynther b04d157915 feat(flyer-import): add detailed product signals and display names
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 5m12s
Test Suite / flutter-quality (push) Failing after 2m8s
- Added `signals` and `displayNameDetailed` fields to FlyerItem model in Prisma schema
- Introduced `FlyerImportSignals` type with origin countries, labels, quality flags, variant, and packaging
- Added `displayNameDetailed` field to FlyerImportItem DTO and Flutter model
- Implemented utility functions for signal extraction and display name building
- Updated flyer import service to persist and return signals/category data
- Enhanced Flutter UI to display detailed product information including badges for signals
- Added new test coverage for signals persistence and display name generation
- Added new import-common module for shared import utilities
- Created database migration for new fields
- Added Kilo plan for feature development
2026-05-24 19:32:13 +02:00
Nils-Johan Gynther d9f992ca9a feat(ai): enhance AI trace warnings and reason codes system
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 4m21s
Test Suite / flutter-quality (push) Failing after 1m38s
- Added structured warning system with `AdminAiWarning` type in backend and Flutter
- Implemented detailed reason descriptors with `FlyerReasonDescriptor` for parse and match operations
- Added `legacyWarnings` field to maintain backward compatibility
- Enhanced AI trace service to collect and format warnings with item-level context
- Updated flyer import services to include detailed reason descriptions in responses
- Added Swedish diacritic preservation for cheese variants (Prästost, Herrgårdsost, Grevéost)
- Implemented UTF-8 content validation for AI responses
- Added new reason code definitions in `reason-codes.ts`
- Updated Flutter UI to display structured warnings with severity indicators
- Added error report generation and copy functionality in admin panel
- Added comprehensive test coverage for new warning system and cheese normalization

BREAKING CHANGE: AI trace warnings are now structured objects instead of simple strings
2026-05-23 21:11:46 +02:00
Nils-Johan Gynther 0fb507f247 ci(caddy): update Content-Security-Policy for Google Fonts and scripts
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m26s
Test Suite / flutter-quality (push) Failing after 1m32s
Updated the Content-Security-Policy header to include Google Fonts and Google Analytics domains:
- Added `https://www.gstatic.com` to `script-src`, `script-src-elem`, `img-src`, and `font-src` directives
- Removed duplicate `script-src` and `style-src` entries in the policy
- Ensured all relevant directives properly include the new domains
2026-05-23 20:14:04 +02:00
Nils-Johan Gynther a240bce8fc chore(backend): update test execution strategy in Dockerfile
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m10s
Test Suite / flutter-quality (push) Failing after 1m22s
Update Jest test runner configuration to use `--runInBand` flag in Docker build process for more reliable test execution in containerized environment.
2026-05-23 19:50:12 +02:00
Nils-Johan Gynther 69bcc3e342 feat(web): improve web build configuration and accessibility
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 14m6s
Test Suite / flutter-quality (push) Failing after 4m44s
- Add source maps and web renderer build arguments with defaults
- Configure Caddy with CSP headers, cache policies, and service worker handling
- Defer loading of import screen for performance optimization
- Add semantic labels to icons for accessibility
- Update web index.html with Swedish language, meta tags, and description
- Add robots.txt and lighthouse configuration
- Add new planning documents and archive entries
2026-05-23 18:04:27 +02:00
Nils-Johan Gynther 30d27d6b8a feat(localization): update English and Swedish translations for admin features and profile actions
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 4m10s
Test Suite / flutter-quality (push) Failing after 1m19s
2026-05-21 22:46:33 +02:00
Nils-Johan Gynther 9dd49c5014 bug-fix
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 4m7s
Test Suite / flutter-quality (push) Failing after 57s
2026-05-21 22:36:28 +02:00
Nils-Johan Gynther 8c9da36312 feat(profile): implement user-initiated GDPR-compliant profile deletion
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 4m36s
Test Suite / flutter-quality (push) Failing after 40s
- Add DELETE /users/me endpoint with cascading data removal
- Implement frontend confirmation dialog and deletion flow
- Add audit logging for deletion requests
- Update localization files for new UI strings
- Add scheduled cleanup service for AI traces
- Document GDPR compliance in technical specification

BREAKING CHANGE: Users can now permanently delete their profiles and associated data
2026-05-21 22:19:50 +02:00
Nils-Johan Gynther 6ddb58dc7c docs: update release checklist for AI trace and flyer import
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m19s
Test Suite / flutter-quality (push) Failing after 58s
- Replace outdated security and data protection section with current requirements
- Add new checklist items for authentication migration to httpOnly cookies
- Include AI trace retention policy and penetration testing requirements
- Update target audience and document status to 2026-05-21
- Add E2E testing requirements for flyer and receipt imports
- Document GDPR compliance processes
2026-05-21 19:26:22 +02:00
Nils-Johan Gynther e079758f1d docs(release): update documentation for AI trace and flyer import improvements
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m49s
Test Suite / flutter-quality (push) Failing after 57s
- Update NEXT_STEPS.md with completed AI trace persistence and admin panel enhancements
- Add new section for executed steps (2026-05-21) documenting AI trace integration
- Update README.md with new AI trace features and improvements
- Enhance TEKNISK_BESKRIVNING.md with detailed technical description of AI trace implementation
- Document backend flow, persistence, masking, and UI improvements
- Add next steps including retention policy and API filtering for AiTrace
2026-05-21 19:16:06 +02:00
Nils-Johan Gynther 026323b72a refactor(ai): enhance AI trace integration and OCR normalization
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m54s
Test Suite / flutter-quality (push) Failing after 1m29s
- Add FlyerTraceSupplement type for AI trace metadata
- Implement getFlyerTraceSupplements method to fetch trace supplements
- Update AiTraceService to include prompt/rawOutput and counters in flyer traces
- Add persistFlyerTrace method to FlyerImportService for trace persistence
- Enhance AiFlyerParserService to return structured trace data with prompts and retries
- Update FlyerNormalizerService with OCR typo fixes for cheese variants and spröd bakad firre
- Improve Flutter admin panel with selectable text, warnings display, and tooltips
- Add comprehensive tests for AI trace supplements and normalization rules
2026-05-21 19:11:54 +02:00
Nils-Johan Gynther 67a7590525 feat(ai): add AI trace tracking and admin panel
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 12m45s
Test Suite / flutter-quality (push) Failing after 7m24s
- Add AiTrace model to Prisma schema with relations to User
- Implement AiTraceService with CRUD operations for AI traces
- Add new admin panel for AI traces with filtering and detail views
- Integrate trace persistence in receipt import flow
- Add API endpoints for listing and retrieving AI traces
- Update Flutter admin UI with new AI tab and navigation
- Add new domain models for AI traces and details
- Add migration for AiTrace table creation

BREAKING CHANGE: None
2026-05-21 17:33:21 +02:00
Nils-Johan Gynther c3520b5ad4 test(products): increase test timeout for category update
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m0s
Test Suite / flutter-quality (push) Failing after 1m23s
- Set Jest timeout to 15 seconds for products.update-category.http.spec.ts
- Ensures reliable execution of category update integration tests

BREAKING CHANGE: None
2026-05-21 15:03:44 +02:00
Nils-Johan Gynther 2d94a83e73 feat(flyer-import): add cheese variant splitting and normalization rules
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 5m48s
Test Suite / flutter-quality (push) Failing after 1m9s
- Add logic to split Swedish cheese variants (Präst, Herrgård, Grevé) into separate products
- Implement brand normalization for "Arla Ko" and category assignment to "Hårdost"
- Update flyer parser with detailed rules for bundle/group announcements
- Add unit tests for variant splitting and brand/category normalization
- Replace single-item return with flatMap for expanded product lists
2026-05-21 14:44:37 +02:00
Nils-Johan Gynther 47c89c9915 docs: update deployment workflow documentation
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m49s
Test Suite / flutter-quality (push) Failing after 1m14s
- Replace references to removed `--migrate` flag with new migration behavior
- Document automatic migration execution during `--clean-database` deployments
- Add details about Prisma Client generation output visibility in deploy logs
- Correct maintenance SQL file usage and table references
2026-05-21 14:30:28 +02:00
Nils-Johan Gynther f9bf3156eb chore(deploy): refactor database cleanup and migration workflow in deploy.sh
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m29s
Test Suite / flutter-quality (push) Failing after 1m23s
- Remove deprecated --migrate flag and related RUN_MIGRATE logic
- Simplify database cleanup workflow to always run migrations before cleaning
- Add run_prisma_generate() function to update Prisma Client after migrations
- Update documentation to reflect new workflow where --clean-database implies migration
- Remove conditional migration logic that could cause double execution
2026-05-21 14:29:10 +02:00
Nils-Johan Gynther 0ebb39150f ci(deploy): add migration control and deployment improvements
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m11s
Test Suite / flutter-quality (push) Failing after 1m20s
- Add SKIP_MIGRATION environment variable to control automatic Prisma migrations in backend
- Update Dockerfile to conditionally run migrations based on SKIP_MIGRATION flag
- Enhance deploy.sh with:
  - Better error handling using set -euo pipefail
  - New --skip-migration flag to bypass automatic migration startup
  - Improved documentation and help text
  - New helper functions for waiting on services and database
  - Better status reporting for migration results
- Update compose.yml to include SKIP_MIGRATION environment variable
2026-05-21 14:13:54 +02:00
Nils-Johan Gynther 67c3170067 feat(flyer-import): add bundle support and new product fields
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m43s
Test Suite / flutter-quality (push) Failing after 1m51s
- Add bundle support with isBundle, bundleWeight, and bundleItems fields
- Add brand, weight, and comparisonUnit fields to FlyerItem model
- Update AI flyer parser to extract bundle information
- Add sanitization for bundle items in FlyerNormalizerService
- Update DTOs and interfaces to include new fields
- Add migration for new database fields
- Update tests to cover bundle item handling
2026-05-21 13:26:50 +02:00
Nils-Johan Gynther 7bbb5a63b5 changed model to: ministral-8b-2512
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m47s
Test Suite / flutter-quality (push) Failing after 1m27s
2026-05-21 09:56:10 +02:00
Nils-Johan Gynther 505339aa33 Changed Mistral LLM to mistral-8b-2512. Changed timeout for LMM in .env
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m4s
Test Suite / flutter-quality (push) Failing after 1m24s
2026-05-21 09:45:39 +02:00
Nils-Johan Gynther 740e8e5897 chore(docs): update project documentation with deployment and maintenance changes
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m33s
Test Suite / flutter-quality (push) Failing after 1m24s
Updated three documentation files to reflect recent deployment and maintenance improvements:

- NEXT_STEPS.md: Added completed steps for 2026-05-20 including deploy script updates, Prisma integration, standardized database cleaning, and production fixes
- README.md: Added new deployment flags and maintenance file information for 2026-05-20 release
- TEKNISK_BESKRIVNING.md: Added technical details about new deploy flow, database cleaning process, and production hotfix resolution
2026-05-20 21:34:08 +02:00
Nils-Johan Gynther e491a6c67f docs: add new documentation files for project planning and development
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m32s
Test Suite / flutter-quality (push) Failing after 1m12s
Added two new documentation files:
- .kilo/plans/1779256422838-glowing-knight.md: Contains development plan for the "Glowing Knight" feature
- plan-dokumentation.md: Provides overview of project planning structure and documentation standards
2026-05-20 21:28:10 +02:00
Nils-Johan Gynther ff179430aa fix(db): update clean-database.sql with correct table names and structure
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m57s
Test Suite / flutter-quality (push) Failing after 1m24s
2026-05-20 21:27:45 +02:00
Nils-Johan Gynther 6c38101e5c chore(deploy): add database maintenance and migration options
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 7m44s
Test Suite / flutter-quality (push) Failing after 8m15s
Added new deployment options to deploy.sh:
- --migrate: Runs Prisma migration deploy command
- --clean-database: Executes maintenance SQL to clean data while preserving categories

Added new maintenance directory backend/prisma/maintenance/ containing:
- clean-database.sql: SQL script for database cleaning operations

Updated deployment script to:
- Include new command-line flags in help text
- Add conditional blocks for running migrations and database cleaning
- Implement container readiness checks before running Prisma commands
- Preserve existing seed functionality while adding new maintenance features
2026-05-20 20:58:36 +02:00
Nils-Johan Gynther a1a2c33427 feat(shopping-list): add shopping list feature with flyer integration
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 5m8s
Test Suite / flutter-quality (push) Failing after 1m41s
This commit introduces a comprehensive shopping list feature with the following key changes:

Backend:
- Added ShoppingListItem model with relations to User, Product, and Category
- Added new fields to FlyerSession for source file metadata
- Added categoryId field to FlyerItem model
- Implemented session source file retrieval endpoint
- Added endpoint for updating flyer session items with category assignment
- Added endpoint for planning flyer selections to shopping list
- Implemented backfillCategoriesMine for AI-assisted category assignment
- Added ShoppingListModule and integrated with FlyerSelectionModule

Frontend:
- Added ShoppingListScreen and navigation route
- Implemented API paths and client methods for shopping list operations
- Added category tree loading for shopping list item creation
- Integrated shopping list functionality in flyer import tab
- Added category backfill trigger in inventory screen
- Updated FlyerImportItem model with categoryId support
- Added methods for updating flyer session items and retrieving source files

Database:
- Added new Prisma migration for flyer source metadata and shopping list items
- Updated schema with new relations and indexes

The shopping list feature allows users to:
1. Plan flyer selections directly to their shopping list
2. View and manage their shopping list items
3. Update flyer session items with proper categorization
4. Retrieve original flyer source files
5. Automatically backfill categories for uncategorized products
2026-05-20 09:07:30 +02:00
Nils-Johan Gynther 996f0d774b Updatede documentation
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m35s
Test Suite / flutter-quality (push) Failing after 1m29s
2026-05-19 22:01:44 +02:00
Nils-Johan Gynther 6cd5b80adb feat(flyer-import): add session management and retrieval endpoints
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m15s
Test Suite / flutter-quality (push) Failing after 1m25s
- Add new API endpoints for retrieving flyer import sessions:
  - GET /flyer-import/sessions/latest - Retrieve latest session for user
  - GET /flyer-import/sessions/:sessionId - Retrieve specific session
- Implement session persistence and restoration in Flutter UI
- Add toJson() methods to FlyerImportItem and FlyerImportResult for serialization
- Add new FlyerImportSession domain model for local session management
- Add unit test file for FlyerImportService
- Update FlyerImportController with new endpoints and user ID extraction
- Update FlyerImportService with session retrieval logic and response mapping
- Update API paths in Flutter client
- Add session restoration on widget init in FlyerImportTab
2026-05-19 21:55:55 +02:00
Nils-Johan Gynther 8b8f8b7b6f Update flyerimport. flutter timeout 300 sek
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 4m58s
Test Suite / flutter-quality (push) Failing after 1m41s
2026-05-19 20:53:39 +02:00
Nils-Johan Gynther 33190bd8e0 refactor(ai-parser): implement chunk-based text processing for flyer parsing
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m28s
Test Suite / flutter-quality (push) Failing after 1m14s
- Replace fixed text window retry strategy with dynamic chunk-based approach
- Add configurable chunk size, overlap, and maximum chunks via environment variables
- Implement chunk splitting with overlap handling for context preservation
- Add chunk processing with retry logic per chunk
- Include deduplication of items across chunks
- Update logging to reflect chunk-based processing

BREAKING CHANGE: Changes the retry strategy from fixed text windows to dynamic chunk-based processing. Existing configurations may need adjustment for FLYER_AI_CHUNK_SIZE_CHARS, FLYER_AI_CHUNK_OVERLAP_CHARS, and FLYER_AI_MAX_CHUNKS.
2026-05-19 20:24:03 +02:00
Nils-Johan Gynther 4d2942a8e5 chore(infra): add AI flyer parsing configuration and retry logic
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m9s
Test Suite / flutter-quality (push) Failing after 1m19s
- Add FLYER_AI_TIMEOUT_MS and FLYER_AI_RETRIES environment variables
- Configure timeout and retry settings in compose.yml
- Update AiFlyerParserService with configurable timeout and retry logic
- Add text window reduction strategy for retry attempts
- Update documentation in TEKNISK_BESKRIVNING.md
- Fix ESLint configuration in app.security.spec.ts
2026-05-19 20:13:59 +02:00
Nils-Johan Gynther 187d0283a5 feat(flyer-import): integrate AI-based flyer parsing with image support
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m31s
Test Suite / flutter-quality (push) Failing after 3m48s
Test Suite / backend-pr-quick (push) Failing after 13m57s
- Add support for PNG, JPEG, and WebP image formats in flyer import
- Replace external importer service with internal AI-based parsing pipeline
- Add new services: TextExtractorService, AiFlyerParserService, FlyerNormalizerService
- Integrate Mistral AI, pdf-parse, and tesseract.js dependencies
- Add quality confidence indicators and warning panels in Flutter UI
- Update package.json with new dependencies and transform ignore patterns
- Add documentation for flyer importer system
- Add Kilo AI planning file for Happy Island project

BREAKING CHANGE: Flyer import now uses internal AI parsing instead of external importer service
2026-05-19 19:57:54 +02:00
Nils-Johan Gynther 0ce1db5471 chore: update flyer import features and resources
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m43s
Test Suite / flutter-quality (push) Failing after 1m33s
- Remove outdated Willys flyer PDF (0001-0008_WIL_V21_ED1pdf.pdf)
- Add new Willys flyer PDF (willys_reklamblad.pdf)
- Improve offer detection logic in backend flyer-import service
- Add offer limit text extraction and sanitization in Flutter UI
- Fix Swedish character encoding issues in UI text
2026-05-18 23:40:05 +02:00
Nils-Johan Gynther c720f611ea Merge branch 'main' of ssh://gitea.gynther.se:2222/nilsjohan/recipe-app
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m36s
Test Suite / flutter-quality (push) Failing after 1m19s
2026-05-18 23:27:31 +02:00
Nils-Johan Gynther e658f2e6f1 chore(ci): update project documentation and flyer import features
Update project documentation with recent CI improvements and flyer import enhancements:

- Add ESLint configuration for backend and Dart lints for Flutter
- Document Prisma query logging via PRISMA_LOG_QUERIES environment variable
- Update NEXT_STEPS.md, README.md, and TEKNISK_BESKRIVNING.md with new features
- Add isOffer, offerLimitText, comparisonPrice, comparisonUnit, parseConfidence, and parseReasons fields to FlyerImportItem
- Update FlyerImportResponse type to include new fields
- Extend file picker to support image formats (png, jpg, jpeg, webp)
- Add offer badge display and price formatting in Flutter UI
- Implement PDF preview functionality for flyer import
2026-05-18 23:27:20 +02:00
nilsjohan a5cd49284a willys-reklamblad
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m11s
Test Suite / flutter-quality (push) Failing after 1m13s
Exempel på reklamblad från willys
2026-05-18 23:26:00 +02:00
Nils-Johan Gynther 3f242f9a6d docs: add Prisma query logging configuration guide
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m30s
Test Suite / flutter-quality (push) Failing after 1m17s
Add documentation for enabling Prisma query logging in test environments via PRISMA_LOG_QUERIES environment variable.

Include:
- Step-by-step instructions for configuration in Docker Compose
- Environment setup requirements
- Performance and security considerations
- Warning against production use
2026-05-18 23:08:29 +02:00
Nils-Johan Gynther f6ccdd859f ci(github): add linting and improve CI workflow
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 5m14s
Test Suite / flutter-quality (push) Failing after 1m36s
- Add ESLint configuration for backend TypeScript code
- Include linting step in backend quality checks
- Add linting step to GitHub Actions CI workflow
- Enable configurable Prisma query logging via PRISMA_LOG_QUERIES environment variable
- Update PrismaService to support dynamic log levels based on PRISMA_LOG_QUERIES
- Replace BadRequestException with UnauthorizedException in receipt import security tests
2026-05-18 23:01:29 +02:00
Nils-Johan Gynther d5f903db98 chore(import): improve error handling and add flyer integration
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 3m41s
Test Suite / flutter-quality (push) Successful in 2m3s
- Replace BadRequestException with UnauthorizedException for authentication failures in flyer-import and flyer-selection controllers
- Add bulk selection endpoint in FlyerSelectionController for creating multiple selections in one request
- Update FlyerSelectionModule to include new FlyerSelectionMatcherService and FlyerSelectionSyncController
- Extend FlyerSelectionService with createMany method for bulk operations
- Add new DTOs for bulk selection and receipt matching functionality
- Update ReceiptImportService to accept FlyerSelectionService dependency and track successful rows
- Extend SaveReceiptResponse with flyerAutoSync field for receipt-to-flyer matching results
- Add new API paths for flyer import and selection endpoints
- Update Flutter UI to include Flyer import tab and adjust tab controller length
- Add new domain models and repository methods for flyer import functionality
- Update test files to include new FlyerSelectionService dependency
- Modify .kilo plan documentation to reflect current system architecture
2026-05-18 22:51:27 +02:00
Nils-Johan Gynther 24a96c3da1 feat(flyer): add flyer session and selection system
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m49s
Test Suite / flutter-quality (push) Successful in 2m0s
- Add FlyerSession, FlyerItem, and FlyerSelection models to Prisma schema
- Implement session persistence with weekly key generation in FlyerImportService
- Add FlyerSelectionModule to AppModule
- Extend FlyerImportResponse with sessionId and flyerItemId fields
- Create new flyer-selection module directory structure
- Add migration for flyer session and selection tables

BREAKING CHANGE: Flyer import now persists data to FlyerSession and FlyerItem tables
2026-05-18 19:02:32 +02:00
Nils-Johan Gynther a31aff7c35 test: update admin aliases panel tests for product model changes
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m28s
Test Suite / flutter-quality (push) Successful in 2m21s
- Add imports for new domain models
- Update AdminProduct initialization to include canonicalName
- Update ReceiptAlias initialization to include ownerId and productName
- Replace Switch widget checks with SwitchListTile for global alias test
- Update test data to match new model structure
2026-05-18 18:48:26 +02:00
Nils-Johan Gynther f42132ed5b chore: add flyer import module and configuration
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 3m57s
Test Suite / flutter-quality (push) Failing after 1m19s
- Added FlyerImportModule to AppModule imports
- Created new flyer-import module directory
- Added .kilo/ configuration directory
2026-05-18 18:40:25 +02:00
Nils-Johan Gynther e6961fc593 created kilo.json
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m33s
Test Suite / flutter-quality (push) Failing after 1m22s
2026-05-16 09:24:29 +02:00
Nils-Johan Gynther 50816a6844 feat: add central help text system, Prisma migration, and Flutter help button
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m2s
Test Suite / flutter-quality (push) Successful in 1m40s
2026-05-13 17:04:44 +02:00
Nils-Johan Gynther bb7a4c1ff2 feat: add Copilot instructions for database command style and credential handling
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m2s
Test Suite / flutter-quality (push) Successful in 2m46s
2026-05-13 16:37:35 +02:00
Nils-Johan Gynther 3d9b124766 feat: add HelpText model, service, and controller for dynamic help text management
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 2m27s
Test Suite / flutter-quality (push) Successful in 1m47s
2026-05-13 16:20:04 +02:00
Nils-Johan Gynther 0da4bbf4cf feat: implement alias-scope management for admin, enhance category chip handling, and upgrade dependencies
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 1m40s
Test Suite / flutter-quality (push) Successful in 1m44s
2026-05-12 22:24:26 +02:00
Nils-Johan Gynther 4492d7aa1c feat: enhance receipt alias management with global scope support and update validation
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 50s
Test Suite / flutter-quality (push) Successful in 50s
2026-05-12 22:20:48 +02:00
Nils-Johan Gynther 4471e344eb chore(deps): upgrade NestJS and related dependencies to version 11.x
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 32s
Test Suite / flutter-quality (push) Successful in 51s
fix(auth): ensure JWT_SECRET is set in JwtStrategy constructor
2026-05-12 22:00:29 +02:00
Nils-Johan Gynther 621ced0e43 refactor: streamline alias editing and improve category path handling in admin panel
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 21s
Test Suite / flutter-quality (push) Successful in 57s
2026-05-12 21:53:19 +02:00
Nils-Johan Gynther a4d16cdbae feat: add workspace checkout step to CI workflow for improved reliability
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 21s
Test Suite / flutter-quality (push) Successful in 53s
2026-05-12 21:38:28 +02:00
Nils-Johan Gynther 6ff58ed013 refactor: simplify CI workflow by removing Node.js setup steps and verifying toolchain
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 1s
Test Suite / flutter-quality (push) Failing after 1s
2026-05-12 21:30:39 +02:00
Nils-Johan Gynther 37f931fa0b refactor: streamline Flutter setup in CI workflow by removing unnecessary steps
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 22s
Test Suite / flutter-quality (push) Failing after 1s
2026-05-12 21:28:34 +02:00
Nils-Johan Gynther 8184fbd7e9 refactor: simplify Node.js setup in CI workflows by removing matrix strategy
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 33s
Test Suite / flutter-quality (push) Failing after 5s
2026-05-12 21:26:21 +02:00
Nils-Johan Gynther 46b9be4791 feat: implement update functionality for receipt aliases and add corresponding tests
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / quick-import-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 22s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 21:25:48 +02:00
Nils-Johan Gynther fb6b371fb7 feat: enhance error dialogs with delete functionality and improve documentation
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / quick-import-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 26s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 21:11:54 +02:00
Nils-Johan Gynther 0784c1a032 feat: add tests for QuickImportService and ReceiptImportService parse flow
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / quick-import-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 27s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 20:56:13 +02:00
Nils-Johan Gynther 44ea3cdd7e feat: add unit tests for ReceiptImportService.saveReceipt method
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 21s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 20:01:30 +02:00
Nils-Johan Gynther 320a646950 feat: add microservice folders to workspace configuration
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 21s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 19:54:03 +02:00
Nils-Johan Gynther acb1e56506 Merge branch 'main' of ssh://gitea.gynther.se:2222/nilsjohan/recipe-app
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 22s
Test Suite / flutter-quality (push) Failing after 4s
2026-05-12 19:18:18 +02:00
Nils-Johan Gynther 2a230a6994 feat: add GitHub Actions workflow for testing backend and Flutter components 2026-05-12 19:18:10 +02:00
Nils-Johan Gynther 43c91881a7 feat: add GitHub Actions workflow for testing backend and Flutter components
Test Suite / backend-pr-quick (24.15.0) (push) Has been skipped
Test Suite / backend-full (24.15.0) (push) Failing after 1m38s
Test Suite / flutter-quality (push) Failing after 1m37s
2026-05-12 18:39:16 +02:00
Nils-Johan Gynther 98ee8a3ad6 feat: implement real-time database synchronization with SSE and update backend modules
Test Suite / backend-pr-quick (24.15.0) (push) Has been cancelled
Test Suite / backend-full (24.15.0) (push) Has been cancelled
Test Suite / flutter-quality (push) Has been cancelled
2026-05-12 16:57:05 +02:00
Nils-Johan Gynther 2dda34d4d2 feat: update project analysis documentation to include fullstack context and backend-specific checks for NestJS and Prisma
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / flutter-quality (push) Has been cancelled
2026-05-12 16:17:11 +02:00
Nils-Johan Gynther d135aa643a feat: enhance pre-commit quality gate with detailed analysis and output format
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / flutter-quality (push) Has been cancelled
2026-05-12 16:15:37 +02:00
Nils-Johan Gynther d645d3ad9d feat: add Flutter quality checks and tests for category chips in inventory and pantry screens
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / flutter-quality (push) Has been cancelled
2026-05-12 16:13:10 +02:00
Nils-Johan Gynther 08d14bf9e6 feat: remove obsolete cache tracking file from build directory 2026-05-12 16:12:51 +02:00
Nils-Johan Gynther acca5dcbae feat: add session checkpoint for category chip enhancements and cache invalidation
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 22:12:04 +02:00
Nils-Johan Gynther cd84e25890 feat: enhance category handling with new category chip logic and user-specific product categorization
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 22:03:09 +02:00
Nils-Johan Gynther f19c157e8f feat: add updateCategoryMine endpoint to manage product category updates
Test Suite / test (24.15.0) (push) Has been cancelled
- Implemented a new PATCH endpoint in ProductsController to update the category of a user's product.
- Added corresponding service method in ProductsService to handle business logic and validation.
- Created UpdateCategoryMineDto for request validation.
- Enhanced error handling for forbidden actions and not found resources.
- Updated API error mapping in Flutter to handle specific forbidden messages.
- Modified ProductPickerField to allow product creation directly from the picker.
- Added tests for the new endpoint and service method to ensure proper functionality and error handling.
2026-05-11 21:41:42 +02:00
Nils-Johan Gynther 8e0166c68a feat: Improve category ID handling with dynamic parsing in inventory screens
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 21:18:06 +02:00
Nils-Johan Gynther 3e0af925d5 feat: Refactor inventory screens with category selection and product handling improvements
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 21:09:40 +02:00
Nils-Johan Gynther 2281df3716 feat: Add location display and category chips in pantry and inventory screens
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 20:53:11 +02:00
Nils-Johan Gynther 68476142c1 refactor: Remove PantryProduct class and simplify category resolution in PantryScreen
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 20:01:00 +02:00
Nils-Johan Gynther a635f1002a feat: Enhance inventory screens with category selection and product loading improvements
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 19:40:02 +02:00
Nils-Johan Gynther d05b7da8bc feat: Add isPrivate field to AdminProduct and update filtering logic in admin panels
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 19:30:42 +02:00
Nils-Johan Gynther a4f65c6065 feat: Implement caching for selectable products and enhance product filtering in admin panels
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 18:42:35 +02:00
Nils-Johan Gynther d75fd00666 feat: Update documentation with recent changes, including Flutter build fixes and deployment guidelines
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 17:21:17 +02:00
Nils-Johan Gynther 9b4d1f94bf Refactor category handling in inventory and pantry forms
Test Suite / test (24.15.0) (push) Has been cancelled
- Introduced `_categorySearchController` and `_categoryOptions` in both `_InventoryFormDialogState` and `_PantryFormDialogState` to manage category selection more effectively.
- Implemented `_flattenCategoryOptions` method to create a flat list of category options from nested category nodes.
- Updated the initialization and disposal of the new controllers to ensure proper resource management.
- Made minor adjustments to the PopupMenuItem definitions in `AppShell` for consistency.
2026-05-11 17:03:58 +02:00
Nils-Johan Gynther d038d30831 feat: Update MVP checklist with completed security and regression testing tasks
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 16:40:49 +02:00
Nils-Johan Gynther 1db30c9b6f test(security): add and refactor api security/idor coverage
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 16:40:16 +02:00
Nils-Johan Gynther 9b468d9a13 feat: Update documentation with new features and improvements, including inventory category management and UI changes
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 12:55:25 +02:00
Nils-Johan Gynther 3ad6cfee50 feat: Introduce SearchableCategoryField component and integrate it into admin panels for enhanced category selection
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 12:20:57 +02:00
Nils-Johan Gynther f132983b75 feat: Implement admin pantry item management with create and update functionality, including category selection and validation
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 11:18:13 +02:00
Nils-Johan Gynther 573c12cdc3 feat: Add _getMap and _postMap methods for improved API handling and refactor resetPassword to use _postMap
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 10:40:54 +02:00
Nils-Johan Gynther c63afad672 feat: Enhance category suggestion logic with improved matching rules and disambiguation guidelines
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 10:35:30 +02:00
Nils-Johan Gynther 56050a896b feat: Enhance apple categorization logic and improve bulk category update feedback
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 10:31:57 +02:00
Nils-Johan Gynther 06056c6182 feat: Improve bulk category update functionality with validation and clearer logic
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 10:22:17 +02:00
Nils-Johan Gynther afbc5b91b2 feat: Enhance admin panel navigation and UI by implementing tab management and improving layout structure
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 10:10:03 +02:00
Nils-Johan Gynther 06492ff099 feat: Refactor admin database panel to improve tab management and enhance UI structure with dedicated tab configuration
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 09:55:52 +02:00
Nils-Johan Gynther 1d2c3c9032 feat: Refactor inventory screen to improve type safety and enhance UI structure with dedicated widget methods
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 09:40:42 +02:00
Nils-Johan Gynther d4a7983afb feat: Add bulk delete and merge functionality for inventory items with DTOs and API endpoints
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 09:36:15 +02:00
Nils-Johan Gynther 8e6e0e96b8 feat: Enhance admin user management with search, filtering, and sorting capabilities
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-11 09:22:19 +02:00
Nils-Johan Gynther 84ccabe2fe feat: Add functionality to move inventory items to pantry and enhance pantry management
Test Suite / test (24.15.0) (push) Has been cancelled
- Implemented moveInventoryItemToPantry method in InventoryRepository to facilitate moving items from inventory to pantry.
- Enhanced InventoryScreen with a new header section providing context about the inventory.
- Added a button in SwipeableInventoryTile to move items to pantry with appropriate error handling.
- Introduced movePantryItemToInventory method in PantryRepository to support moving items back to inventory.
- Refactored PantryScreen to rename _addToInventory to _moveToInventory for clarity and updated UI to reflect changes.
- Added AdminPantryItem model to represent pantry items in the admin panel.
- Created AdminPantryPanel for managing pantry items, including moving items to inventory and listing users.
- Developed AdminPrivateProductsPanel for managing private products, allowing promotion to global products.
2026-05-11 09:06:30 +02:00
Nils-Johan Gynther edf9c74e75 feat: update MVP plan and close previous inventory IDOR risk, ensuring user-scope implementation and focus on regression testing
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-10 00:35:23 +02:00
Nils-Johan Gynther ca8987d9e4 Add comprehensive documentation for Flutter frontend migration and backend review
Test Suite / test (24.15.0) (push) Has been cancelled
- Introduced user guide for Flutter frontend in README.md, detailing user flows and recent improvements.
- Created next steps roadmap for Flutter migration in next_steps_flutter.md, outlining current tasks and priorities.
- Developed technical description for Flutter frontend in teknisk_beskrivning_flutter.md, covering architecture and security status.
- Removed outdated migration documentation for Prisma P3009 and added recovery steps for failed migrations in migrering-MSI.md.
- Established a release checklist for product launches in produktlansering.md, ensuring security and stability measures are met.
- Formulated a systematic backend review and optimization plan in review_backend.md, focusing on reducing complexity and improving performance.
2026-05-10 00:28:59 +02:00
Nils-Johan Gynther 1709bb1dad Implement admin inventory management features including CRUD operations, merging, filtering, sorting, previewing, and security enhancements. Update documentation and add comprehensive test coverage for security and validation.
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-10 00:20:25 +02:00
Nils-Johan Gynther 65137b41fb feat: implement alias strategy for receipt import with user-scoped and global fallback, enhance validation and normalization, and update UI components
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:41:42 +02:00
Nils-Johan Gynther b342de906e feat: implement alias strategy for receipt import with user-scoped and global fallback aliases
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:31:17 +02:00
Nils-Johan Gynther 7aa93ff5d0 refactor: streamline error handling by removing redundant SnackBar calls in AdminProductsPanel
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:26:56 +02:00
Nils-Johan Gynther ae6d7aad1a feat: implement product rename and merge endpoints for users and admins, optimize receipt import UI, and enhance deploy script
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:20:52 +02:00
Nils-Johan Gynther 14a1107466 feat: add private product management endpoints for updating canonical names and merging products
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:19:28 +02:00
Nils-Johan Gynther 8e276a34fe feat: enhance deploy script with new flag for checking base image updates and improve usage instructions
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:09:36 +02:00
Nils-Johan Gynther da193b26ef feat: enhance deploy script with improved flag handling and usage instructions
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 23:05:02 +02:00
Nils-Johan Gynther 9f3f5c1cef feat: add canonical name endpoint and update product renaming functionality in admin panel
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 22:58:23 +02:00
Nils-Johan Gynther 3ff27701fc feat: document receipt import cleanup and optimization session with detailed changes and verification steps
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 16:41:25 +02:00
Nils-Johan Gynther d2f651fa2c feat: enhance error handling in prepareMatchingContext with logging for category loading issues
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 16:07:22 +02:00
Nils-Johan Gynther b121000bf7 feat: enhance receipt import matching logic to include category path resolution and improve edit handling
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 16:06:19 +02:00
Nils-Johan Gynther b09ea28ff0 feat: remove deprecated refreshCategories endpoint and refactor matching logic for improved clarity and performance
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:38:08 +02:00
Nils-Johan Gynther 4d5c55f459 feat: deprecate matchProducts and enrichWithAiCategories methods, update categorization logic with unified matcher
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:27:24 +02:00
Nils-Johan Gynther 000a28bea4 feat: finalize category seeding process and remove obsolete product categorization logic
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:20:16 +02:00
Nils-Johan Gynther 97e7b09bcd feat: simplify receipt import matching logic and enhance trace logging
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:17:00 +02:00
Nils-Johan Gynther 1966a92a87 feat: implement unified matching and categorization for receipt items with detailed debug information
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:11:06 +02:00
Nils-Johan Gynther 8354abbc8f feat: implement save receipt functionality with transaction handling and DTOs
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-09 15:04:23 +02:00
Nils-Johan Gynther 853e853e5e feat: enhance ingredient matching rules with keyword matching and add chocolate detection
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 17:36:06 +02:00
Nils-Johan Gynther bd78b1de81 feat: add "See receipt" button and preview modal in receipt import flow
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 16:56:03 +02:00
Nils-Johan Gynther e3bbd7d99e feat: replace ListView with SingleChildScrollView for better layout in admin panels
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 16:31:09 +02:00
Nils-Johan Gynther a5273158e2 fix: correct type casting in onChanged callback in ProductPickerField
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 15:33:20 +02:00
Nils-Johan Gynther fc366547c7 feat: clean up imports and fix minor formatting issues in various files
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 15:31:30 +02:00
Nils-Johan Gynther 84dbe8490d feat: remove unused methods and improve widget styling in various screens
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 15:28:54 +02:00
Nils-Johan Gynther 5019bf6acf feat: add import for ProductOption in receipt_import_tab
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 14:33:58 +02:00
Nils-Johan Gynther 148fee8859 feat: update service worker version in flutter_bootstrap.js
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 14:25:00 +02:00
Nils-Johan Gynther 5aadc34d4a feat: remove unused import for product_picker_field in receipt_import_tab
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-08 12:58:55 +02:00
Nils-Johan Gynther 0873fa42bb feat: Refactor routing and navigation structure with StatefulShellRoute
Test Suite / test (24.15.0) (push) Has been cancelled
- Introduced a new function `_shellBranchIndexForPath` to determine the index of the shell branch based on the path.
- Replaced `ShellRoute` with `StatefulShellRoute.indexedStack` for better state management during navigation.
- Updated `AppShell` to handle navigation path changes and integrate with the new routing structure.
- Organized routes into `StatefulShellBranch` for better modularity and clarity.
- Enhanced admin panel functionality with improved alias management and UI updates.
- Added new methods in `ReceiptImportSessionNotifier` for managing selected items and edits more efficiently.
- Improved UI components in receipt import and admin panels for better performance and user experience.
- Added PageStorageKeys to various ListViews to maintain scroll positions across navigation.
- Documented performance goals and profiling strategies in a new PERFORMANCE.md file.
2026-05-08 12:51:38 +02:00
Nils-Johan Gynther 73309cb110 feat: implement alias strategy for receipt import with matchedVia tracking
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 14:15:45 +02:00
Nils-Johan Gynther d92272e554 feat: implement matchedVia tracking for receipt items and enhance user alias management
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 13:57:41 +02:00
Nils-Johan Gynther f7446cc2df feat: enhance security with user-scoped inventory and IDOR protection
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 12:00:57 +02:00
Nils-Johan Gynther 17893824d5 feat: implement user-specific inventory management with security checks
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 11:58:00 +02:00
Nils-Johan Gynther 4affb03504 Refactor code structure for improved readability and maintainability 2026-05-07 11:57:54 +02:00
Nils-Johan Gynther 943e449c97 docs: update security status in technical documentation for backend and Flutter frontend
Test Suite / test (24.15.0) (push) Has been cancelled
- Added detailed security status section in backend documentation, outlining implemented security features and remaining risks as of 2026-05-07.
- Included security features implemented in Flutter, emphasizing auth-gating, token storage, and limitations regarding web security.
2026-05-07 10:48:35 +02:00
Nils-Johan Gynther 5b1aec6449 fix: ensure new unit is not null when confirming unit change
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 10:23:44 +02:00
Nils-Johan Gynther a68a0ca86f feat: add unit mapping functionality
Test Suite / test (24.15.0) (push) Has been cancelled
- Added new API path for unit mappings in `api_paths.dart`.
- Implemented `upsertUnitMapping` method in `ImportRepository` to handle unit mapping creation.
- Updated `ReceiptImportTab` to learn and save unit mappings during receipt import.
- Created DTO for unit mapping with validation in `create-unit-mapping.dto.ts`.
- Added SQL migration for `UnitMapping` table creation with necessary constraints.
2026-05-07 10:00:42 +02:00
Nils-Johan Gynther 26823fbf35 feat: add unitMappings field to User and Product models
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 08:12:42 +02:00
Nils-Johan Gynther 7d63b615b6 feat: add unit mapping functionality and confirmation dialog for unit changes in import process
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 08:10:56 +02:00
Nils-Johan Gynther a19bc1279a fix: update l1Category method to return 'Övrigt' for empty categoryPath
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 07:56:49 +02:00
Nils-Johan Gynther 7f7e4c24a8 feat: enhance inventory management with category and location filters
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-07 07:51:47 +02:00
Nils-Johan Gynther e7251fd94c feat: add location field to PantryItem model and update related functionality
Test Suite / test (24.15.0) (push) Has been cancelled
Co-authored-by: Copilot <copilot@github.com>
2026-05-06 11:54:56 +02:00
Nils-Johan Gynther 63d249b0a8 feat: update findByOwner method to exclude private filter for active products
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 10:48:56 +02:00
Nils-Johan Gynther 7a8f441390 feat: update API endpoint for product listing and simplify manual product creation UI
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 10:43:55 +02:00
Nils-Johan Gynther ea44c4fe7a feat: update inventory queries to filter by product owner for improved user-specific analysis
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 10:34:04 +02:00
Nils-Johan Gynther 7f15f8028b feat: update inventory queries to filter by userId for accurate recipe analysis
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 10:32:35 +02:00
Nils-Johan Gynther 3e27423f24 feat: update migration documentation and remove obsolete file; enhance Prisma recovery instructions
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 09:42:45 +02:00
Nils-Johan Gynther 8200045438 feat: enhance user-scoped AI functionality with admin toggles and premium features
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 09:29:16 +02:00
Nils-Johan Gynther 04b1fc3024 feat: add rematch functionality for recipe ingredients and enhance inventory management
Test Suite / test (24.15.0) (push) Has been cancelled
- Added a new API path for rematching recipe ingredients in `api_paths.dart`.
- Implemented a manual product creation dialog in `inventory_screen.dart` to allow users to create new products directly.
- Integrated the rematch functionality in `recipe_repository.dart` to handle rematching of recipe ingredients.
- Updated the recipe detail screen to include a button for triggering the rematch process.
- Introduced a new `RecipeMatchingService` in the backend to handle ingredient matching logic.
- Added database migration to include `aiEngineEnabled` column in the User table.

Co-authored-by: Copilot <copilot@github.com>
2026-05-06 09:20:31 +02:00
Nils-Johan Gynther 9fe85a719c feat: implement recipe analysis service and data models
Test Suite / test (24.15.0) (push) Has been cancelled
- Added RecipeAnalysisService to handle recipe ingredient analysis, including methods for checking ingredient availability and calculating quantities.
- Introduced new TypeScript definitions for recipe analysis results, including ingredient status and summary.
- Created corresponding Dart models for recipe analysis, including RecipeIngredientAnalysis, RecipeAnalysisSummary, and RecipeShoppingCandidate.
- Updated Flutter UI to reflect changes in ingredient availability status.
- Fixed color opacity issue in recipe image card.
2026-05-06 07:54:03 +02:00
Nils-Johan Gynther 969dafdbc6 Refactor code structure for improved readability and maintainability
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 07:37:59 +02:00
Nils-Johan Gynther e4f201ea36 feat: enhance recipe ingredient model; add raw fields and optional properties for better ingredient handling
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-06 07:25:42 +02:00
Nils-Johan Gynther 612fcddb47 feat: refactor recipe import process; separate ingredient handling and improve data model for better flexibility
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-05 16:52:58 +02:00
Nils-Johan Gynther ce20b1dd07 feat: implement AI recipe suggestions; add endpoint and UI for generating suggestions based on inventory
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-05 14:15:28 +02:00
Nils-Johan Gynther 3ea5a4778f feat: add functionality to manually add ingredients; implement CreateIngredientDto and update RecipesController and RecipesService
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-05 13:38:23 +02:00
Nils-Johan Gynther dba3c63ec4 feat: enhance recipe creation process; add editable fields for ingredients and support for alternative ingredients in review step
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 22:29:29 +02:00
Nils-Johan Gynther 505c89c731 Fix error handling in various admin and inventory screens
Test Suite / test (24.15.0) (push) Has been cancelled
- Updated error handling in AdminAliasesPanel, AdminDatabasePanel, AdminPendingProductsPanel, and AdminProductsPanel to ensure consistent snackbar display without extra parentheses.
- Refined error handling in ConsumeInventoryScreen, CreateInventoryScreen, InventoryDetailScreen, InventoryEditScreen, and SwipeableInventoryTile to maintain consistent snackbar formatting.
- Improved error handling in MealPlanScreen, PantryScreen, ProfileScreen, and RecipeDetailScreen to ensure proper user feedback on errors.
2026-05-04 22:25:56 +02:00
Nils-Johan Gynther db02f6f0fb feat: update migration script; remove IF NOT EXISTS clause for alternativeProductIds column in RecipeIngredient table
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 22:20:12 +02:00
Nils-Johan Gynther 2e4e2141c6 feat: update migration script; add IF NOT EXISTS clause for alternativeProductIds column in RecipeIngredient table
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 22:11:57 +02:00
Nils-Johan Gynther 1b1d5d006d feat: enhance error handling; implement copyable SnackBar for user messages across various screens
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 22:10:23 +02:00
Nils-Johan Gynther 2c8d6b69ae feat: add support for alternative ingredients; implement JSON storage and parsing logic
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 22:06:57 +02:00
Nils-Johan Gynther 64f63b3392 feat: enhance ingredient management; add editable fields for quantity, unit, and notes in recipe creation
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:43:43 +02:00
Nils-Johan Gynther f32f69db5d feat: refactor recipe parsing logic; replace regex literals with constants for improved readability and maintainability
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:38:44 +02:00
Nils-Johan Gynther 38801f84eb feat: export ParsedIngredient and ParsedRecipe interfaces for external usage
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:35:14 +02:00
Nils-Johan Gynther b52205c8c3 feat: refactor recipe ownership logic; auto-claim ownerless recipes and ensure atomic updates for ingredients
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:33:33 +02:00
Nils-Johan Gynther b7c857732c feat: add product validation in recipe update and creation; throw BadRequestException for inactive products
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:30:51 +02:00
Nils-Johan Gynther 648e1856a1 feat: refactor recipe parsing logic; move parseRecipeMarkdown and related functions to a new utility file
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:28:06 +02:00
Nils-Johan Gynther a164b42bdc feat: update Dockerfile to ensure prisma directory is copied correctly and remove unnecessary .prisma copy
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:09:53 +02:00
Nils-Johan Gynther 8d7f01a4ad feat: add missing .prisma directory copy in Dockerfile for proper deployment
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:08:17 +02:00
Nils-Johan Gynther 293fe34710 feat: remove unnecessary prisma generate command from Dockerfile
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:03:45 +02:00
Nils-Johan Gynther 5f176ce75f feat: update Dockerfile stages for improved dependency management; add prisma to package.json and package-lock.json
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 21:02:15 +02:00
Nils-Johan Gynther 5411dfe2c0 feat: add utility functions for date and quantity formatting; refactor inventory and recipe screens to use new formatters
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 20:50:18 +02:00
Nils-Johan Gynther a645d6a364 feat: refactor inventory and recipe services for improved error handling and code reuse; add systematic backend review plan
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 20:44:43 +02:00
Nils-Johan Gynther 6dfd4c372d feat: add recipe-api service definition to docker-compose
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 20:17:24 +02:00
Nils-Johan Gynther ffe50e5151 feat: add TypeScript definitions for next-auth session with accessToken and user details
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 20:09:21 +02:00
Nils-Johan Gynther afd2607000 feat: add AdminDatabasePanel and integrate it into AdminScreen; update profile screen to remove database tab
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 20:03:04 +02:00
Nils-Johan Gynther 64b06435cf feat: implement user-scoped receipt aliases with global fallback; enhance alias management in admin panel
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-04 19:43:13 +02:00
Nils-Johan Gynther d73ea5ef7c feat: enhance PDF processing and error handling; add Node.js version tracking
Test Suite / test (24.15.0) (push) Has been cancelled
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 22:29:47 +02:00
Nils-Johan Gynther 7c382dbf2e fix: forward 503 from importer-api as ServiceUnavailableException instead of 400
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-03 22:15:33 +02:00
nilsjohan 941c5f5915 chore: add package-lock.json and allow tracking in .gitignore for npm ci
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-03 22:03:03 +02:00
Nils-Johan Gynther 0922af57a4 Merge branch 'main' of ssh://gitea.gynther.se:2222/nilsjohan/recipe-app
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-03 21:48:28 +02:00
Nils-Johan Gynther dd74d8b7eb feat(docker): update Node.js version to 24.15.0 in Dockerfile and test workflow 2026-05-03 21:48:25 +02:00
nilsjohan c2b9c76ce7 chore: pin Node 24.15.0 in Docker and CI
Test Suite / test (24.15.0) (push) Has been cancelled
2026-05-03 21:47:55 +02:00
Nils-Johan Gynther 6278825f0a feat(docs): update main priorities in NEXT_STEPS to reflect new focus on Node.js version parity
Test Suite / test (24.x) (push) Has been cancelled
2026-05-03 21:38:00 +02:00
Nils-Johan Gynther 700eb2c851 feat(docs): update NEXT_STEPS and TEKNISK_BESKRIVNING with new categories and improved rules
Test Suite / test (24.x) (push) Has been cancelled
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 19:58:12 +02:00
Nils-Johan Gynther 5b6d44b555 feat(receipt-import): enhance receipt processing with new category rules and add unit tests
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 19:55:32 +02:00
Nils-Johan Gynther 85b41f8587 ci: add automated test workflow
Test Suite / test (24.x) (push) Has been cancelled
2026-05-03 19:55:05 +02:00
Nils-Johan Gynther 6abedff07a feat(seed): add new categories for Delikatesschark, Sylt, mos & marmelad, and Hårdost lagrad 2026-05-03 18:20:09 +02:00
Nils-Johan Gynther a555e74201 feat(receipt-import): add function to ignore specific receipt names and filter out ignored items
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 18:18:07 +02:00
Nils-Johan Gynther 6c3e391582 fix(receipt-import): remove unused file extension from file metadata
Co-authored-by: Copilot <copilot@github.com>
2026-05-03 17:05:57 +02:00
Nils-Johan Gynther 1320f936ec Refactor technical documentation for clarity and updates
- Updated "teknisk_beskrivning_flutter.md" to streamline content and remove outdated sections, focusing on architecture, environment, and recent technical additions.
- Enhanced "migrering-MSI.md" with post-migration updates and clarifications for target audience.
- Revised "produktlansering.md" to serve as a release checklist, ensuring it complements existing documentation without duplication.

Co-authored-by: Copilot <copilot@github.com>
2026-05-03 17:03:20 +02:00
445 changed files with 137645 additions and 81104 deletions
+4
View File
@@ -19,3 +19,7 @@ SEED_USER2_PASSWORD=Test-Anv2-FBG
AUTH_SECRET=WheqAss4F/al9yRZRqepJEBs6TzPsN3brX0iBiF4Oww= AUTH_SECRET=WheqAss4F/al9yRZRqepJEBs6TzPsN3brX0iBiF4Oww=
JWT_SECRET=uK9yRQpyyWOcHYcYbpAdsJ7NJcEsyCYZcgF82OnBz2k= JWT_SECRET=uK9yRQpyyWOcHYcYbpAdsJ7NJcEsyCYZcgF82OnBz2k=
MISTRAL_API_KEY=JGPjLuNnzaLSYMxKbexLZohUOegrSLye MISTRAL_API_KEY=JGPjLuNnzaLSYMxKbexLZohUOegrSLye
FLYER_AI_TIMEOUT_MS=60000
FLYER_AI_RETRIES=2
FLYER_AI_DEBUG=1
FLYER_AI_DEBUG_DIR=/app/debug
+45 -33
View File
@@ -1,33 +1,45 @@
# Kopiera till .env och fyll i riktiga värden # Kopiera till .env och fyll i riktiga värden
# cp .env.example .env # cp .env.example .env
# MariaDB # MariaDB
MARIADB_ROOT_PASSWORD=byt-ut-mig MARIADB_ROOT_PASSWORD=byt-ut-mig
MARIADB_DATABASE=recipe_app MARIADB_DATABASE=recipe_app
MARIADB_USER=recipe_user MARIADB_USER=recipe_user
MARIADB_PASSWORD=byt-ut-mig MARIADB_PASSWORD=byt-ut-mig
# Auth.js / NextAuth # Auth.js / NextAuth
# Generera med: openssl rand -base64 32 # Generera med: openssl rand -base64 32
AUTH_SECRET=byt-ut-mig AUTH_SECRET=byt-ut-mig
# JWT (NestJS backend) # JWT (NestJS backend)
# Generera med: openssl rand -base64 32 # Generera med: openssl rand -base64 32
# OBS: Appen vägrar starta om detta saknas. # OBS: Appen vägrar starta om detta saknas.
JWT_SECRET=byt-ut-mig JWT_SECRET=byt-ut-mig
# Mistral AI # Mistral AI
# Hämtas från: https://console.mistral.ai/ # Hämtas från: https://console.mistral.ai/
MISTRAL_API_KEY= MISTRAL_API_KEY=
FLYER_AI_TIMEOUT_MS=45000
# Publik URL (används av frontend) FLYER_AI_RETRIES=2
NEXT_PUBLIC_APP_URL=https://recept.gynther.se FLYER_AI_DEBUG=0
NEXT_PUBLIC_API_URL=https://recept.gynther.se # Linux-container: /app/debug, lokalt: ./debug
# CORS — tillåtna origins för backend-API (normalt samma som APP_URL) FLYER_AI_DEBUG_DIR=/app/debug
ALLOWED_ORIGIN=https://recept.gynther.se
# Publik URL (används av frontend)
# Bootstrap-användare (skapas/uppdateras vid appstart) NEXT_PUBLIC_APP_URL=https://recept.gynther.se
ADMIN_NADMIN_PASSWORD=byt-ut-mig NEXT_PUBLIC_API_URL=https://recept.gynther.se
ADMIN_PADMIN_PASSWORD=byt-ut-mig # CORS — tillåtna origins för backend-API (normalt samma som APP_URL)
SEED_USER1_PASSWORD=byt-ut-mig ALLOWED_ORIGIN=https://recept.gynther.se
SEED_USER2_PASSWORD=byt-ut-mig
# Importer integration
IMPORTER_SERVICE_URL=http://importer-api:3001
RECEIPT_TRACE_DECISIONS=0
# Optional webhook hardening
GITEA_WEBHOOK_SECRET=
# Bootstrap-användare (skapas/uppdateras vid appstart)
ADMIN_NADMIN_PASSWORD=byt-ut-mig
ADMIN_PADMIN_PASSWORD=byt-ut-mig
SEED_USER1_PASSWORD=byt-ut-mig
SEED_USER2_PASSWORD=byt-ut-mig
+254
View File
@@ -0,0 +1,254 @@
name: Test Suite
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
backend-pr-quick:
if: gitea.event_name == 'pull_request'
runs-on: backend-node24
env:
DATABASE_URL: mysql://ci:ci@127.0.0.1:3306/recipe_app
JWT_SECRET: ci-test-secret
steps:
- name: Verify Node toolchain on runner
run: |
node --version
npm --version
- name: Ensure workspace checkout
shell: bash
run: |
set -euo pipefail
ws="${GITHUB_WORKSPACE:-$PWD}"
if [ -d "$ws/backend" ]; then
echo "Workspace already prepared at $ws"
exit 0
fi
mkdir -p "$ws"
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ -n "${GITHUB_TOKEN:-}" ]; then
repo_url="$(echo "$repo_url" | sed "s#https://#https://oauth2:${GITHUB_TOKEN}@#")"
fi
git clone --depth 1 "$repo_url" "$ws"
if [ -n "${GITHUB_SHA:-}" ]; then
git -C "$ws" fetch --depth 1 origin "$GITHUB_SHA" || true
git -C "$ws" checkout -f "$GITHUB_SHA" || true
fi
- name: Install dependencies (backend)
working-directory: ./backend
run: npm ci
- name: Prisma schema validate
working-directory: ./backend
run: npm run prisma:validate
- name: Generate Prisma Client
working-directory: ./backend
run: npm run prisma:generate
- name: Verify generated Prisma client is typed
working-directory: ./backend
run: |
if ! grep -q "export \* from '.prisma/client/default'" node_modules/@prisma/client/index.d.ts; then
echo "Prisma client export is unexpected"
exit 1
fi
- name: Run receipt-import focused tests (PR quick)
working-directory: ./backend
run: npx jest src/receipt-import/receipt-import.service.spec.ts src/receipt-import/receipt-import.parse-flow.spec.ts src/receipt-import/receipt-import.save.spec.ts --no-coverage
- name: Typecheck backend
working-directory: ./backend
run: npm run typecheck
- name: Build NestJS app
working-directory: ./backend
run: npm run build
quick-import-pr-quick:
if: gitea.event_name == 'pull_request'
runs-on: backend-node24
env:
DATABASE_URL: mysql://ci:ci@127.0.0.1:3306/recipe_app
JWT_SECRET: ci-test-secret
steps:
- name: Verify Node toolchain on runner
run: |
node --version
npm --version
- name: Ensure workspace checkout
shell: bash
run: |
set -euo pipefail
ws="${GITHUB_WORKSPACE:-$PWD}"
if [ -d "$ws/backend" ]; then
echo "Workspace already prepared at $ws"
exit 0
fi
mkdir -p "$ws"
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ -n "${GITHUB_TOKEN:-}" ]; then
repo_url="$(echo "$repo_url" | sed "s#https://#https://oauth2:${GITHUB_TOKEN}@#")"
fi
git clone --depth 1 "$repo_url" "$ws"
if [ -n "${GITHUB_SHA:-}" ]; then
git -C "$ws" fetch --depth 1 origin "$GITHUB_SHA" || true
git -C "$ws" checkout -f "$GITHUB_SHA" || true
fi
- name: Install dependencies (backend)
working-directory: ./backend
run: npm ci
- name: Run quick-import focused tests (PR quick)
working-directory: ./backend
run: npx jest src/quick-import/quick-import.service.spec.ts --no-coverage
backend-full:
if: gitea.event_name == 'push'
runs-on: backend-node24
env:
DATABASE_URL: mysql://ci:ci@127.0.0.1:3306/recipe_app
JWT_SECRET: ci-test-secret
steps:
- name: Verify Node toolchain on runner
run: |
node --version
npm --version
- name: Ensure workspace checkout
shell: bash
run: |
set -euo pipefail
ws="${GITHUB_WORKSPACE:-$PWD}"
if [ -d "$ws/backend" ]; then
echo "Workspace already prepared at $ws"
exit 0
fi
mkdir -p "$ws"
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ -n "${GITHUB_TOKEN:-}" ]; then
repo_url="$(echo "$repo_url" | sed "s#https://#https://oauth2:${GITHUB_TOKEN}@#")"
fi
git clone --depth 1 "$repo_url" "$ws"
if [ -n "${GITHUB_SHA:-}" ]; then
git -C "$ws" fetch --depth 1 origin "$GITHUB_SHA" || true
git -C "$ws" checkout -f "$GITHUB_SHA" || true
fi
- name: Install dependencies (backend)
working-directory: ./backend
run: npm ci
- name: Prisma schema validate
working-directory: ./backend
run: npm run prisma:validate
- name: Generate Prisma Client
working-directory: ./backend
run: npm run prisma:generate
- name: Verify generated Prisma client is typed
working-directory: ./backend
run: |
if ! grep -q "export \* from '.prisma/client/default'" node_modules/@prisma/client/index.d.ts; then
echo "Prisma client export is unexpected"
exit 1
fi
- name: Dependency audit (high+critical)
working-directory: ./backend
run: npm run audit:high
- name: Typecheck backend
working-directory: ./backend
run: npm run typecheck
- name: Run tests (backend)
working-directory: ./backend
run: npm test
- name: Build NestJS app
working-directory: ./backend
run: npm run build
flutter-quality:
runs-on: flutter-3-41
steps:
- name: Verify Flutter toolchain on runner
run: flutter --version
- name: Ensure workspace checkout
shell: bash
run: |
set -euo pipefail
ws="${GITHUB_WORKSPACE:-$PWD}"
mkdir -p "$ws"
repo_url="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
if [ -n "${GITHUB_TOKEN:-}" ]; then
repo_url="$(echo "$repo_url" | sed "s#https://#https://oauth2:${GITHUB_TOKEN}@#")"
fi
if [ ! -d "$ws/.git" ]; then
git -C "$ws" init
git -C "$ws" remote add origin "$repo_url"
else
git -C "$ws" remote set-url origin "$repo_url"
fi
ref="${GITHUB_SHA:-${GITHUB_REF_NAME:-main}}"
git -C "$ws" fetch --depth 1 origin "$ref"
git -C "$ws" checkout -f FETCH_HEAD
if [ ! -d "$ws/flutter" ]; then
echo "Flutter directory missing after checkout: $ws/flutter"
ls -la "$ws"
exit 1
fi
- name: Install dependencies (flutter)
shell: bash
run: |
set -euo pipefail
cd "${GITHUB_WORKSPACE:-$PWD}/flutter"
flutter pub get
- name: Analyze Flutter code
shell: bash
run: |
set -euo pipefail
cd "${GITHUB_WORKSPACE:-$PWD}/flutter"
flutter analyze
- name: Set Flutter test mode
shell: bash
run: |
if [ "${{ gitea.event_name }}" = "pull_request" ]; then
echo "FLUTTER_TEST_CMD=flutter test --reporter=compact" >> "$GITHUB_ENV"
else
echo "FLUTTER_TEST_CMD=flutter test" >> "$GITHUB_ENV"
fi
- name: Run Flutter tests
shell: bash
run: |
set -euo pipefail
cd "${GITHUB_WORKSPACE:-$PWD}/flutter"
${FLUTTER_TEST_CMD}
+18
View File
@@ -0,0 +1,18 @@
# Copilot Instructions
## Database Command Style
When suggesting database commands in this repository, always use credentials loaded from `.env` inline in the command.
Required pattern:
- Read `MARIADB_ROOT_PASSWORD` from `.env` with `grep` and sanitize quotes/CRLF.
- Read `MARIADB_DATABASE` from `.env` with `grep` and sanitize quotes/CRLF.
- Pass both values directly to `mariadb` command invocations.
Preferred one-liner style:
```bash
docker exec -i recipe-db mariadb -uroot -p"$(grep -E '^[[:space:]]*MARIADB_ROOT_PASSWORD[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" "$(grep -E '^[[:space:]]*MARIADB_DATABASE[[:space:]]*=' .env | tail -n1 | sed -E 's/^[^=]*=[[:space:]]*//; s/[[:space:]]+$//; s/^["'\''']|["'\''']$//g' | tr -d '\r')" -e "SELECT 1;"
```
Do not suggest hardcoded passwords for MariaDB commands.
+155
View File
@@ -0,0 +1,155 @@
name: Test Suite
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
backend-pr-quick:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [24.15.0]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies (backend)
working-directory: ./backend
run: npm ci
- name: Prisma schema validate
working-directory: ./backend
run: npx prisma validate --schema prisma/schema.prisma
- name: Generate Prisma Client
working-directory: ./backend
run: npm run prisma:generate
- name: Verify generated Prisma client is typed
working-directory: ./backend
run: |
if ! grep -q "export \* from '.prisma/client/default'" node_modules/@prisma/client/index.d.ts; then
echo "Prisma client export is unexpected";
exit 1;
fi
- name: Lint backend
working-directory: ./backend
run: npm run lint
- name: Build NestJS app
working-directory: ./backend
run: npm run build
backend-full:
if: github.event_name == 'push'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [24.15.0]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies (backend)
working-directory: ./backend
run: npm ci
- name: Prisma schema validate
working-directory: ./backend
run: npx prisma validate --schema prisma/schema.prisma
- name: Generate Prisma Client
working-directory: ./backend
run: npm run prisma:generate
- name: Verify generated Prisma client is typed
working-directory: ./backend
run: |
if ! grep -q "export \* from '.prisma/client/default'" node_modules/@prisma/client/index.d.ts; then
echo "Prisma client export is unexpected";
exit 1;
fi
- name: Lint backend
working-directory: ./backend
run: npm run lint
- name: Dependency audit (high+critical)
working-directory: ./backend
run: npm audit --audit-level=high
- name: Run tests (backend)
working-directory: ./backend
run: npm test
- name: Build NestJS app
working-directory: ./backend
run: npm run build
flutter-quality:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Detect Flutter-related changes
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
flutter:
- 'flutter/**'
- '.github/workflows/test.yml'
- name: Set Flutter test mode
if: steps.filter.outputs.flutter == 'true'
shell: bash
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "FLUTTER_TEST_CMD=flutter test --reporter=compact" >> "$GITHUB_ENV"
else
echo "FLUTTER_TEST_CMD=flutter test" >> "$GITHUB_ENV"
fi
- name: Setup Flutter
if: steps.filter.outputs.flutter == 'true'
uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.9'
- name: Install dependencies (flutter)
if: steps.filter.outputs.flutter == 'true'
working-directory: ./flutter
run: flutter pub get
- name: Analyze Flutter code
if: steps.filter.outputs.flutter == 'true'
working-directory: ./flutter
run: flutter analyze
- name: Run Flutter tests
if: steps.filter.outputs.flutter == 'true'
working-directory: ./flutter
run: ${{ env.FLUTTER_TEST_CMD }}
+11 -1
View File
@@ -2,10 +2,20 @@
node_modules/ node_modules/
**/node_modules/ **/node_modules/
*.lock *.lock
package-lock.json # package-lock.json spåras för reproducerbara Docker-builds (npm ci)
!package-lock.json
!**/package-lock.json
# Environment files
.env
.env.* .env.*
!.env.example
# Compiled backend artifacts (built in Docker)
backend/dist/
backend/tsconfig.tsbuildinfo
# Dart/Flutter generated files with absolute host paths — must not be committed # Dart/Flutter generated files with absolute host paths — must not be committed
.dart_tool/ .dart_tool/
*/.dart_tool/ */.dart_tool/
flutter/build/
+27
View File
@@ -0,0 +1,27 @@
# Dokumentationsagent
**Roll**: Hjälper till att uppdatera och förbättra dokumentation, men **modifierar aldrig filer utan godkännande**.
## Regler
- **Läs endast**: Använd `read` och `grep` för att analysera dokumentation.
- **Föreslå ändringar**: Använd `suggest` för att visa förändringar innan de appliceras.
- **Förbjudna åtgärder**:
- Modifiera `teknisk_beskrivning.md` eller `docs/` direkt.
- Köra `bash`-kommandon som påverkar filer (t.ex. `rm`, `echo >`).
## Arbetsflöde
1. Läs befintlig dokumentation med `read`.
2. Föreslå ändringar via `/local-review-uncommitted`.
3. Vänta på explicit godkännande innan modifieringar görs.
4. Om användaren ber om en ändring, visa **alltid** en diff först.
## Exempel på tillåtna åtgärder
- Söka efter föråldrad information i `teknisk_beskrivning.md`.
- Föreslå nya avsnitt eller korrigeringar.
- Sammanfatta befintligt innehåll.
- Lägga till referenser eller länkar till relevant information.
- lägga till information i `docs/` efter godkännande.
## Exempel på förbjudna åtgärder
- Redigera `teknisk_beskrivning.md` direkt.
- Köra `git add` eller `git commit` utan begäran.
- Ta bort eller flytta filer i `docs/`.
+575
View File
@@ -0,0 +1,575 @@
Du är en senior utvecklare och säkerhetsexpert. Analysera commit-kandidater i detta fullstack-projekt (backend: NestJS + Prisma, frontend: Next.js/Flutter, databas: MariaDB).
Syfte:
- Detta är en pre-commit quality gate innan commit.
- Leverera ett tydligt gate-beslut: `PASS`, `PASS_WITH_WARNINGS` eller `BLOCK`.
- Vid `BLOCK`: lista exakta blockerare och fixordning.
---
## 0. Deterministiska gate-regler (källa till sanning)
### 0.1 Filurval (delta-first)
1. Primärt: analysera alla staged filer.
2. Om inga staged filer finns: analysera commit-kandidater i working tree (modified + untracked).
3. Exkludera alltid: `node_modules`, `.git`, build/cache-artifacts, binärfiler, genererade filer som inte ska committas.
4. Fokusera blockerande bedömning på förändrad kod (delta). Legacy-problem i opåverkade delar rapporteras som teknisk skuld (ej blockerande i denna gate).
### 0.2 Severity och beslut
- **Critical**: säkerhetshål/scope-brist med hög impact (t.ex. IDOR, auth bypass, PII-läckage, injection).
- **High**: allvarlig korrektness-/driftsrisk i produktion.
- **Medium/Low**: informativa förbättringar (blockerar inte).
**Beslutslogik (deterministisk):**
- `BLOCK` om minst 1 `Critical`.
- `BLOCK` om 2 eller fler `High`.
- `PASS_WITH_WARNINGS` om exakt 1 `High` utan `Critical`.
- `PASS` om inga `Critical`/`High`.
### 0.3 Evidenskrav för blockerande fynd
Varje `Critical`/`High` måste ha:
- `Evidence`: `code`, `test`, eller `runtime`.
- Fil + radreferens.
- Konkreta fixsteg.
Fynd med endast antagande märks `Needs verification` och får inte ensamt orsaka `BLOCK`, om inte risken är uppenbart kritisk.
### 0.4 Stop-early-regel (effektivitet)
- Vid första tydliga `Critical`: sätt preliminärt `BLOCK`, identifiera max 3 ytterligare blockerare, avsluta sedan djupanalys.
### 0.5 Rapportbudget
- Rapportera max 5 informativa fynd (`Medium/Low`), prioriterade efter högst nytta/lägst kostnad.
---
## 1. Analysfokus
### 1.1 Allmän kodkvalitet
- Läsbarhet/underhållbarhet: namngivning, modularisering, komplexitet.
- TypeScript/Flutter best practices.
- Kommentarer för icke-obvious logik.
### 1.2 Performance-optimeringar (informational)
- Algoritmisk effektivitet.
- Onödiga kopior/serialiseringar.
- Databasfrågor, N+1-risk, ineffektiva `include/select`.
### 1.3 Säkerhetsanalys
Fokusera på **NestJS/Prisma/Next.js-specifika säkerhetsrisker** med automatiserad detektion.
#### 1.3.1 Vanliga säkerhetsrisker (med exempel)
| Risk | Exempel i kod | PowerShell för att hitta | Åtgärd |
|-------------------------------|-------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| **SQL-injection (Prisma)** | `prisma.$queryRaw\`SELECT * FROM users WHERE name = ${userInput}\`` | `Select-String -Path "src\*" -Pattern "\$queryRaw.*\`\$\{"` | Använd Prisma-parametrar: `prisma.$queryRaw\`SELECT * FROM users WHERE name = ${userInput}\`` |
| **XSS (Next.js)** | `<div dangerouslySetInnerHTML={{ __html: userInput }} />` | `Select-String -Path "src\*" -Pattern "dangerouslySetInnerHTML"` | Använd `DOMPurify` eller `react-dom/server`. |
| **Secrets i kod** | `const apiKey = "sk_123456789";` | `Select-String -Path "src\*" -Pattern "apiKey|password|secret" -CaseSensitive` | Flytta till `.env` och använd `@nestjs/config`. |
| **CSRF (NestJS)** | Saknad `@UseGuards(CsrfGuard)` i `main.ts` | `Select-String -Path "src\main.ts" -Pattern "UseGuards.*Csrf" -NotMatch` | Lägg till `app.use(csurf())` i `main.ts`. |
| **Insecure Deserialization** | `JSON.parse(userInput)` utan validering | `Select-String -Path "src\*" -Pattern "JSON\.parse\("` | Använd `zod` eller `class-validator`. |
#### 1.3.2 Automatiserad säkerhetssökning (PowerShell)
Kör dessa kommandon för att hitta säkerhetsproblem:
```powershell
# 1. Sök efter hårdkodade secrets
Select-String -Path "src\*" -Pattern "apiKey|password|secret" -CaseSensitive |
ForEach-Object {
Write-Warning "Potentiell secret läcka i $($_.Path):$($_.LineNumber) - $($_.Line)"
}
# 2. Sök efter SQL-injection-risker i Prisma
Select-String -Path "src\*" -Pattern "\$queryRaw.*\`\$\{" |
ForEach-Object {
Write-Warning "Potentiell SQL-injection i $($_.Path):$($_.LineNumber)"
}
# 3. Kör npm audit för sårbara beroenden
npm audit --json | Out-File "npm_audit.json"
if ((Get-Content "npm_audit.json" | ConvertFrom-Json).metadata.vulnerabilities.high) {
Write-Error "Kritiska sårbarheter hittade! Kör `npm audit fix`."
}
```
#### 1.3.3 Verktygsintegration
- **`npm audit`**: Upptäcker sårbara `node_modules`.
```powershell
npm audit --json | ConvertFrom-Json | Select-Object -ExpandProperty metadata
```
- **`eslint-plugin-security`**: Installera och kör:
```powershell
npm install --save-dev eslint-plugin-security
npx eslint --plugin security src/
```
- **`trivy`** (för container-säkerhet, om relevant):
```powershell
trivy fs --security-checks vuln .
```
#### 1.3.4 Prisma/NestJS-specifika risker
- **Prisma-raw-queries**: Undvik dynamiska SQL-strängar.
```ts
// OSAKER:
prisma.$queryRaw\`SELECT * FROM users WHERE name = ${userInput}\`;
// SÄKER:
prisma.$queryRaw\`SELECT * FROM users WHERE name = ${Prisma.sql\`${userInput}\`}\`;
```
- **NestJS-guards**: Validera alltid `roles` i `@UseGuards()`:
```ts
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles('admin')
@Get('admin')
adminOnly() {}
```
### 1.4 Backend-specifik kontroll (NestJS + Prisma)
Kontrollera **NestJS/Prisma-specifika mönster** med automatiserade verktyg.
#### 1.4.1 Vanliga backend-risker (med exempel)
| Risk | Exempel i kod | PowerShell för att hitta | Åtgärd |
|-------------------------------|-------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
| **Saknad DTO-validering** | `@Post() create(@Body() data: any)` | `Select-String -Path "src\*" -Pattern "@Body\(\)\.*any"` | Använd `class-validator`: `@Body() data: CreateUserDto` |
| **IDOR (Insecure Direct Object Reference)** | `@Get(':id') findOne(@Param('id') id: string)` utan scope-validering | `Select-String -Path "src\*" -Pattern "@Get\('.*'\)\.*@Param.*id.*string" -NotMatch "UseGuards"` | Lägg till `@UseGuards(OwnershipGuard)`. |
| **Saknad transaktion (Prisma)** | `await prisma.user.create({ data }); await prisma.log.create({ data })` | `Select-String -Path "src\*" -Pattern "await prisma\..*\.create.*await prisma"` | Använd `prisma.$transaction`: `await prisma.$transaction([...])` |
| **N+1-problem (Prisma)** | `users.map(user => prisma.posts.findMany({ where: { userId: user.id } }))` | `Select-String -Path "src\*" -Pattern "\.map.*prisma\..*\.findMany"` | Använd `include`: `prisma.user.findMany({ include: { posts: true } })` |
| **Saknad rate limiting** | `@Post('login')` utan begränsning | `Select-String -Path "src\*" -Pattern "@Post.*login" -NotMatch "ThrottlerGuard"` | Lägg till `@UseGuards(ThrottlerGuard)`. |
#### 1.4.2 Automatiserad validering (PowerShell)
Kör dessa kommandon för att hitta backend-problem:
```powershell
# 1. Sök efter saknad DTO-validering
Select-String -Path "src\*" -Pattern "@Body\(\)\.*any" |
ForEach-Object {
Write-Warning "Saknad DTO-validering i $($_.Path):$($_.LineNumber)"
}
# 2. Sök efter IDOR-risker
Select-String -Path "src\*" -Pattern "@Get\('.*'\)\.*@Param.*id.*string" -NotMatch "UseGuards" |
ForEach-Object {
Write-Warning "Potentiell IDOR-risk i $($_.Path):$($_.LineNumber)"
}
# 3. Sök efter saknade Prisma-transaktioner
Select-String -Path "src\*" -Pattern "await prisma\..*\.create.*await prisma" |
ForEach-Object {
Write-Warning "Saknad transaktion i $($_.Path):$($_.LineNumber)"
}
```
#### 1.4.3 Rekommenderade bibliotek
- **DTO-validering**: [`class-validator`](https://github.com/typestack/class-validator)
```powershell
npm install class-validator class-transformer
```
- **Auktorisation**: [`@nestjs/passport`](https://docs.nestjs.com/security/authentication)
```powershell
npm install @nestjs/passport passport passport-jwt
```
- **Rate limiting**: [`@nestjs/throttler`](https://docs.nestjs.com/security/rate-limiting)
```powershell
npm install @nestjs/throttler
```
- **Prisma-optimeringar**:
- Använd `include` för att undvika N+1:
```ts
prisma.user.findMany({ include: { posts: true } });
```
- Använd `select` för att minimera data:
```ts
prisma.user.findMany({ select: { id: true, name: true } });
```
#### 1.4.4 Prisma-specifika optimeringar
- **Indexering**: Lägg till `@@index` i Prisma-schemat för frekventa frågor:
```prisma
model User {
id Int @id @default(autoincrement())
email String @unique
name String
@@index([email]) // Optimerar sökningar på email
}
```
- **Batch-operations**: Använd `createMany` istället för loopar:
```ts
await prisma.user.createMany({ data: users });
```
- **Timeout-hantering**: Använd `PrismaClient.$extends` för att lägga till timeout:
```ts
const prisma = new PrismaClient().$extends({
query: {
async $allOperations({ operation, model, args, query }) {
return query({ timeout: 10000 }); // 10s timeout
},
},
});
```
---
## 2. Krav på varje fynd
Använd följande mall:
- **Severity**: `Critical|High|Medium|Low`
- **Evidence**: `code|test|runtime|Needs verification`
- **Delsystem**: `backend|frontend|db|infra`
- **Fil**: `<path:line>`
- **Risk**: kort riskbeskrivning
- **Varför**: varför detta är ett problem
- **Åtgärd**: konkret, realistisk fix
- **Verifiering**: kommando/test för att bekräfta fix
Blocking-fynd (`Critical/High`) listas först, därefter informational (`Medium/Low`).
---
## 3. Obligatoriskt outputformat
Returnera exakt i denna ordning:
1. `Scope`
- Urvalsregel: `staged` eller `working-tree`
- Analyserade filer (exakt lista)
- Exkluderade filer (med orsak)
2. `Gate-beslut`
- `PASS|PASS_WITH_WARNINGS|BLOCK`
- Antal per severity: `Critical`, `High`, `Medium`, `Low`
- Kort motivering
3. `Blocking Findings (Critical/High)`
- Om inga finns: skriv `Inga blockerande fynd`.
4. `Informational Findings (Medium/Low)`
- Max 5 fynd.
5. `Fixplan (vid BLOCK eller PASS_WITH_WARNINGS)`
- Numrerad ordning, konkreta steg.
6. `Sammanfattning`
- Topp 3 åtgärder efter risk/vinst
- Tidsestimat
- Rekommenderade automatiserade kontroller
---
## 4. Konsistenskontroller (måste uppfyllas)
- Om `Gate-beslut=PASS` får inga `Critical/High` listas.
- Om `Gate-beslut=BLOCK` måste `Fixplan` innehålla minst 1 konkret blockerande åtgärd.
- Om `PASS_WITH_WARNINGS` används måste exakt 1 `High` finnas och 0 `Critical`.
---
## 5. Fallback: inget att analysera
Om inga relevanta filer hittas:
- Skriv `Inget att analysera` och varför (t.ex. tom staged + tom working tree).
- Ge nästa konkreta steg:
- `git add <filer>`
- `git diff --cached --name-only`
- Kör analysen igen.
---
## 6. Kontext för projektet
- Backend: NestJS + Prisma + MariaDB (Docker).
- Frontend: Next.js + TypeScript + Flutter.
- Mål: produktion, låg teknisk skuld, säkrad hantering av känslig data.
---
## 7. CI-koppling
- Detta är lokalt pre-commit-steg.
- Samma kvalitetskrav bör speglas i CI (push/PR) för att minska miljöskillnader.
---
## 8. TypeScript Syntaxanalys (PowerShell)
### 8.1 Automatiserad Felupptäckt
#### 8.1.1 Förberedelser
Kontrollera att TypeScript och Node.js är installerade:
```powershell
node --version
tsc --version
```
#### 8.1.2 Hämta alla TypeScript-fel
Kör TypeScript-compilern i **noEmit-läge** för att lista fel **utan att generera filer**:
```powershell
tsc --noEmit --pretty | Out-File -FilePath "ts_errors.log" -Encoding utf8
```
#### 8.1.3 Sök efter specifika felkoder (PowerShell)
Använd `Select-String` (PowerShells motsvarighet till `grep`) för att filtrera fel:
```powershell
# Sök efter "Cannot find name" (TS2304)
Select-String -Path "ts_errors.log" -Pattern "TS2304" | Format-Table -AutoSize
# Sök efter "Property does not exist" (TS2339/TS2551)
Select-String -Path "ts_errors.log" -Pattern "TS2339|TS2551" | Format-Table -AutoSize
# Sök efter "implicitly has 'any' type" (TS7006)
Select-String -Path "ts_errors.log" -Pattern "TS7006" | Format-Table -AutoSize
```
#### 8.1.4 Analysera specifika filer
Fokusera på ändrade filer i `git` (pre-commit):
```powershell
# Hämta alla ändrade TypeScript-filer (staged + unstaged)
$changedFiles = git diff --name-only --diff-filter=d | Where-Object { $_ -match '\.tsx?$' }
if ($changedFiles) {
tsc --noEmit $changedFiles | Out-File -FilePath "ts_errors_staged.log" -Encoding utf8
} else {
Write-Host "Inga ändrade TypeScript-filer hittades."
}
```
### 8.2 Vanliga TypeScript-Syntaxfel (Windows)
| Felkod | Beskrivning | PowerShell för att hitta fel | Åtgärd |
|--------------|---------------------------------------|--------------------------------------------------|------------------------------------------------------------------------|
| **TS2304** | Okänd variabel/import | `Select-String -Pattern "TS2304"` | Lägg till import eller deklarera variabeln. |
| **TS2339** | Egenskap saknas på objekt | `Select-String -Pattern "TS2339"` | Utöka interfacet eller använd `as`-typning. |
| **TS2551** | Felaktig egenskapsåtkomst | `Select-String -Pattern "TS2551"` | Kontrollera typdefinitionen. |
| **TS1128** | Saknad deklaration eller statement | `Select-String -Pattern "TS1128"` | Lägg till kodblock eller semikolon. |
| **TS7006** | Parameter saknar typ | `Select-String -Pattern "TS7006"` | Lägg till typannotation (t.ex. `: string`). |
| **TS1005** | Saknad semikolon | `Select-String -Pattern "TS1005"` | Lägg till semikolon om `tsconfig.json` kräver det. |
### 8.3 Åtgärdsförslag (PowerShell-exempel)
#### 8.3.1 TS2304: "Cannot find name"
**Orsak**: Variabel, funktion eller import saknas.
**Lösningar**:
1. **Lägg till import**:
```powershell
# Exempel: Saknad import för `axios`
Get-Content "src\api\service.ts" | Select-String "axios"
```
```diff
- fetchData();
+ import axios from 'axios';
+ axios.get('/api/data');
```
2. **Installera saknade typer**:
```powershell
npm install --save-dev @types/node
```
#### 8.3.2 TS2339/TS2551: "Property does not exist"
**Orsak**: Egenskap saknas i typdefinitionen.
**Lösningar**:
1. **Utöka interfacet**:
```diff
- interface User { name: string; }
+ interface User { name: string; address?: { city: string; }; }
```
2. **Använd `as`-typning (temporärt)**:
```ts
const city = (user as { address: { city: string } }).address.city;
```
#### 8.3.3 TS1128: "Declaration or statement expected"
**Orsak**: Ogiltig syntax (t.ex. saknat kodblock).
**Lösningar**:
1. **Lägg till kodblock**:
```diff
- function foo() return 5;
+ function foo() { return 5; }
```
2. **Kontrollera semikolon** (om `tsconfig.json` kräver det):
```diff
- const x = 5
+ const x = 5;
```
#### 8.3.4 TS7006: "Parameter implicitly has 'any' type"
**Orsak**: Parameter saknar typ.
**Lösningar**:
1. **Lägg till typannotation**:
```diff
- function greet(name) { ... }
+ function greet(name: string) { ... }
```
2. **Stäng av `noImplicitAny`** (ej rekommenderat):
```json
{
"compilerOptions": {
"noImplicitAny": false
}
}
```
### 8.4 Exempel på Korrigeringar (PowerShell)
#### 8.4.1 Fixa saknad import (TS2304)
**Felaktig kod**:
```ts
getUser(); // TS2304: Cannot find name 'getUser'
```
**Fix**:
```powershell
# Kontrollera om funktionen existerar i projektet
Get-ChildItem -Recurse -Include *.ts | Select-String -Pattern "function getUser"
```
```ts
import { getUser } from './api'; // Lägg till import
getUser();
```
#### 8.4.2 Fixa ogiltig egenskap (TS2551)
**Felaktig kod**:
```ts
interface User { name: string; }
const user: User = { name: 'Alice' };
console.log(user.age); // TS2551: Property 'age' does not exist
```
**Fix**:
```ts
interface User { name: string; age?: number; } // Gör 'age' valfritt
const user: User = { name: 'Alice' };
console.log(user.age); // OK (kan vara `undefined`)
```
### 8.5 PowerShell-specifika Tips
#### 8.5.1 Sökvägar och Filtrering
- **Använd dubbla citattecken (`"`) för sökvägar**:
```powershell
Get-ChildItem -Path "C:\Users\Nils-JohanGynther\dev\recipe-app\src\*.ts"
```
- **Escape specialtecken** med backtick (`\`):
```powershell
Select-String -Pattern "Property\`|does not exist"
```
#### 8.5.2 Kör `tsc` med full sökväg (om nödvändigt)
```powershell
& "C:\Users\Nils-JohanGynther\dev\recipe-app\node_modules\.bin\tsc" --noEmit
```
#### 8.5.3 Spara felutdata till fil
```powershell
tsc --noEmit | Out-File -FilePath "ts_errors.log" -Encoding utf8
```
### 8.6 Rekommenderat Arbetsflöde (PowerShell)
1. **Lista alla TypeScript-fel**:
```powershell
tsc --noEmit --pretty | Out-File -FilePath "ts_errors.log" -Encoding utf8
```
2. **Filtrera efter felkod**:
```powershell
Select-String -Path "ts_errors.log" -Pattern "TS2304|TS2551" | Format-Table -AutoSize
```
3. **Åtgärda felen** enligt tabellen ovan.
4. **Validera fixar**:
```powershell
tsc --noEmit
```
### 8.7 Vanliga PowerShell-kommandon
| Syfte | PowerShell-kommando |
|--------------------------------|-------------------------------------------------------|
| Lista TypeScript-filer | `Get-ChildItem -Recurse -Include *.ts,*.tsx` |
| Sök efter text i filer | `Select-String -Path "src\*.ts" -Pattern "TS2304"` |
| Kör TypeScript-compilern | `tsc --noEmit` |
| Spara felutdata till fil | `tsc --noEmit | Out-File "errors.log"` |
| Visa innehåll i en fil | `Get-Content "src\app.ts"` |
| Kontrollera git-status | `git status` |
| Lista ändrade filer | `git diff --name-only --diff-filter=d` |
### 8.8 Output-format (för pre-commit)
När du kör analysen, returnera resultatet i detta format:
```markdown
### Scope
- **Analyserade filer**:
- `src\api\service.ts` (2 TS2304-fel)
- `src\models\user.ts` (1 TS2551-fel)
- **Exkluderade filer**:
- `node_modules\` (ignorerad)
- `dist\` (genererad kod)
### Gate-beslut: `PASS_WITH_WARNINGS`
- **Critical**: 0
- **High**: 1 (TS2551 i `user.ts`)
- **Medium**: 2
- **Low**: 0
### Blocking Findings (Critical/High)
1. **TS2551** i `src\models\user.ts:15`
- **Risk**: Egenskapen `address` saknas i `User`-interfacet.
- **Åtgärd**: Utöka interfacet eller använd `as`-typning.
- **Verifiering**: Kör `tsc --noEmit` efter fix.
### Informational Findings (Medium/Low)
1. **TS2304** i `src\api\service.ts:8`
- **Förbättring**: Lägg till import för `axios`.
- **Kommando**: `npm install axios --save`
2. **TS7006** i `src\utils\helpers.ts:3`
- **Förbättring**: Lägg till typ för parametern `data`.
- **Exempel**: `function parse(data: string) { ... }`
### Fixplan
1. Åtgärda `TS2551` i `user.ts` (blockerande).
2. Lägg till saknade imports i `service.ts`.
3. Typa parametrar i `helpers.ts`.
### Sammanfattning
- **Topp 3 åtgärder**:
1. Fixa `User`-interfacet (5 min).
2. Installera `axios` (2 min).
3. Lägg till typer i `helpers.ts` (3 min).
- **Tidsestimat**: 10 minuter.
- **Rekommenderade automatiserade kontroller**:
- Lägg till `tsc --noEmit` i `pre-commit`-hook.
- Konfigurera ESLint för TypeScript (`@typescript-eslint`).
```
### 8.9 Felhantering i PowerShell
Använd `try/catch` för att fånga fel i PowerShell-skript:
```powershell
try {
tsc --noEmit | Out-File -FilePath "ts_errors.log" -Encoding utf8 -ErrorAction Stop
} catch {
Write-Error "TypeScript-compilering misslyckades: $_"
exit 1
}
```
### 8.10 Dynamiska sökvägar och validering
#### 8.10.1 Använd dynamiska sökvägar
Ersätt hårdkodade sökvägar med variabler för bättre portabilitet:
```powershell
$projectRoot = Resolve-Path "."
$tsconfigPath = Join-Path $projectRoot "tsconfig.json"
tsc --project $tsconfigPath --noEmit
```
#### 8.10.2 Validera TypeScript-installation
Kontrollera att `tsc` är tillgängligt innan analys:
```powershell
if (-not (Get-Command tsc -ErrorAction SilentlyContinue)) {
Write-Error "TypeScript är inte installerat. Kör: npm install -g typescript"
exit 1
}
```
#### 8.10.3 Förbättrad git-integration
Hämta ändrade filer med fullständiga sökvägar:
```powershell
$changedFiles = git diff --name-only --diff-filter=d | ForEach-Object {
$filePath = Resolve-Path $_ -ErrorAction SilentlyContinue
if ($filePath -and $filePath -match '\.tsx?$') { $filePath }
}
if ($changedFiles) {
tsc --noEmit $changedFiles | Out-File -FilePath "ts_errors_staged.log" -Encoding utf8
}
```
### 8.11 Stöd för PowerShell Core (pwsh)
> **Notera**: Alla kommandon i denna guide är testade och fungerar i både:
> - **Windows PowerShell 5.1** (standard i Windows 10/11)
> - **PowerShell Core 7+** (`pwsh`)
För bästa resultat, använd PowerShell Core (`pwsh`) för:
- Snabbare exekvering.
- Bättre Unicode-stöd (t.ex. emojis i felmeddelanden).
- Kompatibilitet med multiplattforms-projekt.
Installera PowerShell Core:
```powershell
winget install --id Microsoft.Powershell --source winget
```
+3
View File
@@ -0,0 +1,3 @@
{
"$schema": "https://app.kilo.ai/config.json"
}
+225
View File
@@ -0,0 +1,225 @@
# Plan: Fortsatt implementation av FlyerImport (autoflöde + UX i `/import` Flutter)
## Målbild
Implementera ett komplett flyer-flöde med så få klick som möjligt där:
- användaren markerar planerade inköp i `FlyerImportTab`
- kvittoimporten matchar automatiskt mot öppna `FlyerSelection` och sätter `status=bought`
- användaren bara behöver ingripa vid osäker matchning
Detta följer båda underlagen:
1) ingen befintlig flyer-UX i `/import` ännu (behöver byggas)
2) maximal automation i punkt 3 (sync med kvittoimport)
---
## Principer för “så få klick som möjligt”
- Default är automation: matcha och uppdatera utan extra dialoger.
- UI ska vara “review first, edit only when needed”.
- Endast två manuella åtgärder i happy path:
1. markera planerade flyer-varor
2. importera kvitto
- Manuell override ska finnas men inte blockera flödet.
---
## Fas 1: Backend-kontrakt för auto-sync
### 1.1 Nya/utökade endpoints
Implementera i `backend/src/flyer-selection`:
- `POST /flyer-selections/receipt-match-preview`
- Input: kvittorader (normaliserad struktur), `weekKey` (optional), `sessionId` (optional)
- Output: matchförslag per kvittorad + confidence + reasonCodes + kandidat-selection
- Används för transparent UI-annotering före commit
- `POST /flyer-selections/receipt-match-commit`
- Input: samma payload + optional overrides
- Output: antal uppdaterade selections, listor över `bought`, `unmatched`, `ambiguous`
- Utför transaktionell statusuppdatering (`planned -> bought`)
- `GET /flyer-selections/open`
- Query: `weekKey`, `retailer`, pagination
- Returnerar öppna selections (`status=planned`) för snabb klienthämtning
Notering: Behåll befintliga CRUD-rutter under `/flyer-sessions/:sessionId/selections`.
### 1.2 Matchningsmotor (service-nivå)
I `FlyerSelectionService` lägg till en intern matcher med prioriterad strategi:
1. `productId` exact (högsta prio)
2. normalized name exact
3. alias/ordöverlap (token)
4. quantity/unit-stöd som förstärkning (inte ensam källa)
Regler:
- En `FlyerSelection` kan bara konsumeras en gång per commit.
- Confidence-trösklar:
- `>=0.90`: auto-commit-kandidat
- `0.70-0.89`: ambiguous (kräver override för commit)
- `<0.70`: unmatched
- Returnera alltid `matchedVia`, `confidence`, `reasonCodes`.
### 1.3 Datamodell-justeringar (om behövs)
Nuvarande schema räcker i stort, men planera följande icke-blockerande förbättringar:
- `FlyerSelection`:
- `boughtAt DateTime?`
- `boughtSource String?` (t.ex. `receipt_auto`, `receipt_manual`)
- `receiptImportBatchId String?` för spårbarhet
- Index:
- `(userId, status, updatedAt)` för snabb hämtning av öppna poster
Migration i separat steg efter kod.
### 1.4 Säkerhet och robusthet
- Validera alltid user-scope i alla nya endpoints.
- Rate-limit på match-endpoints (liknande befintlig throttle-nivå).
- Transaktion (`prisma.$transaction`) för commit så statusuppdatering blir atomisk.
- Idempotens: commit med samma `receiptImportBatchId` ska inte dubbeluppdatera.
---
## Fas 2: Flutter UX i `/import` (ny flyer-tab)
### 2.1 Lägg till `FlyerImportTab`
Uppdatera `ImportScreen` så tabbar blir:
1. Recept
2. Kvitto
3. Flyer
Skapa:
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`
- `flutter/lib/features/import/data/flyer_import_repository.dart`
- `flutter/lib/features/import/data/flyer_import_providers.dart`
- `flutter/lib/features/import/domain/flyer_item.dart`
- `flutter/lib/features/import/domain/flyer_selection.dart`
### 2.2 Flyer-tabens minimalklick-flöde
Steg i UI:
1. Upload flyerfil
2. Visa parserader med förvald checkbox för matchade varor
3. Primär CTA: `Planera markerade` (bulk-create/upsert)
4. Direkt visning av statuschips (`planned/bought/skipped`)
Klickoptimering:
- Förifyll `plannedQuantity/plannedUnit` från flyerdata.
- Batch-upsert selections i ett enda API-anrop.
- Visa inline varningar istället för modaler där möjligt.
### 2.3 Statusöversikt
I flyer-tabben visa sektioner:
- `Planerade` (öppna)
- `Nyligen köpta` (autouppdaterade från kvitto)
- `Ej matchade vid senaste kvitto` (för snabb manuell hantering)
---
## Fas 3: Integrera auto-sync i `ReceiptImportTab`
### 3.1 Hook efter receipt parse
I befintlig `_submit()` i `receipt_import_tab.dart`:
1. importera kvitto som idag
2. anropa `receipt-match-preview`
3. annotera UI-rader med matchstatus (icon/chip)
4. vid `Lägg till markerade`: anropa `receipt-match-commit` parallellt/sekventiellt med saveReceipt
### 3.2 Zero-click commit i happy path
Defaultbeteende vid `Lägg till markerade`:
- auto-committa alla matcher med confidence `>=0.90`
- lämna ambiguous som `planned`
- visa en kompakt snackbar:
- `2 planerade markerades som köpta`
- `1 kräver manuell kontroll`
Ingen extra dialog i standardfall.
### 3.3 Manuell override (endast vid behov)
Lägg till valfri expandrad i resultatlistan:
- “Föreslagen flyer-match” + knapp `Bekräfta ändå`
- används endast för ambiguous fall
---
## Fas 4: API- och UI-detaljer för låg friktion
### 4.1 Payload-standard (receipt -> matcher)
Standardisera kvittorad till:
- `rowId` (lokalt index eller UUID)
- `rawName`
- `normalizedName`
- `productId` (om redan mappad)
- `quantity`
- `unit`
- `price`
### 4.2 UX-copy
Konsekventa texter i UI:
- `Automatchad mot flyer`
- `Osäker matchning`
- `Ej matchad`
### 4.3 WeekKey-hantering
Fallback-ordning vid matchning:
1. explicit `sessionId`
2. explicit `weekKey`
3. server beräknar aktuell `weekKey`
Detta minimerar klientlogik och fel.
---
## Fas 5: Test och kvalitetssäkring
### 5.1 Backend
- Unit-tester för matcher-regler och confidence-nivåer
- Service-tester för commit-idempotens
- Controller-e2e för user-scope + throttling + felkoder
- Prisma-transaktionsscenarion (dubbelklick/duplicerat commit)
### 5.2 Flutter
- Widget-tester för:
- `FlyerImportTab` listning/bulk-planering
- kvitto-rad med automatch-chip
- Integrationstester för `ReceiptImportTab` + auto-sync callback
### 5.3 Acceptanskriterier (måste uppfyllas)
- Happy path kräver max 2 aktiva klick från planering till auto-bought.
- Minst 90% av high-confidence-matchningar autouppdateras korrekt i test-fixtures.
- Inga writes till `Product/Inventory` sker i flyer-planeringsfas.
---
## Fas 6: Gradvis lansering
1. Backend-endpoints bakom feature flag: `flyerReceiptAutoSyncEnabled`
2. Aktivera för intern/test-användare först
3. Mät:
- andel auto-match
- andel ambiguous
- manuell override-frekvens
4. Finjustera thresholds innan full rollout
---
## Implementeringsordning (konkret)
1. Backend: matcher + preview/commit endpoints
2. Backend: idempotens + spårbarhetsfält + migration
3. Flutter: ny `FlyerImportTab` + repository/providers
4. Flutter: integrera preview/commit i `ReceiptImportTab`
5. Tester backend + Flutter
6. Feature flag rollout
---
## Risker och mitigering
- Felmatchningar: håll konservativ tröskel och auto-commit bara vid hög confidence.
- Dubbla commits: idempotensnyckel + transaktion.
- UX-brus: visa detaljer först vid ambiguous, inte i happy path.
- Prestanda: batcha matchning och undvik N+1-frågor via prefetch av öppna selections.
---
## Definition of Done
- `/import` har en fungerande `FlyerImportTab`.
- Kvittoimport auto-synkar mot `FlyerSelection` med minimal friktion.
- `planned -> bought` uppdateras automatiskt för high-confidence.
- Ambiguous fall kan hanteras manuellt utan att blockera flödet.
- Tester gröna och feature flag klar för kontrollerad utrullning.
+125
View File
@@ -0,0 +1,125 @@
# Plan: Omgjord flyerimport (pdf-parse + tesseract + Mistral Tiny)
## Mål
Ersätta nuvarande flyerimportflöde (som idag delegerar till `importer-api`) med en robust pipeline som:
1) extraherar text från flyer-PDF (primärt `pdf-parse`, fallback OCR via Tesseract),
2) skickar normaliserad text till Mistral Tiny,
3) returnerar strikt strukturerad JSON,
4) behåller befintlig matchning/planeringsflöde i backend + Flutter men förbättrar UX kring importresultat och fel.
## Nulägesanalys (projektanpassad)
- Backend endpoint finns: `POST /flyer-import/parse` i `backend/src/flyer-import/flyer-import.controller.ts`.
- Nuvarande backendlogik i `backend/src/flyer-import/flyer-import.service.ts` anropar extern tjänst via `IMPORTER_SERVICE_URL` (`/api/flyer/parse`).
- Flutter har redan komplett flyerflik i `flutter/lib/features/import/presentation/flyer_import_tab.dart`:
- filval, importknapp, preview, radrendering med checkboxar, bulk-planering.
- Datamodell för sessions/items finns redan i Prisma (`FlyerSession`, `FlyerItem`, `FlyerSelection`) och stödjer parse+match-metadata.
- `flyerimporter.md` beskriver rätt riktning men är generisk; projektet behöver NestJS-integration och kompatibilitet med befintliga DTO/Flutter-modeller.
## Föreslagen arkitektur (ersättning av dagens lösning)
### 1) Ny intern parser i recipe-api (NestJS)
- Ersätt `parseViaImporter(...)` i `FlyerImportService` med lokal pipeline:
- `extractFlyerText(file)`
- PDF/text-extraktion via `pdf-parse`.
- Fallback OCR via Tesseract för sidor/underlag utan användbar text.
- `parseFlyerWithMistral(text)`
- Mistral Tiny-anrop med strikt JSON-schema-prompt.
- `normalizeFlyerItems(aiJson)`
- validering, typkonvertering, enhetsnormalisering, confidence/reasonCodes.
- Behåll resten av tjänsten intakt (matchning, sessionpersistens, selections-kompatibilitet).
### 2) AI-kontrakt (strikt JSON)
- Introducera explicit schema för AI-svar (intern typ + runtime-validering):
- `rawName`, `normalizedName`, `category`, `price`, `priceUnit`, `comparisonPrice`, `comparisonUnit`, `offerText`, `confidence`, `reasonCodes`.
- Promptdesign:
- svensk flyer-kontext,
- tydlig enhets- och prisnormalisering,
- "returnera ENDAST JSON" + exempel,
- fallback vid saknade fält (`null`, tomma listor).
- Robust parsing av modelloutput:
- ta bort ev. markdown fences,
- fail-fast med tydligt felmeddelande om ogiltigt JSON.
### 3) OCR-strategi
- Primärväg: `pdf-parse` (snabb, billig).
- OCR-fallback: bara när extraherad text är tom/under tröskel.
- Preprocess för OCR (vid behov): sidvis rasterisering + språk `swe` (ev. `swe+eng`).
- Timeout/guardrails per steg för att undvika låsta importer.
### 4) API/infra-anpassning
- Controller (`flyer-import.controller.ts`):
- uppdatera tillåtna MIME-typer så de matchar Flutter-filtyper (PDF + bilder om vi ska stödja bildflyers).
- `compose.yml`/env:
- gör `IMPORTER_SERVICE_URL` optional eller avveckla för flyerflödet.
- säkerställ `MISTRAL_API_KEY` används av `recipe-api` för flyer.
- Dokumentation:
- uppdatera teknisk beskrivning så flyerimport inte längre kräver extern flyer-parser.
## UX-analys Flutter (nuvarande) och planerade förbättringar
### Nuvarande UX (bra att bygga vidare på)
- Enkel 3-stegsinteraktion: välj fil -> importera -> markera/planera.
- Förhandsvisning finns och passar arbetsflödet.
- Offer-badge + pris/jämförpris + matchvisning ger snabb scanning.
### UX-gap att täppa till i denna implementation
- Ingen tydlig visning av parserwarnings från backend (fältet `warnings` finns i modellen).
- Ingen kvalitetssignal i UI trots att `parseConfidence/matchConfidence` finns.
- Felmeddelanden är relativt råa; saknar råd per feltyp (timeout, ogiltig fil, AI-svar oformaterat).
### Föreslagna UX-förbättringar (inkrementella, kompatibla)
1. Visa `warnings` över resultatlistan i en kompakt varningspanel.
2. Lägg till "kvalitetsindikator" per rad (t.ex. låg/medel/hög) baserat på `parseConfidence` + `matchConfidence`.
3. Lägg till filterchips: `Endast erbjudanden`, `Saknar matchning`, `Låg kvalitet`.
4. Förbättra loading-state med stegnära text ("Extraherar text", "Tolkar med AI", "Matchar produkter").
5. Felmappning till användarvänliga meddelanden i `showErrorDialog` (teknisk detalj i kopierbar sekundärtext).
## Implementationsplan (ordning)
### Fas A - Backend kärna
1. Lägg till dependencies i `backend/package.json` för PDF/OCR/Mistral-klient.
2. Skapa intern flyer-parser service i `backend/src/flyer-import/` (text extraction + AI parse).
3. Byt `parseViaImporter` till intern implementation i `FlyerImportService`.
4. Lägg till runtime-validering och normalisering av AI-svar.
### Fas B - Kontrakt och robusthet
5. Säkerställ att response-format fortsatt matchar `FlyerImportResponse` (ingen breaking change mot Flutter).
6. Förbättra controller MIME-regler så de stämmer med faktiska stödda format.
7. Lägg till tydliga felkoder/meddelanden för:
- tom/oläsbar flyer,
- AI-parsefel,
- timeout/service unavailable.
### Fas C - Flutter UX på befintlig skärm
8. Visa backend `warnings` i `flyer_import_tab.dart`.
9. Lägg till kvalitetsindikator + minimala filterchips.
10. Förfina loading/feltexter utan att ändra grundlayouten.
### Fas D - Verifiering
11. Backendtester för intern flyer-parser (happy path + fallback + felbanor).
12. Uppdatera/addera Flutter widgettester för warnings/indikator/filter.
13. Manuell E2E: PDF med text, PDF med skannade sidor, bildflyer, trasig fil.
## Filer som sannolikt berörs vid implementation
- `backend/src/flyer-import/flyer-import.service.ts`
- `backend/src/flyer-import/flyer-import.controller.ts`
- `backend/src/flyer-import/dto/flyer-import.response.ts` (endast om extra metadata behövs)
- `backend/package.json`
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`
- Ev. `flutter/lib/features/import/domain/flyer_import_item.dart` (om ny UI-metadata exponeras)
- Dokumentation: `TEKNISK_BESKRIVNING.md` (kort uppdatering av arkitektur)
## Risker och mitigering
- OCR-prestanda/latens: använd fallback-only och timeout.
- Mistral kan ge semistrukturerat svar: strikt schema + robust JSON-sanitizing + validering.
- Kostnad/kvot på AI-anrop: minimera promptstorlek, trunkera brus, återanvänd normalisering.
- Driftöverraskningar: behåll endpoint-kontrakt oförändrat mot Flutter.
## Acceptance criteria
- Flyerimport fungerar utan beroende av extern `/api/flyer/parse` i importer-api.
- Minst en PDF med inbäddad text och en skannad PDF importeras framgångsrikt.
- Backend returnerar valid `FlyerImportResponse` och befintlig planeringsfunktion fortsätter fungera.
- Flutter visar warnings och gör det tydligare vilka rader som behöver manuell granskning.
## Fastställt beslut
- Första leveransen ska stödja **PDF + bildfiler** (`png/jpg/webp`) fullt ut.
+152
View File
@@ -0,0 +1,152 @@
# Åtgärdsplan utifrån E2E-fynd (Flyer + Inventory)
## Mål
- Göra flyerflödet praktiskt användbart för jämförelse, redigering och planering till inköpslista.
- Säkerställa att inventory/pantry visar korrekta kategorier i stället för att allt hamnar i `Övrigt`.
## Scope (utifrån dina punkter)
1. Flyer-vy:
- Visa importerad flyer-PDF för jämförelse mot extraherade rader.
- Redigera poster (namn + kategori) med samma kategorikälla som products/pantry.
- `Planera X markerade` ska skapa en faktisk inköpslista i en egen flik.
2. Inventory:
- Felsöka och åtgärda varför poster visas under `Övrigt`.
---
## Nulägesbild (från kodbasen)
- Flutter har redan `FlyerImportTab` med:
- filval/import,
- checkboxar,
- `Planera X markerade` -> `POST /flyer-sessions/:id/selections/bulk`.
- PDF-visning finns idag bara för aktuell uppladdad fil i minnet (`_pickedFile.bytes`) och kan inte återöppnas säkert vid återställd session/app-omstart.
- Flyer-rader kan ännu inte redigeras i UI (ingen inline edit-dialog för flyer-item).
- Meal Plan har en shopping-sektion, men ingen dedikerad flik för flyer-planerade köp.
- Kategori-träd finns redan och används i flera vyer via `/categories/tree`.
- Inventory läser kategori via `product.categoryRef`; null blir `Övrigt` i UI.
---
## Genomförandeplan
### 1) Flyer-PDF: beständig förhandsvisning per session
**Backend**
- Lägg till lagring av originalfil för flyer-session (MVP: lokal filstore eller DB blob beroende på befintligt mönster i projektet).
- Utöka `flyer_session` metadata med filreferens (filnamn, mime, storlek, storageKey).
- Ny endpoint: `GET /flyer-import/sessions/:sessionId/source` (auth + ägarskap) som streamar PDF/bild.
**Flutter**
- I `FlyerImportTab`:
- använd befintlig local preview direkt efter uppladdning,
- när session återställs: hämta source-endpoint och visa `Visa flyer` även då.
- Behåll fallback-meddelande för plattformar som inte kan öppna PDF direkt.
**Klart-kriterium**
- Samma importerade flyer kan öppnas efter tab-byte och app-omstart för samma användare.
### 2) Redigering av flyer-poster (namn + kategori)
**Backend**
- Lägg till endpoint för uppdatering av flyer-item i session, t.ex.
- `PATCH /flyer-import/sessions/:sessionId/items/:itemId`
- fält: `rawName` (eller `displayName`) och `categoryId` (ev. `categoryHintPath` för visning).
- Validera att kategori finns i samma kategoriträd (`categories`).
- Ägarskapskontroll via sessionens `userId`.
**Flutter**
- I listan i `FlyerImportTab`: lägg till `Redigera`-action per rad.
- Edit-dialog:
- textfält för namn,
- kategori-väljare baserad på samma träd/komponentmönster som inventory/pantry/admin.
- Spara uppdatering till backend och uppdatera lokal/session state.
**Klart-kriterium**
- Användaren kan ändra namn och kategori på en flyer-rad och ser ändringen direkt i listan.
### 3) Flyer -> Inköpslista i egen flik
**Backend**
- Definiera enkel shopping-list-resurs för MVP (user-scoped):
- tabell t.ex. `shopping_list_item` (name/productId/categoryId/quantity/unit/source/status/userId).
- Ny endpoint för att skapa inköpsrader från flyer-selections:
- `POST /flyer-sessions/:sessionId/selections/plan-to-shopping-list`
- alternativt återanvänd bulk-create i shopping-modul.
- Deduplicering/regler:
- om samma `productId+unit` finns öppet: summera eller hoppa över (bestäms i implementation; rekommenderat: summera).
**Flutter**
- Lägg till ny flik/skärm `Inköpslista` i app-shell.
- `Planera X markerade` i flyer-vyn ska:
1) skapa/uppdatera flyer selections,
2) trigga backend-mappning till shopping-list,
3) visa snackbar med antal tillagda/uppdaterade rader.
- Inköpslista-vyn (MVP): lista rader + enkel check/avprickning.
**Klart-kriterium**
- Klick på `Planera X markerade` flyttar markerade flyer-produkter till Inköpslista-fliken.
### 4) Inventory-fel: allt i `Övrigt`
**Felsökning**
- Verifiera varför `product.categoryId/categoryRef` blir null i aktuella poster:
- skapade produkter utan kategori,
- importflöden som inte persistar vald kategori,
- äldre data utan backfill.
**Åtgärd**
- Säkerställ att produktskapande från import/edit alltid skickar/sätter kategori när sådan är vald.
- Lägg skydd i backend så kategori inte tappas vid update-flöden.
- Engångs-backfill för befintliga produkter utan kategori:
- använd befintlig kategoriseringslogik (regel/AI) + fallback till rimlig underkategori.
- Kör re-fetch/invalidations i Flutter inventory/pantry efter backfill.
**Klart-kriterium**
- Inventory/pantry visar blandade korrekta kategorier; endast okända poster ligger kvar i `Övrigt`.
---
## Testplan
### Backend
- Nya tester för:
- auth/ownership på source-endpoint och item-edit endpoint,
- validering av kategori-id,
- plan-to-shopping-list (antal skapade, dedupe, idempotens).
### Flutter widget/integration
- Flyer:
- render av `Visa flyer` efter restore,
- edit-dialog uppdaterar rad,
- `Planera X markerade` ger förväntad feedback.
- Inköpslista:
- ny flik syns,
- mottar rader från flyer.
- Inventory regression:
- kategori visas från product category path när satt,
- `Övrigt` endast fallback.
### E2E-checklista
- Importera flyer PDF -> öppna PDF -> redigera 2 rader -> planera markerade -> verifiera Inköpslista-fliken.
- Starta om app -> återöppna samma flyer-PDF -> verifiera ändringar kvar.
- Kontrollera inventory/pantry-kategorier efter backfill.
---
## Leveransordning (rekommenderad)
1. Inventory-kategori bugfix + backfill (snabbt värde, hög påverkan).
2. Flyer item-redigering (namn/kategori).
3. Inköpslista-flik + backend-mappning från flyer.
4. Beständig flyer-PDF source-visning (kan byggas parallellt med 2/3 om backendkapacitet finns).
---
## Risker och mitigering
- **Datamigrering/backfill-risk**: kör först mot staging + logga träffsäkerhet och antal fallback till `Övrigt`.
- **Dubbelposter i inköpslista**: inför tydlig dedupe-regel och testfall.
- **PDF-hantering per plattform**: behåll web-first öppning och tydligt fallback-meddelande där native viewer saknas.
- **Prestanda vid stora flyers**: paginera/virtuallista om UI blir tungt.
---
## Definition of Done
- Flyer-vyn har fungerande: PDF-visning, redigering av namn/kategori, planering till inköpslista.
- Inköpslista finns som separat flik och visar planerade flyer-rader.
- Inventory/pantry kategoriserar korrekt och `Övrigt` används endast som verklig fallback.
- Nya backend- och Flutter-tester gröna.
+369
View File
@@ -0,0 +1,369 @@
# Plan: Förbättrad dokumentationsstruktur för Recipe-app
## Bakgrund
Projektet har idag **20+ .md-filer** spridda över olika mappar, inklusive:
- **Aktiva filer** i rotmappen (t.ex. `TEKNISK_BESKRIVNING.md`, `NEXT_STEPS.md`).
- **Arkiverade filer** i `_archive/docs/` (t.ex. session-specifika anteckningar, gamla refaktoringsplaner).
- **Fragmenterade guider** (t.ex. `flyerimporter.md`, `filanalys.md`).
- **GitHub-specifika filer** (t.ex. `.github/copilot-instructions.md`).
**Problem**:
- **Svårt att hitta information**: Relaterat innehåll är splittrat (t.ex. Flutter-dokumentation i `_archive/docs/flutter/` vs. backend-dokumentation i rotmappen).
- **Föråldrat innehåll**: Arkiverade filer blandas med aktiva (t.ex. `SESSION_CHECKPOINT_2026-05-12.md`).
- **Icke-optimerat för språkmodeller**: Saknar tydlig hierarki och kontextuella länkar.
- **Duplicering**: Samma koncept (t.ex. "kategoriträd") dokumenteras på flera ställen.
---
## Mål med ny struktur
1. **Användarvänligt för utvecklare**:
- Logisk gruppering av innehåll (t.ex. "Backend", "Flutter", "Deploy").
- Tydliga **steg-för-steg-guider** för vanliga arbetsflöden.
- **Sökbart** med tydliga rubriker och nyckelord.
2. **Optimerat för språkmodeller (LLMs):
- **Kontextuell sammanhang**: Varje fil innehåller tillräcklig bakgrund för att förstå dess syfte.
- **Länkat innehåll**: Korsreferenser mellan filer för att ge fullständig bild.
- **Strukturera data**: Använd tabeller, listor och diagram för att göra informationen maskinläsbar.
3. **Underhållbart**:
- **Versionerat**: Tydlig separation mellan aktiv dokumentation och arkiv.
- **Modulärt**: Uppdateringar i en modul (t.ex. "Databas") påverkar inte andra.
- **Automatiseringsvänligt**: Filer som `CONTRIBUTING.md` och `README.md` kan genereras delvis från andra källor.
---
## Föreslagen struktur
```
/
├── docs/ # **Huvudkatalog för ALL aktiv dokumentation**
│ ├── 01-overview/ # Övergripande projektbeskrivning
│ │ ├── README.md # Huvudsaklig README (ersätter rot-README.md)
│ │ ├── ARCHITECTURE.md # Systemarkitektur (flytta från TEKNISK_BESKRIVNING.md)
│ │ └── GLOSSARY.md # Termer och definitioner (t.ex. "kategoriträd", "flyer session")
│ │
│ ├── 02-setup/ # Installation och konfiguration
│ │ ├── INSTALL.md # Miljökrav, beroenden, första uppstart
│ │ ├── CONFIG.md # Konfigurationsfiler (.env, Docker, etc.)
│ │ └── TROUBLESHOOTING.md # Vanliga problem och lösningar
│ │
│ ├── 03-development/ # Utvecklingsguider
│ │ ├── CONTRIBUTING.md # Bidragsregler, kodstandard, PR-process
│ │ ├── WORKFLOWS.md # Git-flöden, branch-strategi, CI/CD
│ │ ├── DATABASE.md # Schema, migrationer, seedning (flytta från TEKNISK_BESKRIVNING.md)
│ │ ├── API.md # Backend-API:er, Swagger-länkar, exempelanrop
│ │ ├── FLUTTER.md # Flutter-specifik dokumentation (flytta från _archive/docs/flutter/)
│ │ └── MICROSERVICES.md # Importer-AI, Todo-microservice, etc.
│ │
│ ├── 04-deploy/ # Driftsättning och underhåll
│ │ ├── DEPLOY.md # Steg-för-steg deploy (ersätter delar av TEKNISK_BESKRIVNING.md)
│ │ ├── MAINTENANCE.md # Underhållsskript, backup, monitorering
│ │ └── SCALING.md # Prestanda, skalning, lasttest
│ │
│ ├── 05-features/ # Djupdyk i funktioner
│ │ ├── RECIPE_IMPORT.md # Kvittosimport (ersätter flyerimporter.md)
│ │ ├── CATEGORY_TREE.md # Kategorihantering och L3-integration
│ │ ├── SHOPPING_LIST.md # Inköpslistor och flyer-integration
│ │ └── ... # Övriga funktioner (t.ex. måltidsplanering)
│ │
│ └── 06-archive/ # **Arkiverade dokument** (flytta hit från _archive/)
│ ├── sessions/ # Gamla sessionsanteckningar
│ ├── legacy/ # Föråldrade planer (t.ex. RECIPE_IMPORT_REFACTOR_PLAN.md)
│ └── ...
├── .github/ # GitHub-specifika filer
│ ├── COPILOT_INSTRUCTIONS.md # Flytta hit från .github/copilot-instructions.md
│ └── ...
└── ... # Övriga projektfiler (backend/, flutter/, etc.)
```
---
## Detaljerad filstruktur och innehåll
### 1. `docs/01-overview/`
| Fil | Syfte | Källor (befintliga filer) |
|-------------------|-----------------------------------------------------------------------|---------------------------------------------------|
| `README.md` | **Huvudsaklig ingress**: Projektbeskrivning, mål, snabbstart, länkar. | `README.md`, delar av `TEKNISK_BESKRIVNING.md` |
| `ARCHITECTURE.md` | **Systemarkitektur**: Komponentdiagram, databasrelationer, flöden. | `TEKNISK_BESKRIVNING.md` (avsnitt om arkitektur) |
| `GLOSSARY.md` | **Ordförklaringar**: Definitioner av projekt-specifika termer. | Ny fil |
**Exempelinnehåll för `ARCHITECTURE.md`**:
```markdown
# Systemarkitektur
```mermaid
graph TD
A[Flutter App] -->|HTTP/REST| B[Backend API]
B -->|Prisma Client| C[MariaDB]
B -->|gRPC| D[Importer Microservice]
D -->|HTTP| E[Externa API:er]
C -->|Seed| F[Initial Data]
```
## Komponenter
1. **Flutter App**:
- State management: Riverpod
- UI: Material Design + anpassade widgets
- Integreringar: Kamera (kvittoscan), PDF-rendering
2. **Backend API**:
- Ramverk: NestJS
- Databas: Prisma + MariaDB
- Autentisering: JWT
3. **Microservices**:
- Importer: Node.js + Puppeteer (för kvittosimport)
- AI: Python + Mistral (för kategorisering)
```
---
### 2. `docs/02-setup/`
| Fil | Syfte | Källor |
|-------------------|-----------------------------------------------------------------------|----------------------------------------------|
| `INSTALL.md` | **Miljösetup**: Krav, beroenden, första körning. | Delar av `README.md` och `TEKNISK_BESKRIVNING.md` |
| `CONFIG.md` | **Konfiguration**: `.env`-variabler, Docker-compose, nätverk. | `TEKNISK_BESKRIVNING.md` (avsnitt om miljö) |
| `TROUBLESHOOTING.md` | **Felsökning**: Vanliga fel, lösningar, debug-tips. | Ny fil |
**Exempelinnehåll för `INSTALL.md`**:
```markdown
# Installation
## Krav
| Komponent | Version | Notering |
|-----------------|---------------|-----------------------------------|
| Node.js | 24.15.0 | LTS-version rekommenderas |
| Docker | 24.x | Krävs för databas och microservices|
| Flutter | 3.41.9 | Kanal: stable |
| MariaDB | 11.x | Inkluderas via Docker-compose |
## Steg-för-steg
1. **Klona repo**:
```bash
git clone https://github.com/recipe-app/recipe-app.git
cd recipe-app
```
2. **Konfigurera miljö**:
```bash
cp .env.example .env
# Redigera .env (se CONFIG.md för detaljer)
```
3. **Starta tjänster**:
```bash
docker compose up -d
```
```
---
### 3. `docs/03-development/`
| Fil | Syfte | Källor |
|-------------------|-----------------------------------------------------------------------|----------------------------------------------|
| `CONTRIBUTING.md` | **Bidragsregler**: Kodstandard, PR-process, testkrav. | Ny fil (inspirerad av GitHub-standard) |
| `WORKFLOWS.md` | **Arbetsflöden**: Git-strategi, CI/CD, release-process. | `TEKNISK_BESKRIVNING.md` (avsnitt om Git) |
| `DATABASE.md` | **Databas**: Schema, migrationer, seedning, underhållsskript. | `TEKNISK_BESKRIVNING.md` + nya avsnitt |
| `API.md` | **Backend-API**: Endpoints, autentisering, exempelanrop. | Ny fil |
| `FLUTTER.md` | **Flutter-utveckling**: Widget-träd, state management, teman. | `_archive/docs/flutter/` |
| `MICROSERVICES.md`| **Microservices**: Importer, AI, kommunikation med backend. | Ny fil |
**Exempelinnehåll för `DATABASE.md`**:
```markdown
# Databas
## Schema
```mermaid
erDiagram
User ||--o{ Recipe : creates
User ||--o{ InventoryItem : owns
Category ||--o{ Product : "L3"
FlyerSession ||--o{ FlyerItem : contains
```
## Migrationer
### Standardflöde
1. Uppdatera `prisma/schema.prisma`.
2. Skapa migration:
```bash
npx prisma migrate dev --name add_feature_x
```
3. Testa lokalt:
```bash
npx prisma migrate reset
npx prisma db seed
```
### Underhållsskript
- **Rensa databas** (behåll kategorier):
```bash
./deploy.sh --clean-database
```
> Obs! Uppdatera `prisma/maintenance/clean-database.sql` när nya tabeller läggs till.
## Seedning
- **Initial data**: Laddas från `db/seeds/seed_all.sql`.
- **Kör seed**:
```bash
./deploy.sh --seed
```
```
---
### 4. `docs/04-deploy/`
| Fil | Syfte | Källor |
|-------------------|-----------------------------------------------------------------------|----------------------------------------------|
| `DEPLOY.md` | **Deploy-guider**: Steg-för-steg för staging/produktion. | `TEKNISK_BESKRIVNING.md` (avsnitt om deploy) |
| `MAINTENANCE.md` | **Underhåll**: Backup, monitorering, logghantering. | Ny fil |
| `SCALING.md` | **Skalning**: Prestandaoptimering, lasttest, caching. | Ny fil |
**Exempelinnehåll för `DEPLOY.md`**:
```markdown
# Driftsättning
## Miljöer
| Miljö | Domän | Syfte |
|-------------|---------------------|--------------------------------|
| Lokal | localhost:8080 | Utveckling |
| Staging | staging.app.com | Test перед prod |
| Produktion | app.recipe.com | Live-trafik |
## Steg-för-steg
1. **Bygg och pusha images**:
```bash
docker compose build
docker compose push
```
2. **Kör migrationer**:
```bash
./deploy.sh --backend --migrate
```
3. **Starta tjänster**:
```bash
docker compose up -d
```
## Vanliga flaggor
| Flagga | Beskrivning |
|-------------------|----------------------------------------------|
| `--migrate` | Kör Prisma-migrationer. |
| `--clean-database`| Rensar data (behåller kategorier). |
| `--seed` | Laddar initial data. |
```
---
### 5. `docs/05-features/`
| Fil | Syfte | Källor |
|-------------------|-----------------------------------------------------------------------|----------------------------------------------|
| `RECIPE_IMPORT.md`| **Kvittosimport**: Flyer-parsing, AI-kategorisering, lagring. | `flyerimporter.md` |
| `CATEGORY_TREE.md`| **Kategoriträd**: L3-integration, hierarki, synkronisering. | Delar av `TEKNISK_BESKRIVNING.md` |
| `SHOPPING_LIST.md`| **Inköpslistor**: Flyer-integration, kvantitetsberäkning, delning. | Ny fil |
**Exempelinnehåll för `RECIPE_IMPORT.md`**:
```markdown
# Kvittosimport
## Flöde
```mermaid
flowchart TD
A[Ladda upp PDF] --> B[Extrahera text]
B --> C[AI-kategorisering]
C --> D[Spara som FlyerSession]
D --> E[Mappa till inköpslista]
```
## API-endpoints
| Endpoint | Metod | Beskrivning |
|-----------------------------------|-------|--------------------------------------|
| `/api/flyer-sessions` | POST | Ladda upp och parsa PDF. |
| `/api/flyer-sessions/:id/items` | PATCH | Uppdatera produktnamn/kategori. |
| `/api/shopping-list/from-flyer` | POST | Konvertera flyer till inköpslista. |
## Underhåll
- **Uppdatera AI-modell**: Se `MICROSERVICES.md`.
- **Lägg till nya butiker**: Uppdatera `src/flyer-import/parsers/`.
```
---
### 6. `docs/06-archive/`
- **Syfte**: Flytta alla föråldrade filer hit, organiserade efter typ:
```
docs/06-archive/
├── sessions/ # Gamla sessionscheckpoints
│ ├── SESSION_2026-05-09_RECEIPT_IMPORT.md
│ └── ...
├── legacy/ # Föråldrade planer
│ ├── RECIPE_IMPORT_REFACTOR_PLAN.md
│ └── ...
└── flutter_legacy/ # Gamla Flutter-dokument
├── teknisk_beskrivning_flutter.md
└── ...
```
---
## Migrationsplan
### Steg 1: Rensa och organisera befintliga filer
| Åtgärd | Källfil(er) | Målfil |
|---------------------------------|---------------------------------------------|-----------------------------------------|
| Flytta och uppdatera | `TEKNISK_BESKRIVNING.md` | `docs/01-overview/ARCHITECTURE.md` |
| | `TEKNISK_BESKRIVNING.md` (deploy-avsnitt) | `docs/04-deploy/DEPLOY.md` |
| | `TEKNISK_BESKRIVNING.md` (databas-avsnitt) | `docs/03-development/DATABASE.md` |
| Flytta och slå samman | `flyerimporter.md` | `docs/05-features/RECIPE_IMPORT.md` |
| | `_archive/docs/flutter/*` | `docs/03-development/FLUTTER.md` |
| Arkivera | `_archive/docs/SESSION_*.md` | `docs/06-archive/sessions/` |
| | `MVP_CHECKLISTA.md` | `docs/06-archive/legacy/` |
| Uppdatera och flytta | `README.md` | `docs/01-overview/README.md` |
| | `.github/copilot-instructions.md` | `.github/COPILOT_INSTRUCTIONS.md` |
### Steg 2: Skapa nya filer
| Fil | Syfte |
|-----------------------------|-----------------------------------------------------------------------|
| `docs/03-development/CONTRIBUTING.md` | Standardiserade bidragsregler (branches, PR, code review). |
| `docs/03-development/API.md` | Dokumentation av backend-API:er (OpenAPI/Swagger-länkar). |
| `docs/03-development/MICROSERVICES.md` | Beskrivning av microservices (Importer, AI). |
| `docs/05-features/CATEGORY_TREE.md` | Djupdyk i kategorihantering och L3-integration. |
| `docs/04-deploy/MAINTENANCE.md` | Backup, monitorering, logghantering. |
### Steg 3: Lägg till kontext för språkmodeller
- **Länka filer**: Korsreferenser mellan relaterade ämnen (t.ex. länka från `DATABASE.md` till `DEPLOY.md` för migrationssteg).
- **Metadatarubriker**: Lägg till `## Syfte` och `## Målgrupp` i varje fil.
- **Diagram**: Använd Mermaid för att visualisera flöden och relationer.
- **Exempel**: Inkludera kopierbara kodblock för vanliga operationer.
---
## Fördelar med den nya strukturen
### För utvecklare
✅ **Lätt att hitta**: Logisk gruppering (t.ex. all Flutter-dokumentation på ett ställe).
✅ **Uppdaterad**: Arkiverade filer separerade från aktiv dokumentation.
✅ **Steg-för-steg**: Guider med tydliga exempel (t.ex. `INSTALL.md`).
### För språkmodeller (LLMs)
🤖 **Kontextuell förståelse**:
- Varje fil har ett tydligt syfte och målgrupp.
- Korsreferenser ger fullständig bild (t.ex. länka från `ARCHITECTURE.md` till `DATABASE.md`).
- Diagram och tabeller gör informationen maskinläsbar.
📚 **Sökbarhet**:
- Hierarkisk struktur (`01-overview/`, `02-setup/`, etc.) underlättar navigering.
- Nyckelord i rubriker (t.ex. "Flutter State Management" i `FLUTTER.md`).
🔗 **Länkat innehåll**:
- Relaterade ämnen länkas explicit (t.ex. "Se [DATABASE.md](../03-development/DATABASE.md) för schema").
---
## Nästa steg
1. **Godkänn planen**: Bekräfta att den föreslagna strukturen uppfyller kraven.
2. **Prioritera filer**: Vilka filer ska migreras först? (t.ex. `TEKNISK_BESKRIVNING.md` → flera nya filer).
3. **Implementera**: Skapa den nya strukturen och flytta innehåll stegvis.
+135
View File
@@ -0,0 +1,135 @@
# Plan: Separat Admin-AI huvudtabb + AI-insyn för Kvitto/Flyer
## Mål
- Flytta ut `AI` från `Admin > Databas` till en egen huvudtabb i admin (samma nivå som `Users` och `Database`).
- På nya `AI`-fliken visa två underflikar:
- `Kvitto` (AI-funktioner för receipt-import)
- `Flyer` (AI-funktioner för flyer-import)
- Ge admin insyn i:
- Prompt (vad modellen fick)
- Output (vad modellen returnerade)
- Per import/sessionshistorik
## Nuvarande läge (verifierat)
- Huvudtabbar i admin definieras i:
- `flutter/lib/features/admin/presentation/admin_screen.dart`
- `flutter/lib/core/ui/app_shell.dart`
- `AI` ligger idag som intern databas-tab i:
- `flutter/lib/features/admin/presentation/admin_database_panel.dart`
- Nuvarande `AdminAiPanel` visar bara modellinfo:
- `flutter/lib/features/admin/presentation/admin_ai_panel.dart`
- Flyer har sessions-API och sparar parsat resultat, men ingen API-yta för prompt/output:
- `backend/src/flyer-import/flyer-import.controller.ts`
- `backend/src/flyer-import/dto/flyer-import.response.ts`
- Receipt-import har ingen sessionhistorik i recipe-api och ingen prompt/output i svar:
- `backend/src/receipt-import/receipt-import.controller.ts`
- `backend/src/receipt-import/dto/parsed-receipt-item.dto.ts`
## UX-förslag (enkelt och snyggt)
- Ny huvudflik `AI` i admin med tydlig struktur:
- Topp: två chips/segmenterade knappar `Kvitto` och `Flyer`
- Innehåll: 2-kolumnslayout på desktop, enkel stack på mobil
- Vänster: lista med senaste importer (tid, användare, fil, status)
- Höger: detaljer för vald import
- Detaljvyn har tre kort:
1. **Prompt** (monospace, copy-knapp, expand/collapse)
2. **Model Output** (formatterad JSON, copy-knapp)
3. **Sammanfattning** (modell, duration, chunk/retry, warnings)
- Filter högst upp:
- Källa: Kvitto/Flyer
- Period: senaste 24h / 7d / 30d
- Endast fel
- Default-beteende:
- Välj senaste import automatiskt
- Visa läs-only data (ingen redigering av prompt i denna iteration)
## Föreslagen implementation
### 1) Flutter: ny Admin-huvudtabb AI
1. Utöka enum och query-hantering:
- `AdminViewTab` får `ai`
- query-stöd: `?tab=ai`
2. Uppdatera admin-title chips i `AppShell`:
- Lägg till `AI` bredvid `Användare` och `Databas`
3. Rendra ny panel i `AdminScreen`:
- `AdminAiPanel` blir panel för huvudtabben
4. Ta bort AI från `AdminDatabasePanel` interna tabs
Berörda filer:
- `flutter/lib/features/admin/presentation/admin_screen.dart`
- `flutter/lib/core/ui/app_shell.dart`
- `flutter/lib/features/admin/presentation/admin_database_panel.dart`
### 2) Flutter: bygg om `AdminAiPanel` till AI-observability
1. Inför underflikar `Kvitto` / `Flyer`
2. Lägg till vänster lista + höger detalj
3. Lägg till komponenter:
- `PromptCard` (text/copy)
- `OutputJsonCard` (pretty JSON/copy)
- `TraceMetaCard` (modell, tid, status)
4. Lägg till `adminRepository`-metoder för att hämta traces
Berörda filer:
- `flutter/lib/features/admin/presentation/admin_ai_panel.dart`
- `flutter/lib/features/admin/data/admin_repository.dart`
- `flutter/lib/core/api/api_paths.dart`
- ev. nya domänmodeller under `flutter/lib/features/admin/domain/`
### 3) Backend: exponera AI trace-data (admin-only)
1. Lägg till admin-endpoints för trace-lista + detalj
2. Returnera prompt/output samt metadata
3. Begränsa åtkomst till admin
Föreslagen API-yta:
- `GET /ai/traces?source=receipt|flyer&limit=...&cursor=...`
- `GET /ai/traces/:id`
### 4) Datalagring för prompt/output
För stabil UX behövs persistens, inte bara loggar.
Föreslagen modell:
- Ny Prisma-tabell `AiTrace`
- `id`, `source` (`receipt`/`flyer`), `userId`, `sessionId?`, `model`, `prompt`, `rawOutput`, `normalizedOutput`, `status`, `error`, `durationMs`, `createdAt`
Integration:
- Flyer: skapa trace i `AiFlyerParserService` vid varje AI-anrop/chunk (sammanfatta till en sessionsrad eller flera child-rader)
- Receipt: recipe-api behöver trace från importer-service eller egen instrumentation av prompt/output
## Viktig teknisk avgränsning (receipt)
- I nuvarande repo byggs flyer-prompt i `recipe-api` och är enkel att visa.
- Receipt-AI sker i importer-flöde; prompt/output finns sannolikt inte i recipe-api idag.
- Därför två realistiska steg:
1. **Steg 1 (snabbt):** full trace-visning för Flyer + modellinfo för Kvitto
2. **Steg 2:** utöka receipt/importer så prompt/output skickas eller lagras som trace och visas i samma UI
## Säkerhet
- Endast admin får läsa traces.
- Prompt/output kan innehålla känslig text från uppladdade filer:
- visa som read-only
- möjlighet till maskning av persondata i senare steg
- paginering + kort retention (t.ex. 30 dagar) rekommenderas
## Testplan
- Flutter widget-test:
- Ny huvudtabb `AI` syns och route `?tab=ai` fungerar
- Underflikar `Kvitto`/`Flyer` växlar korrekt
- Prompt/output renderas och copy-knappar fungerar
- Backend tester:
- admin-only behörighet på trace-endpoints
- lista/detalj svarar korrekt
- trace skapas för flyer parse-flöde
- Regression:
- Admin `Users`/`Database` fungerar oförändrat
## Genomförandeordning
1. Flytta AI till huvudtabb i Flutter (utan backend-ändring först)
2. Bygg ny AI-panelstruktur med underflikar
3. Inför backend trace-endpoints + Prisma-migration
4. Koppla Flyer trace end-to-end
5. Koppla Receipt trace (beroende på importer-instrumentation)
6. Sluttest + docs
## Rekommenderat beslut inför implementation
- Implementera i två faser för låg risk:
- Fas A: Ny UX + full Flyer-insyn direkt
- Fas B: Receipt prompt/output när importer-trace är tillgänglig
+154
View File
@@ -0,0 +1,154 @@
# Plan: Implementera automatiserad datarensning för AiTrace
## Mål
Implementera en automatiserad rensning av gamla `AiTrace`-poster för att säkerställa att känsliga data inte lagras längre än nödvändigt och för att följa GDPR-krav.
## Bakgrund
- `AiTrace`-tabellen lagrar känsliga data (t.ex. maskerade promptar och AI-svar) utan någon retention-policy.
- Risk för att data ackumuleras obehindrat, vilket kan leda till lagringsproblem och GDPR-brott.
- Enligt GDPR bör känsliga data rensas efter en viss tid om de inte längre behövs för felsökning eller analys.
## Krav
1. Rensa `AiTrace`-poster äldre än 30 dagar.
2. Schemalägg rensningen att köra dagligen vid midnatt.
3. Logga antalet rensade poster för övervakning.
4. Säkerställa att rensningen inte påverkar systemets prestanda.
## Implementeringsplan
### Steg 1: Installera beroenden
Installera `NestJS Schedule`-modulen för att möjliggöra schemalagda jobb.
**Kommando:**
```bash
npm install @nestjs/schedule
```
### Steg 2: Konfigurera ScheduleModule
Lägg till `ScheduleModule` i `AppModule` för att aktivera schemalagda jobb.
**Fil:** `backend/src/app.module.ts`
**Ändring:**
```typescript
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [ScheduleModule.forRoot()],
})
export class AppModule {}
```
### Steg 3: Skapa AiTraceCleanupService
Skapa en ny tjänst för att hantera rensningen av `AiTrace`-poster.
**Fil:** `backend/src/ai/ai-trace-cleanup.service.ts`
**Innehåll:**
```typescript
import { Injectable, Logger } from '@nestjs/common';
import { Cron, CronExpression } from '@nestjs/schedule';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class AiTraceCleanupService {
private readonly logger = new Logger(AiTraceCleanupService.name);
constructor(private readonly prisma: PrismaService) {}
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
async cleanupOldTraces() {
this.logger.log('Starting cleanup of old AiTrace records...');
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const result = await this.prisma.aiTrace.deleteMany({
where: {
createdAt: {
lt: thirtyDaysAgo,
},
},
});
this.logger.log(`Cleaned up ${result.count} old AiTrace records.`);
}
}
```
### Steg 4: Registrera AiTraceCleanupService
Lägg till `AiTraceCleanupService` i `AiTraceModule` för att aktivera tjänsten.
**Fil:** `backend/src/ai/ai-trace.module.ts`
**Ändring:**
```typescript
import { AiTraceCleanupService } from './ai-trace-cleanup.service';
@Module({
providers: [AiTraceService, AiTraceCleanupService],
})
export class AiTraceModule {}
```
### Steg 5: Testa rensningen manuellt
Skapa en testmetod för att manuellt köra rensningen och verifiera att den fungerar som förväntat.
**Fil:** `backend/src/ai/ai-trace.controller.ts`
**Ändring:**
```typescript
import { AiTraceCleanupService } from './ai-trace-cleanup.service';
@Controller('ai/traces')
export class AiTraceController {
constructor(
private readonly aiTraceService: AiTraceService,
private readonly aiTraceCleanupService: AiTraceCleanupService,
) {}
@Post('cleanup')
@Roles('admin')
async manualCleanup() {
await this.aiTraceCleanupService.cleanupOldTraces();
return { success: true, message: 'Manual cleanup completed.' };
}
}
```
### Steg 6: Verifiera implementationen
1. Skapa några testposter i `AiTrace`-tabellen med olika `createdAt`-datum.
2. Kör den manuella rensningen via `POST /api/ai/traces/cleanup`.
3. Verifiera att endast poster äldre än 30 dagar har rensats.
4. Kontrollera loggarna för att säkerställa att antalet rensade poster loggas korrekt.
### Steg 7: Dokumentera ändringarna
Uppdatera `TEKNISK_BESKRIVNING.md` med information om den automatiserade rensningen.
**Fil:** `TEKNISK_BESKRIVNING.md`
**Ändring:**
```markdown
## Automatiserad datarensning för AiTrace
För att säkerställa att känsliga data inte lagras längre än nödvändigt, har en automatiserad rensning implementerats:
- **Rensningsintervall**: Dagligen vid midnatt.
- **Retention-period**: 30 dagar.
- **Loggning**: Antalet rensade poster loggas för övervakning.
### Implementation
- **Tjänst**: `AiTraceCleanupService` i `backend/src/ai/ai-trace-cleanup.service.ts`.
- **Schemaläggning**: Använder `@nestjs/schedule` för att köra rensningen dagligen.
- **Manuell rensning**: Tillgänglig via `POST /api/ai/traces/cleanup` (kräver admin-behörighet).
### GDPR-efterlevnad
- Rensningen säkerställer att känsliga data raderas efter 30 dagar, vilket uppfyller GDPR-krav på dataminimering.
- Användare kan begära att deras data raderas tidigare via admin-panelen.
```
## Frågor och överväganden
1. **Retention-period**: Är 30 dagar en lämplig period, eller bör den justeras baserat påelsökningsbehov?
2. **Loggning**: Bör loggarna för rensningen sparas längre för revisionsändamål?
3. **Prestanda**: Bör rensningen köra under lågtrafikperioder för att minimera påverkan på systemet?
## Nästa steg
1. Implementera planen enligt ovan.
2. Testa rensningen i en testmiljö.
3. Verifiera att loggarna är korrekta och att rensningen fungerar som förväntat.
4. Dokumentera ändringarna i `TEKNISK_BESKRIVNING.md`.
5. Informera teamet om den nya funktionaliteten och dess påverkan på GDPR-efterlevnaden.
+215
View File
@@ -0,0 +1,215 @@
# Plan: Implementera användarinitierad radering av personuppgifter
## Mål
Skapa en tydlig och stegvis plan för hur användare kan ta bort sina personuppgifter från sin profil på plattformen, inklusive teknisk implementation och användarflöde.
## Bakgrund
- GDPR kräver att användare ska kunna begära radering av sina personuppgifter.
- Nuvarande plattform saknar ett tydligt användarflöde för att initiera radering av personuppgifter.
- Användare bör kunna ta bort sin profil och associerade data på ett säkert och transparent sätt.
## Krav
1. Lägg till en "Ta bort min profil"-knapp i användarens profilinställningar.
2. Implementera en bekräftelsedialog för att förhindra oavsiktlig radering.
3. Skapa en backend-endpoint för att hantera raderingsbegäran.
4. Se till att all användardata (profil, produkter, recept, etc.) raderas eller anonymiseras.
5. Logga raderingsbegäran för revisionsändamål.
6. Skicka ett bekräftelsemeddelande till användaren efter radering.
## Implementeringsplan
### Steg 1: Lägg till "Ta bort min profil"-knapp i Flutter UI
Lägg till en knapp i användarens profilinställningar som låter användaren initiera radering av sin profil.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
ElevatedButton(
onPressed: () {
_showDeleteProfileConfirmation();
},
child: Text('Ta bort min profil'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
)
```
### Steg 2: Implementera bekräftelsedialog
Skapa en dialog som kräver att användaren bekräftar raderingen.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
Future<void> _showDeleteProfileConfirmation() async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Bekräfta radering'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Är du säker på att du vill ta bort din profil?'),
Text('Alla dina data kommer att raderas permanent.'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('Avbryt'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Ta bort'),
onPressed: () {
_deleteProfile();
Navigator.of(context).pop();
},
),
],
);
},
);
}
```
### Steg 3: Skapa backend-endpoint för radering
Implementera en endpoint som hanterar raderingsbegäran och raderar all associerad data.
**Fil:** `backend/src/users/users.controller.ts`
**Ändring:**
```typescript
@Delete('me')
@UseGuards(JwtAuthGuard)
async deleteProfile(@CurrentUser() user: UserEntity) {
await this.usersService.deleteUserAndData(user.id);
return { success: true, message: 'Din profil och data har tagits bort.' };
}
```
### Steg 4: Implementera raderingslogik i UsersService
Skapa en metod som raderar användaren och all associerad data.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async deleteUserAndData(userId: number) {
await this.prisma.$transaction([
this.prisma.product.deleteMany({ where: { ownerId: userId } }),
this.prisma.recipe.deleteMany({ where: { ownerId: userId } }),
this.prisma.inventoryItem.deleteMany({ where: { userId } }),
this.prisma.mealPlanEntry.deleteMany({ where: { userId } }),
this.prisma.user.delete({ where: { id: userId } }),
]);
this.logger.log(`User ${userId} and associated data deleted.`);
}
```
### Steg 5: Logga raderingsbegäran
Lägg till loggning för att spåra raderingsbegäran för revisionsändamål.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async logDeletionRequest(userId: number, userEmail: string) {
await this.prisma.auditLog.create({
data: {
action: 'USER_DELETION',
userId,
email: userEmail,
metadata: { message: 'User initiated deletion of their profile and data.' },
},
});
}
```
### Steg 6: Skicka bekräftelsemeddelande
Skicka ett e-postmeddelande till användaren för att bekräfta raderingen.
**Fil:** `backend/src/users/users.service.ts`
**Ändring:**
```typescript
async sendDeletionConfirmationEmail(email: string) {
await this.emailService.sendEmail({
to: email,
subject: 'Bekräftelse på radering av din profil',
text: 'Din profil och alla associerade data har tagits bort från vår plattform.',
});
}
```
### Steg 7: Uppdatera Flutter för att anropa backend-endpoint
Implementera metoden för att anropa backend-endpoint för radering.
**Fil:** `flutter/lib/features/profile/presentation/profile_screen.dart`
**Ändring:**
```dart
Future<void> _deleteProfile() async {
try {
final response = await ApiService.delete('/users/me');
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Din profil har tagits bort.'));
);
Navigator.of(context).pushNamedAndRemoveUntil('/login', (route) => false);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ett fel uppstod vid radering av din profil.'));
);
}
}
```
### Steg 8: Testa implementeringen
1. Skapa en testanvändare i systemet.
2. Navigera till profilinställningar och klicka på "Ta bort min profil".
3. Bekräfta raderingen i dialogrutan.
4. Verifiera att användaren och all associerad data har tagits bort från databasen.
5. Kontrollera att ett bekräftelsemeddelande har skickats till användarens e-post.
6. Verifiera att raderingsbegäran har loggats i `AuditLog`.
### Steg 9: Dokumentera ändringarna
Uppdatera `TEKNISK_BESKRIVNING.md` med information om den nya funktionaliteten.
**Fil:** `TEKNISK_BESKRIVNING.md`
**Ändring:**
```markdown
## Användarinitierad radering av personuppgifter
För att uppfylla GDPR-krav har en funktion implementerats som låter användare ta bort sin profil och associerade data:
- **Användarflöde**: Användaren kan initiera radering via en knapp i profilinställningarna.
- **Bekräftelse**: En dialog kräver bekräftelse för att förhindra oavsiktlig radering.
- **Backend-endpoint**: `DELETE /users/me` hanterar raderingsbegäran.
- **Data som raderas**: Profil, produkter, recept, inventarieposter och matplaner.
- **Loggning**: Raderingsbegäran loggas i `AuditLog` för revisionsändamål.
- **Bekräftelse**: Ett e-postmeddelande skickas till användaren efter radering.
### Implementation
- **Frontend**: `flutter/lib/features/profile/presentation/profile_screen.dart`
- **Backend**: `backend/src/users/users.controller.ts` och `backend/src/users/users.service.ts`
- **Loggning**: `AuditLog`-poster skapas för varje raderingsbegäran.
- **E-postbekräftelse**: Skickas via `EmailService`.
### GDPR-efterlevnad
- Användare har full kontroll över sina personuppgifter.
- Raderingsprocessen är transparent och dokumenterad.
- All data raderas eller anonymiseras enligt GDPR-krav.
```
## Frågor och överväganden
1. **Dataretention**: Bör vissa data (t.ex. transaktionshistorik) sparas för revisionsändamål även efter radering?
2. **Anonymisering**: Bör vi anonymisera data istället för att radera dem helt?
3. **Ångerperiod**: Bör vi implementera en ångerperiod där användaren kan återställa sin profil inom en viss tid?
## Nästa steg
1. Implementera planen enligt ovan.
2. Testa funktionaliteten i en testmiljö.
3. Verifiera att all data raderas korrekt och att loggning fungerar.
4. Dokumentera ändringarna i `TEKNISK_BESKRIVNING.md`.
5. Informera teamet om den nya funktionaliteten och dess påverkan på GDPR-efterlevnaden.
@@ -0,0 +1,146 @@
# Plan: Projektanpassad Lighthouse-plan for Flutter-web
## Mål
Höja Lighthouse-resultaten för Flutter-webklienten i detta repo utan att bryta befintlig Docker/Caddy-deploy, med fokus på mätbar förbättring av prestanda, tillgänglighet och grundläggande SEO.
## Kontext och nuläge (verifierat i projektet)
- Flutter-web byggs redan i release-läge i `flutter/Dockerfile` via `flutter build web --release`.
- Webb klient körs via Caddy i `flutter/Caddyfile` och har redan `encode gzip`.
- `flutter/web/index.html` är minimal och innehåller redan korrekt viewport utan `user-scalable=no`.
- `flutter/web/` innehåller idag endast `index.html` (ingen `robots.txt`/`sitemap.xml` i Flutter-mappen).
- API-basurl injiceras redan korrekt med `--dart-define=API_BASE_URL=/api` via `compose.flutter.yml`.
## Problem i befintlig flutter-lighthouse.md som inte passar repo exakt
- Planen utgår delvis från Nginx/Apache, men projektet använder Caddy för Flutter-spåret.
- Påståendet om `user-scalable=no` gäller inte nuvarande `flutter/web/index.html`.
- Påståendet om ogiltig `robots.txt` kan inte verifieras i nuvarande Flutter-webmapp.
- Förslag om tvingad HTML-renderer är för grovt; bör vara experiment med mätning och rollback-kriterier.
## Strategi
Arbeta i tre iterationer med baseline -> lågriskoptimeringar -> tyngre optimeringar, och låt varje steg vara datadrivet.
---
## Fas 1: Baseline och mätprotokoll (dag 1)
1. Skapa en reproducerbar mätbaseline för både lokal container och produktionsdomän.
2. Kör Lighthouse minst 3 gånger per miljö och ta median för:
- Performance score
- LCP
- TBT
- INP (om rapporteras)
- Transfer size / antal requests
3. Dokumentera nuläge och tröskelvärden före ändringar.
### Acceptanskriterier Fas 1
- Baseline-tabell finns med mätvärden från 3 körningar per miljö.
- Samma URL, samma nätprofil och samma emulering används konsekvent.
---
## Fas 2: Lågriskfixar med hög nytta (dag 1-2)
### 2.1 Index och metadata (`flutter/web/index.html`)
- Lägg till `lang="sv"``<html>`.
- Lägg till relevant `meta description` för appens huvudsakliga nytta.
- Behåll `viewport` som den är (ingen ändring behövs kring zoom-blockering).
### 2.2 Caddy-headerhygien (`flutter/Caddyfile`)
- Behåll `encode gzip`.
- Lägg till explicita cache-headers för hashade statiska assets (js/wasm/fonts) med lång TTL och immutable.
- Lägg till kortare/konservativ cache för `index.html` så nya deploys slår igenom snabbt.
- Lägg till säkerhetsheaders som är kompatibla med Flutter-web (minst grundnivå: `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`).
### 2.3 Byggoptimering (`flutter/Dockerfile`)
- Utvärdera `--no-source-maps` i produktionsbuild för mindre artifact-storlek.
- Säkerställ att eventuella ändringar inte påverkar felsökning i miljö där sourcemaps behövs.
### Acceptanskriterier Fas 2
- Lighthouse visar förbättring i minst två nyckelmått (t.ex. LCP/TBT).
- Ingen regress i appstart, routning eller API-proxy `/api/*`.
- Build pipeline passerar oförändrat i Docker.
---
## Fas 3: Prestandaexperiment med tydlig rollback (dag 2-4)
### 3.1 Rendering-strategi (CanvasKit vs HTML/Skwasm)
- Kör A/B-test av rendererstrategi i en separat branch/buildvariant.
- Mät skillnad i initial transfer size, LCP och renderingkvalitet på kritiska vyer.
- Beslut endast baserat på mätdata + visuell/regressionskontroll.
### 3.2 Deferred loading i tunga featureflöden
- Identifiera kandidater för deferred imports (exempel: import/admin-vyer med hög kodvikt).
- Introducera gradvis och validera att navigation inte blir ryckig.
### 3.3 Bootstrap-laddning
- Behåll asynkron laddning i `index.html` om mätning visar bäst resultat.
- Undvik manuella hacks som injicerar canvaskit-script ad hoc utan evidens i mätning.
### Acceptanskriterier Fas 3
- Minst 15-25% förbättring i TBT eller tydlig minskning i JS-exekveringstid jämfört med baseline.
- Ingen funktionell regress i kärnflöden (login, inventarie, recept, import).
---
## Fas 4: Tillgänglighet (A11y) med repo-fokus (dag 3-5)
1. Inventera ikonknappar och actions i Flutter-kod och säkra `tooltip`/`semanticsLabel` där det saknas.
2. Lägg till/justera `Semantics` för centrala actions (import, spara, ta bort, navigering).
3. Verifiera tangentbordsnavigering i webbläsare för huvudflöden.
### Acceptanskriterier Fas 4
- Lighthouse Accessibility förbättras mätbart.
- Inga nya fokusfällor eller förlorad keyboard-navigering introduceras.
---
## Fas 5: SEO-minimum för app-shell (dag 4-5)
1. Säkerställ att titel och meta description är korrekta för startsidan.
2. Besluta om `robots.txt` och `sitemap.xml` ska hanteras i Flutter-web, Caddy eller upstream-domänkonfiguration (inte antagande).
3. Implementera endast den väg som matchar faktisk domänrouting i drift.
### Acceptanskriterier Fas 5
- Lighthouse SEO får förbättring från baseline.
- Robots/sitemap-lösning är verifierad mot faktisk driftarkitektur.
---
## Fas 6: Säkerhet och CSP (dag 5)
1. Introducera en pragmatisk CSP för Flutter-web i Caddy med minsta nödvändiga undantag.
2. Testa särskilt att Flutter bootstrap, API-anrop och ev. externa resurser fungerar.
3. Strama åt policyn iterativt istället för en aggressiv engångspolicy.
### Acceptanskriterier Fas 6
- Säkerhetsheaders levereras korrekt från Flutter-Caddy.
- Ingen blockerad kärnfunktion pga CSP.
---
## Prioriterad genomförandeordning
1. Fas 1 (baseline)
2. Fas 2 (lågriskfixar)
3. Re-mätning
4. Fas 3 (prestandaexperiment)
5. Fas 4 (tillgänglighet)
6. Fas 5 (SEO-minimum)
7. Fas 6 (CSP hardening)
8. Slutlig Lighthouse-jämförelse och dokumentation
## Definition of Done
- Reproducerbar före/efter-mätning finns dokumenterad.
- Performance, Accessibility och SEO har förbättrats jämfört med baseline.
- Inga regressioner i Docker/Caddy-flödet eller appens kärnflöden.
- Åtgärderna är anpassade till faktisk stack (Flutter + Caddy), inte generiska Nginx-råd.
## Konkreta filer som sannolikt berörs vid implementation
- `flutter/web/index.html`
- `flutter/Caddyfile`
- `flutter/Dockerfile`
- ev. flera Flutter-vyer med knapp-/ikonsemantik under `flutter/lib/**`
## Risker och motåtgärder
- **Risk:** För aggressiv CSP bryter Flutter-bootstrap.
**Motåtgärd:** Iterativ policy + verifiering efter varje ändring.
- **Risk:** Rendererbyte förbättrar vikt men försämrar visual fidelity.
**Motåtgärd:** A/B-test med tydliga rollback-kriterier.
- **Risk:** Cache-policy gör deploys "stale".
**Motåtgärd:** Lång cache endast för fingerprintade assets, kort cache för `index.html`.
+346
View File
@@ -0,0 +1,346 @@
# Plan: Flyerimport - specialtecken, beskrivande felmeddelanden, synlig prompt
## Mål
Tre sammanhängande förbättringar av flyerimport-pipelinen så att användaren får begripligt feedback och korrekt data:
1. Säkerställ att svenska tecken (ä, å, ö, é) bevaras i produktnamn som "Prästost", "Herrgårdsost", "Grevéost".
2. Ersätt opaka koder som `parse:ai_parsed` och `match:no_match` med människovänliga svenska förklaringar som beskriver "vad" som hände och "var" det hände.
3. Visa promptens innehåll (inte bara output) för operatörer/admins och vid behov i importflödet vid varningar.
Allt arbete ska respektera nuvarande arkitektur: NestJS backend (`backend/src/flyer-import`, `backend/src/ai`), Flutter web frontend (`flutter/lib/features/import`, `flutter/lib/features/admin`).
---
## Verifierad nulägesbild (källkodsbevis)
### Specialtecken
- `ai-flyer-parser.service.ts:189-293` skickar prompt utan diakritiska tecken (skriver "Prastost", "Herrgardsost", "Greveost", "ARLA KO", "ä" undviks medvetet i prompt-instruktionerna). Detta gör att modellen tränas/uppmuntras att returnera namnen utan ä/å/é.
- `ai-flyer-parser.service.ts:358-364` (`normalizeName`) tar bort allt som inte är `[a-zåäö0-9\s]` men `rawName` skickas vidare som det är, så ä/å bevaras tekniskt om AI returnerar dem.
- `flyer-normalizer.service.ts:26-30` har en hårdkodad mappning för cheese variants:
- `prast: 'Prästost'`
- `herrgard: 'Herrgårdsost'`
- `greve: 'Greveost'` (saknar `é`! Bör vara `Grevéost`)
- `flyer-normalizer.service.ts:196-227` (`expandCheeseVariants`) gör `stripDiacritics` på rawName innan den jämför med token-list. Detta funkar för matchning men producerar diakrit-fria varianter om mappningen inte är korrekt.
- `flyer-normalizer.service.ts:140-146` (`normalizeName`) fungerar med `[a-zåäö0-9\s]` så svenska tecken behålls i normaliserat namn när inputen har dem.
### "parse:ai_parsed" och "match:no_match"
- Konstanter genereras som "reason codes" i koden:
- `ai-flyer-parser.service.ts:354`: `reasonCodes: ['ai_parsed']` - sätts på alla AI-parsade items.
- `flyer-import.service.ts:496`: `reasons: ['no_match']` - sätts när inget produktnamn matchar.
- Reason codes prefixas med `parse:` eller `match:` av `ai-trace.service.ts:435-452` (`collectWarnings`):
```ts
warnings.add(`parse:${text}`);
warnings.add(`match:${text}`);
```
- Dessa visas i admin AI-traces-vyn (`admin_ai_panel.dart:355-362`, `_WarningsCard`) och möjligen i import-UI:t via `_buildWarningsPanel` (`flyer_import_tab.dart:497-539`) om `_result.warnings` innehåller dem.
- Slutsats: koderna är inte fel - de är obegripliga utan översättning.
### Promptens synlighet
- Admin AI-panelen (`admin_ai_panel.dart:444-508`, `_PromptCard`) visar redan prompt med expand/copy-knappar.
- Importflödet (`flyer_import_tab.dart`, `receipt_import_tab.dart`) visar idag bara `warnings`-listan. Ingen promptvisning, ingen mappning från reason codes till begripligt språk, ingen länk till AI-trace.
- AI-trace lagras alltid i DB (`flyer-import.service.ts:148-164`, `persistFlyerTrace`). Promptens innehåll finns alltså redan tillgängligt.
---
## Strategi och faser
Tre paralleliserbara delprojekt med varsin egen fas. Slutlig sammanflätning verifieras med befintliga tester och ny smoke-test.
---
## Fas A: Specialtecken i produktnamn (lågriskfix, hög nytta)
### A1. Korrigera hårdkodad cheese-mappning
- `backend/src/flyer-import/services/flyer-normalizer.service.ts:26-30`
- Ändra `greve: 'Greveost'` till `greve: 'Grevéost'`.
- Behåll `prast: 'Prästost'` och `herrgard: 'Herrgårdsost'`.
### A2. Skydda diakritiska tecken hela vägen från AI-svar till klient
- `backend/src/flyer-import/services/ai-flyer-parser.service.ts`
- Säkerställ att Mistralklientens response.choices[0].message.content tolkas som UTF-8. Logga hex-dump i debug-läge om tecken förvanskas.
- I `sanitizeJsonResponse`/`normalizeAiItem`: säkerställ att `rawName` inte normaliseras eller stripps innan `normalize`-steget.
- Uppdatera prompten:
- Lägg till explicit instruktion: `Behåll svenska diakritiska tecken (ä, å, ö, é) i produktnamn. Returnera "Prästost", "Herrgårdsost", "Grevéost" - inte ASCII-versioner.`
- Uppdatera exempel-utdata i prompten (rad 256-286) från "Prastost"/"Herrgardsost" till "Prästost"/"Herrgårdsost"/"Grevéost" så modellen tränas att returnera korrekt.
- Behåll fortfarande `prast/herrgard/greve` som tokens i instruktion 9 men kräv normalisering till diakrit-versionen i utdatan.
- `backend/src/flyer-import/services/flyer-normalizer.service.ts`
- I `expandCheeseVariants` (rad 196-227): efter `stripDiacritics`-tokenisering, mappa via `CHEESE_VARIANT_TO_NAME` så slutnamnet alltid har korrekta tecken.
- I `fixKnownOcrTypos` (rad 250-262): lägg till regel som korrigerar "Greveost" -> "Grevéost" (men bara när det är klart att det är ostnamnet, inte personnamn). Använd kontext: bara om `category` antyder hårdost/ost eller `rawName` slutar på `ost`.
### A3. Säkra Caddy/HTTP-respons för UTF-8
- Verifiera att `Content-Type: application/json; charset=utf-8` returneras från NestJS (default i `@nestjs/platform-express`, men kontrollera att ingen middleware skriver om).
- Caddy gör inte byteomvandling som standard, men dubbelkolla att `flutter/Caddyfile` inte har någon `replace`-direktiv (det har det inte i nuläget).
### A4. Test
- Utöka `flyer-normalizer.service.spec.ts` med:
- Test som matar in rawName "PRAST" och kontrollerar att outputens rawName blir "Prästost" (inte "Prastost").
- Test som matar in rawName "GREVE" och kontrollerar att outputens rawName blir "Grevéost".
- Test som verifierar att `é`-tecken bevaras i hela pipen.
- Snapshot-test för prompten i `ai-flyer-parser.service.spec.ts` så att ändringar i prompten är medvetna.
### Acceptanskriterier Fas A
- En flyer som innehåller "PRAST, HERRGARD, GREVE" producerar rader med rawName `Prästost`, `Herrgårdsost`, `Grevéost`.
- Ingen befintlig test går sönder.
- UI:t (Flutter) visar de korrekta tecknen utan encoding-artifacts.
### Risker
- Mistral kan ignorera diakritiska tecken i instruktioner. Motåtgärd: post-normalisering i `flyer-normalizer.service.ts` är hårda fallback-regeln.
---
## Fas B: Beskrivande felmeddelanden ersätter `parse:ai_parsed` och `match:no_match`
### B1. Centraliserad reason-code-katalog (backend)
- Skapa `backend/src/flyer-import/services/reason-codes.ts` (eller `backend/src/ai/reason-codes.ts` om det är delat med receipt) som exporterar:
- `ParseReasonCode = 'ai_parsed' | 'split_cheese_variants' | 'normalized' | 'low_confidence' | ...`
- `MatchReasonCode = 'no_match' | 'alias_exact' | 'normalized_exact' | 'token_overlap' | 'alias_points_to_missing_product' | 'empty_name'`
- Funktion `describeParseReason(code, context?): { title, message, severity, location? }`
- Funktion `describeMatchReason(code, context?): { title, message, severity }`
- Exempelmappning:
- `ai_parsed` -> `severity: 'info', title: 'AI-tolkad rad', message: 'Raden tolkades av AI utan att en deterministisk regel matchade.'`
- `split_cheese_variants` -> `severity: 'info', title: 'Variant-split', message: 'AI-svarade en gruppannons som expanderades till individuella ostvarianter.'`
- `low_confidence` -> `severity: 'warning', title: 'Låg parsningskvalitet', message: 'Modellens säkerhet är låg, granska raden manuellt.'`
- `no_match` -> `severity: 'warning', title: 'Ingen produktmatchning', message: 'Vi kunde inte hitta någon befintlig produkt som matchar texten på flyern.'`
- `alias_points_to_missing_product` -> `severity: 'error', title: 'Trasig alias-koppling', message: 'Ett alias pekar på en produkt som inte längre finns.'`
- `empty_name` -> `severity: 'error', title: 'Tomt produktnamn', message: 'Raden saknar tolkbart produktnamn.'`
- Inkludera `location` när relevant: t ex `'Steg: AI-parser, chunk N/M'` eller `'Steg: matchning mot dina produkter'`.
### B2. Returnera strukturerade reasons i API
- Utöka `backend/src/flyer-import/dto/flyer-import.response.ts`:
```ts
export type FlyerReasonDescriptor = {
code: string; // 'ai_parsed', 'no_match', ...
kind: 'parse' | 'match';
title: string; // Människovänlig titel
message: string; // Förklarande text
severity: 'info' | 'warning' | 'error';
location: string | null; // T ex 'Steg: matchning mot dina produkter'
};
```
- Lägg till på `FlyerImportItem`:
- `parseReasonsDetailed: FlyerReasonDescriptor[]`
- `matchReasonsDetailed: FlyerReasonDescriptor[]`
- Behåll befintliga `parseReasons: string[]` och `matchReasons: string[]` för bakåtkompatibilitet.
- I `flyer-import.service.ts:110-144` (där `FlyerImportItem` byggs): mappa via `describeParseReason`/`describeMatchReason`.
- Detsamma för session-läs-paths (`toFlyerImportItem` rad 721-799 och `toFlyerImportResponseFromSession`).
### B3. Uppdatera AI-trace warnings (admin)
- `backend/src/ai/ai-trace.service.ts:435-452` (`collectWarnings`):
- Ändra till att returnera strukturerade objekt istället för `parse:xxx`/`match:xxx`-strängar:
```ts
type AdminAiWarning = {
code: string;
kind: 'parse' | 'match';
title: string;
message: string;
severity: 'info' | 'warning' | 'error';
itemIndex?: number; // Pekar på vilken rad i sessionen
};
```
- Uppdatera `AdminAiTraceDetail.warnings` schema till strukturerat format.
- Bibehåll en `legacyWarnings: string[]` med gamla formatet ifall någon klient ännu inte uppdaterats.
### B4. Uppdatera Flutter-modeller och vyer
- `flutter/lib/features/import/domain/flyer_import_item.dart`:
- Lägg till `parseReasonsDetailed: List<FlyerReasonDescriptor>` och `matchReasonsDetailed: List<FlyerReasonDescriptor>` med `fromJson`/`toJson`.
- Skapa `flutter/lib/features/import/domain/flyer_reason_descriptor.dart`:
- `class FlyerReasonDescriptor { final String code; final String kind; final String title; final String message; final String severity; final String? location; ... }`
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`:
- I `_buildWarningsPanel` (rad 497-539): visa enbart sessionens egentliga warnings (existerande) men gör dem klickbara så de kan kopieras.
- Per rad: visa en summerande badge "X varningar" som expanderar till en lista med titel + message istället för tekniska koder. Använd ikoner per severity (info/warning/error).
- Behåll befintlig kvalitetsbadge (Hög/Medel/Låg).
- `flutter/lib/features/admin/presentation/admin_ai_panel.dart`:
- `_WarningsCard` (rad 583+): byt ut SelectableText med rå-strängar mot strukturerad rendering: titel (fet), message (vanlig), severity-färg, eventuellt itemIndex som länk.
- Behåll copy-funktion - kopierar då en formaterad sträng `"[severity] title: message"`.
### B5. Test
- Backend: enhetstest för `describeParseReason`/`describeMatchReason` som täcker alla codes.
- Backend: integrationstest som verifierar att `FlyerImportResponse` innehåller `parseReasonsDetailed` med rätt fält.
- Flutter: widget-test för warnings-panel som verifierar att `parse:ai_parsed` ALDRIG visas, utan ersätts av "AI-tolkad rad".
- Uppdatera `flutter/test/features/admin/presentation/admin_ai_panel_test.dart` som idag verifierar `find.text('parse:low_confidence')` - testet ska istället leta efter "Låg parsningskvalitet".
### Acceptanskriterier Fas B
- Inga `parse:xxx`- eller `match:xxx`-strängar visas i UI:t (varken admin eller import).
- Varje warning har: titel, beskrivande text, severity-ikon, och om relevant en location ("Steg: ...").
- API:et returnerar både legacy- och nytt format, så ingen klient bryts.
### Risker
- Översättning av code -> human text måste hållas i sync mellan backend och Flutter. Motåtgärd: sätt textmappningen ENBART i backend och returnera färdig string. Flutter renderar bara.
---
## Fas C: Synlig prompt i import-flödet och vid varningar
### C1. Bestäm synlighetspolicy
- Operatörer/admins ska kunna se prompten direkt i admin AI-panelen (finns redan, fortsätter fungera).
- Vanliga användare ska kunna se prompten **efter behov** för att felsöka eller rapportera fel - men inte by default eftersom prompten är teknisk.
- Föreslagen UX: när en rad har varningar (parse eller match), visa knapp "Visa AI-detaljer" som öppnar modal med:
- Använd modell + retry/chunk-info
- Prompten (expanderbar)
- AI-svar (raw output, expanderbar)
- Lista över alla parse/match-reasons med deras `describeXxxReason`-output
- Promptvisning i adminpanelen redan komplett (`_PromptCard`) - bara verifiera att det fortsätter funka efter Fas B.
### C2. Backend-ändringar
- Ny endpoint `GET /api/flyer-import/sessions/:sessionId/ai-trace` som returnerar:
```ts
{
sessionId: number;
model: string;
prompt: string;
rawOutput: string;
chunkCount: number | null;
retryCount: number | null;
durationMs: number | null;
status: 'success' | 'warning' | 'error';
warnings: AdminAiWarning[];
}
```
- Återanvänd existerande `aiTrace`-tabellen. Auth: kräv att `userId` ägar sessionen (samma policy som `getSessionSource`).
- Eventuellt feature-flagga visa-prompt-för-användare (`FLYER_AI_USER_PROMPT_VISIBLE` env). Default: `true` för admin, `true` för användare som äger sessionen.
### C3. Flutter-ändringar
- `flutter/lib/features/import/data/import_repository.dart`: ny metod `getFlyerSessionAiTrace(sessionId, token)`.
- `flutter/lib/features/import/domain/`: ny modell `FlyerAiTrace`.
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`:
- Lägg till en "AI-detaljer"-knapp i headern (efter Importera-knappen) eller i varje rads expanderingspanel.
- Visa prompt + rawOutput i en modal/expanderbar Card med samma look-and-feel som admin (`_PromptCard` + `_OutputJsonCard` kan extraheras till delad widget under `flutter/lib/features/import/presentation/widgets/ai_trace_view.dart`).
- Lägg till samma åtgärd för `receipt_import_tab.dart` om motsvarande backend-stöd finns (det finns - receipts har också AI-trace).
### C4. Säkerhet och PII
- Innan prompt visas till slutanvändare: kör `maskSensitiveText` (finns redan i `ai-trace.service.ts`).
- Alla loggade prompts/rawOutputs ska redan vara maskerade i nuvarande pipeline. Verifiera att det fortsätter gälla.
### C5. Test
- Backend: e2e-test för nya endpointen, inklusive 403 för icke-ägare och 200 för ägare.
- Flutter: widget-test för att modalen öppnas och visar prompten.
- Manuell QA: Importera en flyer, klicka "AI-detaljer", verifiera att prompten visas och att kopiera-knappen fungerar.
### Acceptanskriterier Fas C
- Användare kan se prompten som skickades vid sin egen flyerimport.
- Adminpanelen visar fortfarande prompten oförändrat.
- PII-mask appliceras innan prompten skickas till klienten.
### Risker
- Prompten är lång (>5000 tecken). Motåtgärd: använd existerande expand/collapse-mönster (`_PromptCard.expanded`).
- Prompten kan innehålla användardata. Motåtgärd: maskning + tydlig "Detta innehåller text från din flyer"-disclaimer.
---
## Prioriterad genomförandeordning
1. **Fas A** (specialtecken) - lågrisk, snabb seger för UX.
2. **Fas B** (mänskliga felmeddelanden) - medelarbete, hög UX-impact.
3. **Fas C** (prompt-synlighet) - mer komplex pga ny endpoint + UI, men oberoende av A och B.
Faserna kan implementeras i parallella PR:er om så önskas; Fas A och B berör delvis samma kodvägar i `flyer-normalizer.service.ts` och `flyer-import.service.ts`, och bör samordnas så att en PR mergas före nästa.
---
## Definition of Done
- "Prästost", "Herrgårdsost", "Grevéost" och `é`/`å`/`ä` syns korrekt i flyerimport-UI.
- Inga råa `parse:ai_parsed`/`match:no_match`-strängar visas för användare eller admin.
- Användare och admin kan se vilken prompt som skickades till AI.
- Befintliga tester passerar; nya tester täcker varje fas separat.
- Inga regressioner i Docker-bygget (`backend/Dockerfile` kör `npm test -- --runInBand`).
---
## Konkreta filer som berörs
Backend:
- `backend/src/flyer-import/services/flyer-normalizer.service.ts` (Fas A)
- `backend/src/flyer-import/services/ai-flyer-parser.service.ts` (Fas A, eventuellt B)
- `backend/src/flyer-import/services/reason-codes.ts` (ny, Fas B)
- `backend/src/flyer-import/dto/flyer-import.response.ts` (Fas B)
- `backend/src/flyer-import/flyer-import.service.ts` (Fas B, Fas C)
- `backend/src/flyer-import/flyer-import.controller.ts` (Fas C, ny endpoint)
- `backend/src/ai/ai-trace.service.ts` (Fas B)
- `backend/src/flyer-import/services/flyer-normalizer.service.spec.ts` (Fas A)
- `backend/src/flyer-import/services/ai-flyer-parser.service.spec.ts` (Fas A, B)
Flutter:
- `flutter/lib/features/import/domain/flyer_import_item.dart` (Fas B)
- `flutter/lib/features/import/domain/flyer_reason_descriptor.dart` (ny, Fas B)
- `flutter/lib/features/import/domain/flyer_ai_trace.dart` (ny, Fas C)
- `flutter/lib/features/import/data/import_repository.dart` (Fas C)
- `flutter/lib/features/import/presentation/flyer_import_tab.dart` (Fas A-C)
- `flutter/lib/features/import/presentation/widgets/ai_trace_view.dart` (ny, Fas C)
- `flutter/lib/features/admin/domain/admin_ai_trace_detail.dart` (Fas B)
- `flutter/lib/features/admin/presentation/admin_ai_panel.dart` (Fas B)
- `flutter/test/features/admin/presentation/admin_ai_panel_test.dart` (Fas B)
---
## Riskanalys och rollback
| Risk | Sannolikhet | Motåtgärd |
| --- | --- | --- |
| Mistral returnerar fortfarande ASCII-versioner | Medel | Hård post-normalisering i `flyer-normalizer.service.ts` (Fas A2) |
| Strukturerade reasons bryter befintlig admin-vy | Låg | Behåll legacy-format parallellt under en release |
| Promptens längd försämrar mobil-UX | Låg | Default collapsed med expand-knapp |
| AI-trace exponerar PII oavsiktligt | Medel | Återanvänd befintlig `maskSensitiveText` + tydlig disclaimer |
| Ändrade reason codes bryter andra konsumenter (t ex flyer-selection-matcher) | Låg | Sökning visar att `reasonCodes` används endast i flyer-import + ai-trace; uppdatera samtliga callsites i samma PR |
---
## Beslut tagna med dig
1. **Promptens synlighet**: endast admin. Vanliga användare ser inte prompten - bara översatta reasons. Detta förenklar Fas C avsevärt: ingen ny användarendpoint, ingen PII-mask för slutanvändare, ingen ny användar-UI.
2. **Reason-codes översätts i backend**: backend returnerar färdig svensk text (`title`, `message`) i `FlyerReasonDescriptor`. Frontend renderar bara strängarna utan översättning. Lang-parameter förbereds för framtida flerspråksstöd.
3. **Kopiera felrapport-knapp**: ja. Lägg till "Kopiera felrapport"-knapp i admin AI-panelens detail-vy som producerar formaterad text:
```
[AI-trace flyer-123]
Modell: ministral-8b-2512
Status: warning (3 varningar)
Tid: 2026-05-23T20:12:00
Varningar:
- [warning] Ingen produktmatchning (rad 5): Vi kunde inte hitta...
- [info] AI-tolkad rad (rad 7): Raden tolkades av AI...
Prompt:
...
Raw output:
...
```
4. **Stavning**: `Grevéost` med `é` (Arlas officiella stavning).
## Konsekvenser av besluten på faserna
### Fas A (oförändrad)
- `Grevéost` används i mappningen.
### Fas B (oförändrad)
- Backend översätter och returnerar färdig svensk text i `title`/`message`.
- Behåll `code`-fältet så frontend kan filtrera/rendera olika per typ.
### Fas C (förenklad)
- **Tas bort**: Ny användarendpoint `GET /api/flyer-import/sessions/:id/ai-trace`.
- **Tas bort**: Ny Flutter-modell `FlyerAiTrace` och repository-metod för användarvy.
- **Tas bort**: Ny widget `ai_trace_view.dart` för importflödet.
- **Behålls**: Adminpanelens promptvisning (`_PromptCard`) - finns redan, fungerar.
- **Läggs till**: "Kopiera felrapport"-knapp i adminpanelens detail-vy. Genererar formaterad text enligt mallen ovan.
- **Användarflöde uppdateras**: i flyer/receipt-import-tabben visa endast översatta reasons via Fas B; ingen prompt-knapp för användare.
## Uppdaterade konkreta filer (efter beslut)
Backend:
- `backend/src/flyer-import/services/flyer-normalizer.service.ts` (Fas A)
- `backend/src/flyer-import/services/ai-flyer-parser.service.ts` (Fas A)
- `backend/src/flyer-import/services/reason-codes.ts` (ny, Fas B)
- `backend/src/flyer-import/dto/flyer-import.response.ts` (Fas B)
- `backend/src/flyer-import/flyer-import.service.ts` (Fas B)
- `backend/src/ai/ai-trace.service.ts` (Fas B)
- `backend/src/flyer-import/services/flyer-normalizer.service.spec.ts` (Fas A)
- `backend/src/flyer-import/services/ai-flyer-parser.service.spec.ts` (Fas A)
- *Inte längre*: ny endpoint i `flyer-import.controller.ts` (utgår med beslut 1).
Flutter:
- `flutter/lib/features/import/domain/flyer_import_item.dart` (Fas B)
- `flutter/lib/features/import/domain/flyer_reason_descriptor.dart` (ny, Fas B)
- `flutter/lib/features/import/presentation/flyer_import_tab.dart` (Fas A-B)
- `flutter/lib/features/admin/domain/admin_ai_trace_detail.dart` (Fas B)
- `flutter/lib/features/admin/presentation/admin_ai_panel.dart` (Fas B + felrapportknapp)
- `flutter/test/features/admin/presentation/admin_ai_panel_test.dart` (Fas B)
- *Inte längre*: `flyer_ai_trace.dart`, `ai_trace_view.dart`, repository-metod (utgår med beslut 1).
+198
View File
@@ -0,0 +1,198 @@
# Plan: Harmonisera flyer-import och kvitto-import
## Mål
Implementera en gemensam importmodell och matchningspipeline så att flyer-import och kvitto-import beter sig så likt som möjligt, med fokus på:
- Automatisk strukturering av namn/brand/vikt samt bundle-detaljer
- Automatiskt kategoriupplösning (`categoryHint -> categoryId`)
- Matchning mot befintliga produkter via normaliserade namn + signaler
- Ingen automatisk skapning av produkter
- Förberedelse för framtida automation via strukturerade signaler (`signals` JSON)
## Icke-mål (denna implementation)
- Ingen auto-create av produkter i produktkatalog
- Ingen ändring av övergripande UI-flöde (manuell import/validering kvar)
- Ingen full omskrivning av receipt-import; vi extraherar och återanvänder delar stegvis
## Nuvarande gap (från kodbasen)
1. `FlyerItem.categoryId` sätts till `null` i parse-flödet trots `categoryHint`.
2. Flyer-matchning använder enklare strategi än receipt-import (färre regler/signalvikter).
3. Ingen strukturerad lagring av ursprung/etiketter (t.ex. Sverige, Eko) i flyer.
4. Bundleinformation finns men exponeras inte tydligt som detaljnamn i payload.
5. Receipt och flyer använder olika “kontrakt” för mellanrepresentation.
## Övergripande design
Inför en gemensam intern domänmodell för importerade rader (backend), och låt både flyer- och kvittoflöde mappa till den innan kategori/matchning.
### Gemensam intern modell (ny)
`ImportedItemCandidate` (internt, ej API-brytande initialt):
- `rawName`, `normalizedName`, `brand`
- `weight`, `bundleWeight`, `isBundle`, `bundleItems`
- `price`, `priceUnit`, `comparisonPrice`, `comparisonUnit`
- `categoryHint`, `categoryId`
- `matchedProductId`, `matchedProductName`, `matchedVia`, `matchConfidence`, `matchReasons`
- `signals` (JSON):
- `originCountries: string[]`
- `labels: string[]` (ekologisk, laktosfri, etc)
- `qualityFlags: string[]` (normaliserade flaggor, ex `eco`)
- `variant: string | null`
- `packaging: string | null`
- `displayNameDetailed` (beräknat fält, kan persistas eller beräknas vid response)
## Faser och implementation
## Fas 1: Datamodell och migration
1. Uppdatera `backend/prisma/schema.prisma`:
- Lägg till `signals Json?``FlyerItem`
- Lägg till `displayNameDetailed String?``FlyerItem`
2. Skapa Prisma-migration.
3. Säkerställ bakåtkompatibilitet:
- Nullabla fält
- Ingen ändring av befintliga constraints/index som bryter drift
4. (Valfritt i samma fas) indexera vanligt använda JSON-signaler senare först efter verifierad nytta.
### Acceptanskriterier fas 1
- Migration appliceras lokalt utan dataförlust.
- Befintliga endpoints fungerar med gamla rader (`signals = null`).
## Fas 2: Gemensamma normaliserings-/signalverktyg
1. Skapa gemensam utility-modul i backend, exempel:
- `backend/src/import-common/import-item.types.ts`
- `backend/src/import-common/import-signals.util.ts`
- `backend/src/import-common/import-display-name.util.ts`
2. Implementera signal-extraktion från textfält (`rawName`, `brand`, `offerText`):
- Ursprungsländer till `originCountries`
- Etiketter/märkningar till `labels`/`qualityFlags`
- Pack-format till `packaging`
3. Normalisera utan att förlora information:
- Ta bort signalord från primär matchsträng men spara i `signals`
- Ex: `Fläskytterfilé (Sverige)` -> matchsträng `flaskytterfile`, `signals.originCountries=["Sverige"]`
4. Implementera `displayNameDetailed`:
- Bundle: inkludera `bundleItems` i visningsnamn
- Ex: `Kaptenens Favoriter (Chumlax 3x100g + Alaska pollock 3x100g)`
### Acceptanskriterier fas 2
- Signals extraheras deterministiskt för kända mönster (Sverige/Tyskland/Eko/Ekologiskt).
- `displayNameDetailed` genereras för bundles.
## Fas 3: Kategoriupplösning i flyer (paritet med kvitto)
1. Extrahera/återanvänd kategori-regelmotorn från receipt-import till gemensam tjänst:
- Ex: `backend/src/import-common/category-resolver.service.ts`
2. Använd den i flyer-import efter normalisering:
- `categoryHint` + signaltext + regler -> `categoryId`
3. Prioritet:
- Produktmatchad kategori (om säkert matchad produkt har kategori) kan väga högst
- Annars regelbaserad kategori
- Annars behåll `categoryHint` utan `categoryId`
4. Specifika regler för kött/fläskytterfilé verifieras.
### Acceptanskriterier fas 3
- `Fläskytterfilé` får korrekt `categoryId` i flyer-session.
- `categoryId` sätts automatiskt för en betydande andel rader med tydlig signal.
## Fas 4: Matchningsparitet flyer <-> kvitto
1. Bryt ut matchning till gemensam matcher (eller harmonisera algoritm):
- alias exact
- canonical/normalized exact
- token/fuzzy
- bonus för brand/weight/signalträffar
2. Matchning ska använda signalrensad namnsträng + metadata:
- Länder och eco-etiketter ska inte sabotera namnmatch
3. Standardisera reason codes mellan flöden (så långt möjligt utan brytande API):
- `alias_exact`, `normalized_exact`, `token_overlap:*`, `no_match`
4. Behåll strikt policy: ingen auto-create produkt.
### Acceptanskriterier fas 4
- Färre `no_match` på samma flyer-input jämfört med baseline.
- Matchningsorsaker blir mer förklarbara och konsekventa.
## Fas 5: API/DTO och persistens
1. Uppdatera flyer DTO:
- `backend/src/flyer-import/dto/flyer-import.response.ts`
- Lägg till `signals` och `displayNameDetailed`.
2. Uppdatera persistens i `flyer-import.service.ts`:
- Spara `signals`, `displayNameDetailed`, `categoryId`.
3. Säkerställ att `getSession`, `getLatestSession`, `updateSessionItem` returnerar nya fält.
4. Behåll kompatibilitet mot klient:
- Nya fält adderas utan att ta bort befintliga.
### Acceptanskriterier fas 5
- Response innehåller tydlig bundle-info och signaler per rad.
- Inga regressions i existerande frontend-parsing.
## Fas 6: Frontend (flyer import-tab)
1. Uppdatera domänmodeller i Flutter:
- `flutter/lib/features/import/domain/flyer_import_item.dart`
- ev. session/result-objekt
2. Visa `displayNameDetailed` där tillgängligt, annars fallback `rawName`.
3. Visa `bundleItems` tydligt i list-/detaljrad.
4. Visa badge/metadata för signaler (`Sverige`, `Ekologisk`) utan att skriva över produktnamn.
5. Säkerställ att manuellt urval till inköpslista fortsätter fungera.
### Acceptanskriterier fas 6
- Bundle-rader är tydligare i UI.
- Ursprung/eko syns som metadata.
## Fas 7: Teststrategi
### Backend enhetstester
- `flyer-normalizer.service.spec.ts`
- extraktion av `signals` (origin/labels)
- bundle-detaljnamn
- Ny kategori-resolver-spec
- `Fläskytterfilé` -> köttkategori
- `flyer-import.service.spec.ts`
- `categoryId` sätts vid tydlig signal
- `signals` och `displayNameDetailed` persisteras/returneras
- Matchningstester
- namn med land/eko matchar korrekt produkt
### Integrationstester
- End-to-end parseAndMatch med representativ flyer-fixture.
- Verifiera att inga produkter auto-skaps.
- Verifiera att shopping-list insertion fungerar med/utan `matchedProductId`.
### Frontendtester
- Serialisering av nya fält i import-session.
- Rendering av `displayNameDetailed` + `bundleItems`.
## Fas 8: Mätning och rollout
1. Lägg till enkel före/efter-mätning i logg/trace:
- andel `no_match`
- andel med satt `categoryId`
2. Soft rollout via feature flag (om möjligt), annars stegvis release.
3. Utvärdera verkliga flyer-sessioner innan vidare automatisering.
## Konkreta filer att ändra (planerad)
- `backend/prisma/schema.prisma`
- `backend/src/flyer-import/flyer-import.service.ts`
- `backend/src/flyer-import/services/flyer-normalizer.service.ts`
- `backend/src/flyer-import/dto/flyer-import.response.ts`
- `backend/src/receipt-import/receipt-import.service.ts` (endast för extraktion/återanvändning av gemensamma delar)
- Nya gemensamma filer under `backend/src/import-common/*`
- `flutter/lib/features/import/domain/flyer_import_item.dart`
- `flutter/lib/features/import/data/flyer_import_session.dart`
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`
- Relevanta spec/test-filer i backend + flutter
## Risker och mitigering
- Risk: API-kontraktsändringar bryter klient.
- Mitigering: endast additive fält, fallback på gamla fält.
- Risk: Felkategori vid aggressiva regler.
- Mitigering: regelprioritet + reason-codes + tester för edge cases.
- Risk: Övermatchning av produkter.
- Mitigering: tröskelvärden + konservativ confidence för fuzzy.
## Leveransordning (rekommenderad)
1. Fas 12 (schema + signals + utilities)
2. Fas 3 (kategoriupplösning flyer)
3. Fas 4 (matchningsparitet)
4. Fas 5 (DTO/persistens)
5. Fas 6 (frontend)
6. Fas 78 (tester + mätning/rollout)
## Definition of Done
- Flyer och kvitto använder samma centrala regler för kategorisering/matchning där möjligt.
- Flyer-rader innehåller `signals` och tydligare produktrepresentation (`displayNameDetailed`, bundle-innehåll).
- `categoryId` sätts automatiskt i flyer när tillräcklig signal finns (inkl. fläskytterfilé-fall).
- Ingen automatisk produktskapning sker.
- Tester uppdaterade och gröna.
+98
View File
@@ -0,0 +1,98 @@
# Plan: Hantera deprecated dependencies (`inflight` / `glob`) i backend
## Mål
Minska säkerhets- och stabilitetsrisk från deprecated/transitiva paket i `backend` genom kontrollerad uppgradering, verifiering och CI-skydd utan att bryta befintliga flöden.
## Bakgrund
`npm ci` i `backend` visar varningar för:
- `inflight@1.0.6` (ej underhållen, memory leak)
- `glob@7.2.3` (föråldrad med kända sårbarheter)
Detta är normalt transitiva beroenden, men bör ändå adresseras systematiskt.
## Scope
- Endast Node-backend (`backend/`)
- Inga funktionella ändringar i app-logik
- Fokus: dependency graph, lockfile, CI-kontroller, dokumentation
## Implementationsplan
1. **Kartlägg källan till transitiva paket**
- Kör i `backend/`:
- `npm ls inflight`
- `npm ls glob`
- Dokumentera exakt vilka toppnivåpaket som drar in versionerna.
- Syfte: avgöra om problemet löses via toppnivåuppdatering eller `overrides`.
2. **Uppdatera direkta beroenden först (minst invasivt)**
- Kör riktad uppdatering av paket som identifieras i steg 1 (t.ex. test-/build-verktyg först, runtime efteråt).
- Kör därefter:
- `npm ci`
- `npm ls inflight glob`
- Beslutspunkt:
- Om `glob@7`/`inflight` försvinner: gå vidare till verifiering.
- Om kvarstår: gå till steg 3.
3. **Inför `overrides` i `backend/package.json` vid behov**
- Lägg till kontrollerade `overrides` för att styra bort sårbara/föråldrade versioner.
- Prioritet:
- `glob` till modern, kompatibel version i aktiv support.
- Undvik tvingad ersättning av `inflight` med inkompatibla alias om konsumentpaket inte stödjer det.
- Regenerera lockfile via normal npm-process och verifiera installationsflöde.
4. **Säkerhetsverifiera dependency-trädet**
- Kör:
- `npm audit --audit-level=high`
- `npm audit` (för full kontext)
- Klassificera återstående fynd:
- fixbara nu
- accepterad residualrisk (med motivering)
5. **Regressionstest av backend efter dependency-ändringar**
- Kör samma kvalitetskedja som används i projektet:
- `npm run prisma:validate`
- `npm run prisma:generate`
- `npm run typecheck`
- `npm run lint`
- `npm test`
- `npm run build`
- Syfte: säkerställa att dependency-upgrade inte skapar drift-/build-regressioner.
6. **Skärp CI-policy (om inte redan tillräcklig)**
- Verifiera att `.github/workflows/test.yml` fortsatt kör `npm audit --audit-level=high` för backend-push.
- Om önskat: höj till `critical` eller behåll `high` enligt teamets riskprofil.
- Rekommendation: behåll `high` i nuläget för bättre tidig signal i ett aktivt projekt.
7. **Dokumentera beslut och operativ hantering**
- Uppdatera `README.md` kort med:
- att deprecated-varningar hanterats
- hur man felsöker nya transitiva varningar (`npm ls <paket>`)
- policy för hur snabbt dependencies ska uppdateras
## Risker och mitigering
- **Risk: Breaking changes vid dependency bump**
- Mitigering: uppgradera stegvis + full kvalitetskedja i steg 5.
- **Risk: `overrides` maskerar underliggande kompatibilitetsproblem**
- Mitigering: använd `overrides` endast när toppnivåuppdatering inte räcker; dokumentera tydligt varför.
- **Risk: Kvarvarande audit-fynd blockerar release**
- Mitigering: klassificera residualrisk och skapa uppföljningsärende med ägare och deadline.
## Leverabler
- Uppdaterat `backend/package.json` (ev. `overrides` + uppdaterade versionsintervall)
- Uppdaterad `backend/package-lock.json`
- Eventuell liten CI-justering i `.github/workflows/test.yml`
- Kort dokumentationsnotis i `README.md`
## Acceptanskriterier
- `npm ls inflight` visar inte problematisk kedja, eller tydligt dokumenterad residualrisk med motivering.
- `npm ls glob` visar ingen osäker/föråldrad kedja (eller dokumenterad temporär avvikelse med plan).
- `npm audit --audit-level=high` passerar, eller kvarvarande fynd är explicit riskaccepterade.
- Backend-kvalitetskedjan passerar utan regression.
- CI för backend fortsätter passera.
## Rekommenderad genomförandeordning
1) Kartlägg (`npm ls`)
2) Riktade uppdateringar
3) `overrides` endast vid behov
4) Audit + full testkedja
5) CI/dokumentation
@@ -0,0 +1,69 @@
# Plan: Harmonisering av importfält baserat på inventory-tabellen
## Mål
Skapa konsistens mellan kvitto-import, flyer-import och inventory-tabellen genom att anpassa fältnamn, datatyper och struktur. Detta kommer att förenkla integrationen och minska risken för fel.
## Bakgrund
- `inventory`-tabellen är central och har en väletablerad struktur.
- Kvitto-import och flyer-import använder olika fältnamn och datatyper, vilket skapar inkonsistenser.
- Flyer-import använder `signals.originCountries` (array), medan `inventory` använder `origin` (string).
## Scope
- Uppdatera `ParsedReceiptItem` och `FlyerImportItem` för att matcha `inventory`-tabellen.
- Uppdatera mappningslogiken i importfunktionerna.
- Uppdatera databasen för att stödja `originCountries` som en array (lång sikt).
## Implementationsplan
### 1. Uppdatera `ParsedReceiptItem` (kvitto-import)
- **Mål**: Anpassa fältnamn och datatyper för att matcha `inventory`-tabellen.
- **Åtgärder**:
- Lägg till `categoryId` för att möjliggöra kategorisättning.
- Använd `rawName` istället för `receiptName` för konsistens.
- Mappa `origin` till `inventory.origin`.
### 2. Uppdatera `FlyerImportItem` (flyer-import)
- **Mål**: Anpassa fältnamn och datatyper för att matcha `inventory`-tabellen.
- **Åtgärder**:
- Använd `rawName` istället för `receiptName` för konsistens.
- Mappa `signals.originCountries[0]` till `inventory.origin`.
- Mappa `categoryId` till `product.categoryId` om en produkt skapas/uppdateras.
### 3. Uppdatera mappningslogiken
- **Mål**: Förenkla mappningen från importfunktionerna till `inventory`-tabellen.
- **Åtgärder**:
- Uppdatera `receipt-import.service.ts` för att använda `inventory`-fältnamn.
- Uppdatera `flyer-import.service.ts` för att använda `inventory`-fältnamn.
### 4. Uppdatera databasen (lång sikt)
- **Mål**: Stödja `originCountries` som en array i `inventory`-tabellen.
- **Åtgärder**:
- Lägg till `originCountries Json?` i `inventory`-tabellen.
- Uppdatera `CreateInventoryDto` för att inkludera `originCountries`.
### 5. Uppdatera DTO:er
- **Mål**: Säkerställa att DTO:er matchar `inventory`-tabellen.
- **Åtgärder**:
- Uppdatera `CreateInventoryDto` för att inkludera `originCountries`.
## Leverabler
- Uppdaterade `ParsedReceiptItem` och `FlyerImportItem` som matchar `inventory`-tabellen.
- Uppdaterad mappningslogik i `receipt-import.service.ts` och `flyer-import.service.ts`.
- Uppdaterad databas för att stödja `originCountries` som en array.
- Uppdaterade DTO:er för att inkludera `originCountries`.
## Acceptanskriterier
- `ParsedReceiptItem` och `FlyerImportItem` använder samma fältnamn och datatyper som `inventory`-tabellen.
- Mappningslogiken i importfunktionerna är förenklad och använder `inventory`-fältnamn.
- `inventory`-tabellen stödjer `originCountries` som en array.
- `CreateInventoryDto` inkluderar `originCountries`.
## Rekommenderad genomförandeordning
1. Uppdatera `ParsedReceiptItem` och `FlyerImportItem`.
2. Uppdatera mappningslogiken i importfunktionerna.
3. Uppdatera databasen för att stödja `originCountries` som en array.
4. Uppdatera DTO:er för att inkludera `originCountries`.
## Handover from Planning Session
- Planen är klar och redo för implementering.
- Inga frågor eller otydligheter kvarstår.
+706
View File
@@ -0,0 +1,706 @@
🚨 Kritiska Problem (Prioritet 1: Fixa Omedelbart)
Dessa problem påverkar användarupplevelsen mest och bör åtgärdas först.
1. Prestanda: Långsam inladdning & Stora Filer
🔴 Problem:
Total storlek: 2,978 KiB (för stor för en webbapp).
/main.dart.js: 1,216 KiB (Flutters kompilade JavaScript).
canvaskit.wasm: 1,592 KiB (Flutters CanvasKit-renderare).
MaterialIcons-Regular.otf: 9.8 KiB (ikoner).
Largest Contentful Paint (LCP) misslyckades (tidsgräns överskreds).
Total Blocking Time (TBT) misslyckades (långa JavaScript-uppgifter blockerar huvudtråden).
JavaScript-exekveringstid: 1.8s (för långt).
🟢 Lösningar:
A. Optimera Flutter för Web
Flutter-webbappar är tyngre än traditionella webbappar på grund av CanvasKit. Här är hur du minskar storleken:
Använd HTML-renderaren istället för CanvasKit
CanvasKit ger bättre grafik men är mycket tyngre.
Ändra i index.html:
html
Copy
<script>
window.flutterConfiguration = {
renderMode: "html", // Istället för "canvas" (CanvasKit)
};
</script>
Fördel: Minskar storleken med ~1.5MB (WASM-filen laddas inte).
Nackdel: Vissa avancerade animationer/efekter fungerar inte lika bra.
Aktivera komprimering (GZIP/Brotli)
Din server skickar okomprimerade filer.
Lösning:
Nginx: Lägg till i konfigurationen:
nginx
Copy
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
brotli on;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
Apache: Använd mod_deflate eller mod_brotli.
Cloudflare: Aktivera "Brotli Compression" i inställningarna.
Cachea statiska resurser
Problem: /main.dart.js och flutter_bootstrap.js har ingen cache-TTL.
Lösning: Lägg till Cache-Control-headers:
nginx
Copy
location ~* \.(js|css|png|jpg|jpeg|gif|ico|wasm|otf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
Besparing: ~1.2MB vid upprepade besök.
Dela upp JavaScript-koden (Code Splitting)
Flutter laddar allt i en stor fil (main.dart.js).
Lösning: Använd deferred imports för att ladda funktioner på begäran:
dart
Copy
// I din Dart-kod:
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'package:my_app/receipt_import.dart' deferred as receipt_import;
// Ladda endast när behövs:
Future<void> loadReceiptImport() async {
await receipt_import.loadLibrary();
}
Effekt: Minskar initial laddningstid.
Optimera bilder
Problem: Bilder laddas inte optimalt.
Lösning:
Använd flutter_image_compress för att komprimera bilder innan uppladdning.
För webb: Använd <picture> med srcset för responsiva bilder.
Exempel:
html
Copy
<picture>
<source srcset="image-480w.jpg" media="(max-width: 600px)">
<source srcset="image-800w.jpg" media="(max-width: 1200px)">
<img src="image-1200w.jpg" alt="Receptbild">
</picture>
B. Förbättra Laddningsordningen
Fördröj laddning av icke-kritiska resurser
Ladda canvaskit.wasm efter att sidan har renderats:
html
Copy
<script>
window.addEventListener('load', () => {
const script = document.createElement('script');
script.src = 'https://www.gstatic.com/canvaskit/v1/chromium/canvaskit.wasm';
document.body.appendChild(script);
});
</script>
Använd preload för kritiska resurser
Lägg till i <head>:
html
Copy
<link rel="preload" href="/main.dart.js" as="script">
<link rel="preload" href="/flutter_bootstrap.js" as="script">
2. Tillgänglighet: Grundläggande Problem
🔴 Problem:
[user-scalable="no"] i viewport-meta-taggen
Varför det är dåligt: Användare med nedsatt syn kan inte zooma in.
Lösning: Ta bort user-scalable="no":
html
Copy
<meta name="viewport" content="width=device-width, initial-scale=1.0">
Saknas alt-texter på bilder
Problem: Skärmläsare kan inte beskriva bilder.
Lösning: Lägg till alt-texter i Flutter:
dart
Copy
Image.network(
'https://example.com/recept.jpg',
semanticLabel: 'Bild på lasagne med ost och tomatsås', // Alt-text
),
Saknas lang-attribut
Problem: Skärmläsare vet inte vilket språk sidan använder.
Lösning: Lägg till i <html>:
html
Copy
<html lang="sv">
3. SEO: Grundläggande Problem
🔴 Problem:
Saknas meta description
Varför det är dåligt: Sökmotorer visar ingen beskrivning i resultaten.
Lösning: Lägg till i <head>:
html
Copy
<meta name="description" content="Upptäck och lagra dina recept. Importera kvitton och håll koll på ditt kylskåp med vår smarta app.">
Ogiltig robots.txt
Problem: Din robots.txt innehåller HTML-kod istället för korrekt syntax.
Lösning: Skapa en korrekt robots.txt:
text
Copy
User-agent: *
Allow: /
Sitemap: https://recept.gynther.se/sitemap.xml
⚠️ Viktiga Förbättringar (Prioritet 2: Fixa Inom 1-2 Veckor)
Dessa problem påverkar användarupplevelsen och SEO, men är inte lika kritiska.
1. Prestanda: JavaScript & Rendering
🟡 Problem:
Lång JavaScript-exekveringstid (1.8s)
Orsak: Flutters main.dart.js tar 1.7s att parsas och exekveras.
Minify CSS/JS misslyckades
Flutter genererar redan minifierad kod, men du kan optimera vidare.
🟢 Lösningar:
Aktivera Flutters --release flagga
Bygg appen med:
bash
Copy
flutter build web --release
Detta genererar optimerad och minifierad kod.
Använd flutter build web --no-source-maps
Source maps ökar filstorleken. Ta bort dem i produktion:
bash
Copy
flutter build web --no-source-maps
Fördröj laddning av icke-kritisk JavaScript
Använd defer eller async för skript som inte behövs omedelbart:
html
Copy
<script src="/flutter_bootstrap.js" defer></script>
Reducera DOM-storlek
Problem: Din sida har 21 element med en max-djup på 7 (acceptabelt, men kan optimeras).
Lösning: Undvik onödiga Container-widgets i Flutter. Använd const widgets där möjligt.
2. Tillgänglighet: Interaktiva Element
🟡 Problem:
Saknas focus-indikatorer
Användare som navigerar med tangentbord ser inte vilka element som är fokuserade.
Saknas aria-label på anpassade knappar
Skärmläsare vet inte vad knapparna gör.
🟢 Lösningar:
Lägg till focus-stilar i CSS
Exempel:
css
Copy
button:focus, [tabindex="0"]:focus {
outline: 2px solid #4285F4;
outline-offset: 2px;
}
Använd semantics i Flutter för tillgänglighet
Exempel för en knapp:
dart
Copy
Semantics(
label: 'Importera kvitto',
button: true,
child: ElevatedButton(
onPressed: () => _importReceipt(),
child: Text('Importera'),
),
),
Lägg till aria-label på ikonknappar
Exempel:
dart
Copy
IconButton(
icon: Icon(Icons.upload),
onPressed: () => _uploadFile(),
tooltip: 'Ladda upp kvitto', // Visas på hover
semanticsLabel: 'Ladda upp kvitto', // För skärmläsare
),
3. Säkerhet: Content Security Policy (CSP)
🟡 Problem:
Saknas CSP-header
Din sida är sårbar för XSS-attacker (Cross-Site Scripting).
🟢 Lösning:
Lägg till en stark CSP i din serverkonfiguration:
nginx
Copy
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.gstatic.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://*.gstatic.com; font-src 'self' https://fonts.gstatic.com; connect-src 'self' https://recept.gynther.se; frame-src 'none'; object-src 'none';";
Förklaring:
script-src: Tillåter skript från self och gstatic.com (för Flutter).
unsafe-inline: Nödvändigt för Flutter (men försök minska användningen).
img-src: Tillåter bilder från self och gstatic.com.
📌 Mindre Problem (Prioritet 3: Fixa När Tid Finns)
Dessa är förbättringar som inte är kritiska men kan förbättra UX och SEO.
1. Prestanda: Bildoptimering
Problem: Bilder laddas utan width och height (orsakar layout shifts).
Lösning: Använd Image.network med explicit storlek:
dart
Copy
Image.network(
'https://example.com/recept.jpg',
width: 300,
height: 200,
fit: BoxFit.cover,
),
2. SEO: Structured Data
Problem: Saknas schema.org-markup för recept (gör att Google kan visa "rich results").
Lösning: Lägg till JSON-LD i <head>:
html
Copy
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Recipe",
"name": "Lasagne",
"author": {
"@type": "Person",
"name": "Nils-Johan Gynther"
},
"datePublished": "2026-05-21",
"description": "En klassisk lasagne med köttfärs och bechamelsås.",
"prepTime": "PT30M",
"cookTime": "PT45M",
"totalTime": "PT1H15M",
"recipeYield": "4 portioner",
"recipeCategory": "Middag",
"recipeCuisine": "Italiensk",
"keywords": "lasagne, pasta, köttfärs",
"recipeIngredient": ["500g köttfärs", "250g ost", "1 paket lasagneplattor"],
"recipeInstructions": [
{
"@type": "HowToStep",
"name": "Börja med att fräsa köttfärsen.",
"text": "Fräs köttfärsen i en stekpanna tills den är genomstekt."
},
{
"@type": "HowToStep",
"name": "Lagra lasagnen i ugnen.",
"text": "Skikta lasagneplattor, köttfärs och sås i en ugnsform. Grädda i 45 minuter på 200°C."
}
]
}
</script>
Effekt: Google kan visa receptkort i sökresultaten (ökad klickfrekvens).
3. Tillgänglighet: Logisk Tab-Order
Problem: Användare kan tabba till element som är dolda eller off-screen.
Lösning: Använd FocusableAction i Flutter för att kontrollera tab-order:
dart
Copy
FocusableActionDetector(
autofocus: true,
onFocusChange: (hasFocus) {
if (hasFocus) {
// Hantera focus
}
},
child: MyWidget(),
),
📊 Sammanfattning av Prioriteringar
Prioritet
Problem
Lösning
Tidsuppskattning
Impact
1 (Kritisk)
Stora filer (2.9MB)
Byt till HTML-renderare, aktivera GZIP, cachea resurser
1-2 timmar
⭐⭐⭐⭐⭐ (Hög)
1 (Kritisk)
Lång JavaScript-exekvering (1.8s)
Code splitting, defer non-critical JS
2-4 timmar
⭐⭐⭐⭐⭐ (Hög)
1 (Kritisk)
user-scalable="no"
Ta bort från viewport-meta-taggen
5 minuter
⭐⭐⭐⭐ (Hög)
1 (Kritisk)
Saknas meta description
Lägg till i
5 minuter
⭐⭐⭐ (Medel)
1 (Kritisk)
Ogiltig robots.txt
Skapa korrekt robots.txt
10 minuter
⭐⭐⭐ (Medel)
2 (Viktig)
Saknas CSP-header
Lägg till i serverkonfigurationen
1 timme
⭐⭐⭐⭐ (Hög)
2 (Viktig)
Saknas alt-texter
Lägg till semanticLabel i Flutter
1-2 timmar
⭐⭐⭐ (Medel)
2 (Viktig)
Lång DOM-parsning
Minska onödiga widgets
1-2 timmar
⭐⭐ (Låg)
3 (Mindre)
Saknas structured data
Lägg till JSON-LD för recept
1 timme
⭐⭐ (Låg)
🛠️ Konkreta Åtgärder (Steg-för-Steg)
Steg 1: Fixa Prestanda (1-2 dagar)
Byt till HTML-renderare (sparar ~1.5MB):
Ändra index.html som visat ovan.
Aktivera GZIP/Brotli på servern.
Cachea statiska filer (1 år för JS/CSS).
Bygg appen med --release:
bash
Copy
flutter build web --release --no-source-maps
Ladd upp den optimerade versionen till din server.
Steg 2: Fixa Tillgänglighet (1 dag)
Ta bort user-scalable="no" från viewport.
Lägg till lang="sv" i <html>.
Lägg till alt-texter på alla bilder.
Lägg till semanticsLabel på knappar och ikoner.
Steg 3: Fixa SEO (1 dag)
Lägg till meta description.
Fixa robots.txt.
Lägg till structured data för recept.
Steg 4: Säkerhet (1 timme)
Lägg till CSP-header i serverkonfigurationen.
📈 Förväntade Resultat
Mätning
Nuvarande
Efter Fixar
Förbättring
Total storlek
2,978 KiB
~500-800 KiB
-70%
Laddningstid (LCP)
Timeout
< 2.5s
✅ Fixad
JavaScript-exekvering
1.8s
< 1s
-45%
Tillgänglighet
82/100
95+/100
+15 poäng
SEO
6/10
9/10
+50%
Säkerhet
Risk för XSS
Skyddad
✅ Fixad
🔍 Hur du Testar Förbättringarna
Kör Lighthouse igen i Chrome DevTools (F12 → "Lighthouse").
Testa på mobil (använd Chromes "Device Toolbar").
Använd WebPageTest (webpagetest.org) för att mäta prestanda globalt.
Testa tillgänglighet med:
WAVE Evaluation Tool
Skärmläsare (t.ex. NVDA eller VoiceOver).
💡 Extra Tips för Flutter-Webbappar
Använd flutter build web --target=lib/main_deferred.dart
Förbättrar code splitting.
Ladda Flutter asynkront
Lägg till async i <script>-taggen:
html
Copy
<script src="/flutter_bootstrap.js" async></script>
Använd flutter_tex för text-rendering
Minskar beroendet av CanvasKit.
Override Flutters default fonts
Använd systemtypsnitt för snabbare laddning:
css
Copy
@font-face {
font-family: 'Roboto';
src: local('Roboto'), local('Roboto-Regular');
}
🎯 Sammanfattning: Vad du ska göra nu
Fixa de kritiska prestandaproblemen (HTML-renderare, GZIP, caching).
Fixa tillgänglighetsproblemen (user-scalable, alt-texter, lang).
Fixa SEO (meta description, robots.txt).
Lägg till säkerhetsheaders (CSP).
Testa och iterera med Lighthouse.
+359
View File
@@ -0,0 +1,359 @@
flyerimporter.md
📌 Steg 1: Skapa en funktion för att extrahera text från PDF:en
Använd pdf-parse som primär metod och Tesseract.js som fallback för OCR.
Kod: extractFlyerText.ts
typescript
Copy
import * as fs from 'fs';
import * as pdf from 'pdf-parse';
import Tesseract from 'tesseract.js';
/**
* Extraherar text från en PDF-fil (flyer), med fallback till OCR.
* @param pdfPath Sökväg till PDF-filen.
* @returns Extraherad text.
*/
export async function extractFlyerText(pdfPath: string): Promise<string> {
try {
// Försök med pdf-parse först
const dataBuffer = fs.readFileSync(pdfPath);
const data = await pdf(dataBuffer);
if (data.text.trim()) {
return data.text;
}
} catch (error) {
console.warn('pdf-parse misslyckades, försöker med OCR...');
}
// Fallback till Tesseract.js för OCR
try {
const { data: { text } } = await Tesseract.recognize(pdfPath, 'swe', {
logger: (m) => console.log(m),
});
return text;
} catch (error) {
console.error('OCR misslyckades:', error);
throw new Error('Kunde inte extrahera text från PDF:en.');
}
}
📌 Steg 2: Skapa en funktion för att skicka texten till Mistral Tiny
Använd Mistral Tiny för att extrahera och strukturera all produktinformation från flyern.
Kod: importFlyerWithAI.ts
typescript
Copy
import { MistralClient } from '@mistralai/mistralai';
const mistral = new MistralClient({
apiKey: process.env.MISTRAL_API_KEY,
});
/**
* Skickar flyer-texten till Mistral Tiny för att extrahera strukturerad data.
* @param text Texten från flyern.
* @returns Strukturerad data (JSON-array).
*/
export async function importFlyerWithAI(text: string): Promise<any[]> {
const prompt = `
Du är en expert på att tolka svenska matvaruflyers (t.ex. från Willys).
Extrahera ALL produktinformation från följande text och returnera den som en JSON-array.
För varje produkt, inkludera:
- name: Produktnamn (fullständigt namn)
- weight: Vikt (om tillgänglig, t.ex. "150g", "Ca 1kg")
- origin: Ursprung/land/märke (om tillgänglig, t.ex. "FALKENBERG", "NYBERGS DELI • Sverige")
- price: Pris (som ett nummer, t.ex. 39.90)
- comparisonPrice: Jämförpris (som ett nummer, t.ex. 266.00)
- unit: Enhet (kg, st, förp, l, etc.)
- offer: Erbjudande (t.ex. ["Max 3 köp/hushåll", "Lägsta 30-dgrspris 125:00 kr"])
- category: Kategori (t.ex. "Fisk", "Kött", "Mejeri", "Grönsaker", "Frukt", "Dryck")
- validFrom: Giltig från (datum i formatet YYYY-MM-DD, om tillgängligt)
- validTo: Giltig till (datum i formatet YYYY-MM-DD, om tillgängligt)
Texten att tolka:
${text}
Returnera ENDAST en JSON-array. Inga andra kommentarer.
Exempel på utdata:
[
{
"name": "KALLRÖKT LAX, GRAVAD LAX",
"weight": "150g",
"origin": "FALKENBERG",
"price": 39.90,
"comparisonPrice": 266.00,
"unit": "kg",
"offer": ["Max 3 köp/hushåll"],
"category": "Fisk",
"validFrom": "2026-05-18",
"validTo": "2026-05-24"
}
]
`;
try {
const response = await mistral.chat({
model: 'mistral-tiny', // Använder den enklaste modellen
messages: [{ role: 'user', content: prompt }],
temperature: 0.1, // Låg temperatur för mer deterministiska svar
});
// Rensa upp JSON-strängen
const jsonString = response.choices[0].message.content
.replace(/```json|```/g, '')
.trim();
// Parsa JSON:en
return JSON.parse(jsonString);
} catch (error) {
console.error('Fel vid AI-import:', error);
throw new Error('Kunde inte importera flyern med AI.');
}
}
📌 Steg 3: Fullständigt importflöde
Kombinera text-extrahering och AI-import i ett fullständigt flöde.
Kod: flyerImportService.ts
typescript
Copy
import { extractFlyerText } from './extractFlyerText';
import { importFlyerWithAI } from './importFlyerWithAI';
/**
* Importerar en flyer (PDF) och returnerar strukturerad data.
* @param pdfPath Sökväg till PDF-filen.
* @returns Strukturerad data från flyern.
*/
export async function importFlyer(pdfPath: string) {
try {
// 1. Extrahera text från PDF:en
console.log('Extraherar text från flyern...');
const text = await extractFlyerText(pdfPath);
// 2. Skicka texten till Mistral Tiny för att extrahera data
console.log('Skickar text till Mistral Tiny för extrahering...');
const products = await importFlyerWithAI(text);
return {
success: true,
products,
text,
};
} catch (error) {
console.error('Fel vid import:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Okänt fel',
};
}
}
📌 Steg 4: API-Endpoint för flyer-import
Skapa en Express-endpoint för att hantera uppladdning och import av flyers.
Kod: flyerImportRouter.ts
typescript
Copy
import express from 'express';
import multer from 'multer';
import { importFlyer } from '../services/flyerImportService';
import * as fs from 'fs';
const router = express.Router();
const upload = multer({ dest: 'uploads/' });
// Endpoint för att ladda upp och importera en flyer
router.post('/import/flyer', upload.single('flyer'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'Ingen flyer uppladdad.' });
}
const result = await importFlyer(req.file.path);
// Rensa upp uppladdad fil
fs.unlinkSync(req.file.path);
if (!result.success) {
return res.status(500).json({ error: result.error });
}
res.json(result);
} catch (error) {
console.error('Fel vid flyer-import:', error);
res.status(500).json({ error: 'Kunde inte importera flyern.' });
}
});
export default router;
📌 Steg 5: Exempel på utdata
När du skickar texten från Willys flyer till Mistral Tiny, kommer du att få tillbaka en JSON-array med alla produkter, t.ex.:
json
Copy
[
{
"name": "KALLRÖKT LAX, GRAVAD LAX",
"weight": "150g",
"origin": "FALKENBERG",
"price": 39.9,
"comparisonPrice": 266.0,
"unit": "kg",
"offer": ["Max 3 köp/hushåll"],
"category": "Fisk",
"validFrom": "2026-05-18",
"validTo": "2026-05-24"
},
{
"name": "FLÄSKYTTERFILÉ",
"weight": "Ca 1kg",
"origin": "NYBERGS DELI • Sverige",
"price": 64.9,
"comparisonPrice": 64.9,
"unit": "kg",
"offer": ["Max 3 förp/hushåll"],
"category": "Kött",
"validFrom": "2026-05-18",
"validTo": "2026-05-24"
}
]
📌 Steg 6: Använda den extraherade datan
När du har den strukturerade datan, kan du:
Lagra den i din databas (t.ex. för att jämföra med inventory).
Visa den för användaren (t.ex. i en tabell).
Använda den för att generera recept (med eller utan AI).
Exempel: Lagra i databasen
typescript
Copy
// Antas att du har en Prisma-modell för flyer-produkter
await prisma.flyerProduct.createMany({
data: products.map((product) => ({
name: product.name,
weight: product.weight,
origin: product.origin,
price: product.price,
comparisonPrice: product.comparisonPrice,
unit: product.unit,
offer: JSON.stringify(product.offer),
category: product.category,
validFrom: product.validFrom ? new Date(product.validFrom) : null,
validTo: product.validTo ? new Date(product.validTo) : null,
})),
});
📌 Steg 7: Frontend-Integrering (Exempel: React)
Här är hur du kan integrera flyer-importen i din frontend:
Kod: FlyerImportForm.tsx
tsx
Copy
import { useState } from 'react';
import axios from 'axios';
function FlyerImportForm() {
const [file, setFile] = useState<File | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [result, setResult] = useState<any>(null);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!file) return;
setIsLoading(true);
const formData = new FormData();
formData.append('flyer', file);
try {
const response = await axios.post('/api/import/flyer', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
setResult(response.data);
} catch (error) {
console.error('Fel vid uppladdning:', error);
} finally {
setIsLoading(false);
}
};
return (
<div>
<h2>Importera flyer</h2>
<form onSubmit={handleSubmit}>
<input
type="file"
accept=".pdf"
onChange={(e) => setFile(e.target.files?.[0] || null)}
required
/>
<button type="submit" disabled={isLoading}>
{isLoading ? 'Importerar...' : 'Importera flyer'}
</button>
</form>
{result?.success && (
<div>
<h3>Importerade produkter ({result.products.length})</h3>
<table>
<thead>
<tr>
<th>Namn</th>
<th>Pris</th>
<th>Jämförpris</th>
<th>Kategori</th>
<th>Erbjudande</th>
</tr>
</thead>
<tbody>
{result.products.map((product: any, index: number) => (
<tr key={index}>
<td>{product.name}</td>
<td>{product.price} {product.unit}</td>
<td>{product.comparisonPrice} {product.unit}</td>
<td>{product.category}</td>
<td>{product.offer.join(', ')}</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
);
}
export default FlyerImportForm;
📌 Miljövariabler (.env)
env
Copy
# Mistral API-nyckel
MISTRAL_API_KEY=din_api_nyckel_här
+169
View File
@@ -0,0 +1,169 @@
# Plan för förbättrad dokumentationsstruktur
## Bakgrund
Projektet har idag över 20 `.md`-filer spridda över olika mappar, vilket gör det svårt att hitta information och förstå projektets helhet. Den nya strukturen syftar till att:
- **Göra dokumentationen användarvänlig** för både utvecklare och språkmodeller.
- **Optimerad för underhåll** med tydlig separation mellan aktiv och arkiverad dokumentation.
- **Skapa kontextuell sammanhang** genom länkade filer och tydliga hierarkier.
---
## Nuvarande problem
- **Fragmentering**: Relaterat innehåll är splittrat (t.ex. Flutter-dokumentation i `_archive/docs/flutter/` vs. backend-dokumentation i rotmappen).
- **Föråldrat innehåll**: Arkiverade filer blandas med aktiva (t.ex. `SESSION_CHECKPOINT_2026-05-12.md`).
- **Duplicering**: Samma koncept dokumenteras på flera ställen (t.ex. kategoriträd).
- **Icke-optimerat för språkmodeller**: Saknar tydlig hierarki och kontextuella länkar.
---
## Föreslagen struktur
```
/
├── docs/ # Huvudkatalog för ALL aktiv dokumentation
│ ├── 01-overview/ # Övergripande projektbeskrivning
│ │ ├── README.md # Huvudsaklig ingress (ersätter rot-README.md)
│ │ ├── ARCHITECTURE.md # Systemarkitektur
│ │ └── GLOSSARY.md # Termer och definitioner
│ │
│ ├── 02-setup/ # Installation och konfiguration
│ │ ├── INSTALL.md # Miljökrav, beroenden, första uppstart
│ │ ├── CONFIG.md # Konfigurationsfiler (.env, Docker, etc.)
│ │ └── TROUBLESHOOTING.md # Vanliga problem och lösningar
│ │
│ ├── 03-development/ # Utvecklingsguider
│ │ ├── CONTRIBUTING.md # Bidragsregler, kodstandard, PR-process
│ │ ├── WORKFLOWS.md # Git-flöden, branch-strategi, CI/CD
│ │ ├── DATABASE.md # Schema, migrationer, seedning
│ │ ├── API.md # Backend-API:er, Swagger-länkar
│ │ ├── FLUTTER.md # Flutter-specifik dokumentation
│ │ └── MICROSERVICES.md # Importer, AI, etc.
│ │
│ ├── 04-deploy/ # Driftsättning och underhåll
│ │ ├── DEPLOY.md # Steg-för-steg deploy
│ │ ├── MAINTENANCE.md # Underhållsskript, backup, monitorering
│ │ └── SCALING.md # Prestanda, skalning
│ │
│ ├── 05-features/ # Djupdyk i funktioner
│ │ ├── RECIPE_IMPORT.md # Kvittosimport och flyer-parsing
│ │ ├── CATEGORY_TREE.md # Kategorihantering och L3-integration
│ │ ├── SHOPPING_LIST.md # Inköpslistor och flyer-integration
│ │ └── ... # Övriga funktioner
│ │
│ └── 06-archive/ # Arkiverade dokument
│ ├── sessions/ # Gamla sessionsanteckningar
│ ├── legacy/ # Föråldrade planer
│ └── flutter_legacy/ # Gamla Flutter-dokument
├── .github/ # GitHub-specifika filer
│ ├── COPILOT_INSTRUCTIONS.md
│ └── ...
└── ... # Övriga projektfiler (backend/, flutter/, etc.)
```
---
## Migrationsplan
### Steg 1: Skapa den nya strukturen
```bash
mkdir -p docs/{01-overview,02-setup,03-development,04-deploy,05-features,06-archive/sessions,06-archive/legacy,06-archive/flutter_legacy}
```
### Steg 2: Flytta och uppdatera filer
| Källfil(er) | Målfil | Åtgärd |
|---------------------------------------------|-----------------------------------------|------------------------------------------------------------------------|
| `TEKNISK_BESKRIVNING.md` | `docs/01-overview/ARCHITECTURE.md` | Extrahera arkitekturavsnitt. |
| `TEKNISK_BESKRIVNING.md` (deploy) | `docs/04-deploy/DEPLOY.md` | Extrahera deploy-avsnitt. |
| `TEKNISK_BESKRIVNING.md` (databas) | `docs/03-development/DATABASE.md` | Extrahera databasavsnitt + lägg till underhållsskript. |
| `flyerimporter.md` | `docs/05-features/RECIPE_IMPORT.md` | Uppdatera med nya flöden och API-endpoints. |
| `_archive/docs/flutter/*` | `docs/03-development/FLUTTER.md` | Slå samman och uppdatera Flutter-dokumentation. |
| `README.md` | `docs/01-overview/README.md` | Uppdatera med länkar till nya dokument. |
| `.github/copilot-instructions.md` | `.github/COPILOT_INSTRUCTIONS.md` | Flytta och uppdatera. |
| `_archive/docs/SESSION_*.md` | `docs/06-archive/sessions/` | Arkivera gamla sessionsanteckningar. |
| `MVP_CHECKLISTA.md` | `docs/06-archive/legacy/` | Arkivera föråldrade planer. |
### Steg 3: Skapa nya filer
| Fil | Syfte |
|-----------------------------|-----------------------------------------------------------------------|
| `docs/03-development/CONTRIBUTING.md` | Standardiserade bidragsregler (branches, PR, code review). |
| `docs/03-development/API.md` | Dokumentation av backend-API:er (OpenAPI-länkar, exempel). |
| `docs/03-development/MICROSERVICES.md` | Beskrivning av microservices (Importer, AI). |
| `docs/05-features/CATEGORY_TREE.md` | Djupdyk i kategorihantering och L3-integration. |
| `docs/04-deploy/MAINTENANCE.md` | Backup, monitorering, logghantering. |
---
## Exempel på innehåll
### `docs/01-overview/ARCHITECTURE.md`
```markdown
# Systemarkitektur
```mermaid
graph TD
A[Flutter App] -->|HTTP/REST| B[Backend API]
B -->|Prisma Client| C[MariaDB]
B -->|gRPC| D[Importer Microservice]
D -->|HTTP| E[Externa API:er]
C -->|Seed| F[Initial Data]
```
## Komponenter
1. **Flutter App**: State management med Riverpod, UI med Material Design.
2. **Backend API**: NestJS + Prisma, autentisering via JWT.
3. **Microservices**: Importer (Node.js + Puppeteer), AI (Python + Mistral).
```
---
### `docs/03-development/DATABASE.md`
```markdown
# Databas
## Schema
```mermaid
erDiagram
User ||--o{ Recipe : creates
User ||--o{ InventoryItem : owns
Category ||--o{ Product : "L3"
FlyerSession ||--o{ FlyerItem : contains
```
## Migrationer
### Standardflöde
1. Uppdatera `prisma/schema.prisma`.
2. Skapa migration:
```bash
npx prisma migrate dev --name add_feature_x
```
3. Testa lokalt:
```bash
npx prisma migrate reset
npx prisma db seed
```
### Underhållsskript
- **Rensa databas** (behåll kategorier):
```bash
./deploy.sh --clean-database
```
> Obs! Uppdatera `prisma/maintenance/clean-database.sql` när nya tabeller läggs till.
```
---
## Fördelar
- **Lätt att hitta**: Logisk gruppering (t.ex. all Flutter-dokumentation på ett ställe).
- **Uppdaterad**: Arkiverade filer separerade från aktiv dokumentation.
- **Optimerad för språkmodeller**: Tydliga rubriker, länkar, och maskinläsbara format.
- **Underhållbar**: Modulär struktur gör det enkelt att uppdatera enskilda delar.
---
## Nästa steg
1. **Godkänn planen**: Bekräfta att strukturen uppfyller era behov.
2. **Implementera**: Skapa den nya strukturen och flytta filer stegvis.
3. **Uppdatera länkar**: Se till att alla referenser pekar på de nya platserna.
+118
View File
@@ -0,0 +1,118 @@
# Plan: Anpassad multiplattformsplan for recipe-app
## Mal
Gora Flutter-klienten i `flutter/` till en riktig multiplattformsklient (web + Android + iOS) utan att bryta befintligt webbflode via Docker/Caddy, och med tydlig miljohantering for API-anrop pa mobil.
## Nulagesanalys (projektanpassad)
- Flutter-projektet ar i praktiken web-only just nu: `flutter/` saknar `android/` och `ios/`.
- Webbbygget ar redan etablerat och stabilt via Docker:
- `flutter/Dockerfile` bygger `flutter build web --dart-define=API_BASE_URL=/api`.
- `compose.flutter.yml` och `flutter/Caddyfile` proxar `/api/*` till `recipe-api:8080`.
- API-basurl hanteras redan med `--dart-define` (bra grund for multiplattform):
- `flutter/lib/core/api/api_client.dart`
- `flutter/lib/features/import/data/import_repository.dart`
- Token-lagring ar forberedd for mobil men inte implementerad:
- `flutter/lib/core/platform/token_storage.dart`
- `flutter/lib/core/platform/platform_providers.dart` (har TODO om secure storage).
## Viktig skillnad mot gamla planen
- Ingen hardkodad `Config.apiUrl` med fasta domaner ska inforas som huvudlosning.
- Projektet anvander redan `String.fromEnvironment('API_BASE_URL')`; vi behaller detta och utokar till mobil.
- Befintlig Docker-setup for web ska inte ersattas, bara kompletteras med mobil-byggflode.
## Foreslagen implementation
### Fas 1: Aktivera plattformsstommar utan att rora webdeploy
1. Skapa Android/iOS-mappar i `flutter/`:
- `flutter create --platforms android,ios .`
2. Verifiera att webfiler och befintliga Dart-filer inte overskrivs pa ett destruktivt satt.
3. Bekrafta att Docker-webbygget fortfarande fungerar oforandrat.
**Leverabel:** Projektet innehaller `flutter/android/` och `flutter/ios/` samtidigt som webflodet ar intakt.
### Fas 2: Plattformsaker konfiguration av API-basurl
1. Standardisera API-konfiguration kring en enda kontraktspunkt:
- Behall `API_BASE_URL` via `--dart-define`.
2. Satt tydliga miljoer:
- Web i Docker: `API_BASE_URL=/api` (som idag).
- Android emulator lokalt: t.ex. `http://10.0.2.2:8080/api` (vid lokal backend utan reverse proxy).
- Fysisk mobil/test/prod: publik HTTPS-url (doman som ar natbar utanfor Docker).
3. Se over alla direkta API-basar i Flutter-koden sa att de gar via samma pattern (inga hardkodade hostnamn).
**Leverabel:** Samma kodbas fungerar pa web och mobil genom miljoinjektering, inte forks av API-klient.
### Fas 3: Mobilanpassad auth/tokenlagring
1. Implementera `SecureTokenStorage` med `flutter_secure_storage` for mobil.
2. Uppdatera `platform_providers.dart` till plattformsval:
- Web -> befintlig `WebTokenStorage`.
- Android/iOS -> `SecureTokenStorage`.
3. Verifiera att inloggning/logout/session beter sig lika mellan web och mobil.
**Leverabel:** JWT lagras sakert pa mobil, befintligt webbeteende bibehalls.
### Fas 4: UI- och UX-hardning for mindre skarmar
1. Identifiera skarmar med hog informationsdensitet (admin/import/tabeller).
2. Lagg in responsiva brytpunkter med `LayoutBuilder`/`MediaQuery` dar det behovs.
3. Prioritera funktionellt minimum for mobil i forsta iteration:
- Login
- Inventarie
- Receptlista
- Grundlaggande importfloden
4. Markera admin-tunga vyer som sekundara om de inte ar mobilkritiska i fas 1.
**Leverabel:** Nyckelfloden ar anvandbara pa telefon utan horisontell overflow eller blockerande layoutfel.
### Fas 5: Build- och releaseflode (utan att blanda ihop med Docker-runtime)
1. Dokumentera separata kommandon:
- Web (befintligt): Docker/compose.
- Android: `flutter build apk --release` (och ev. `appbundle`).
- iOS: `flutter build ios --release` (kraver macOS/Xcode).
2. Behall principen: mobilappar kor inte i Docker; Docker far anvandas som byggmiljo dar det ar rimligt.
3. Om CI ska byggas senare: separera web-jobb och mobil-jobb for tydlighet.
**Leverabel:** Reproducerbar byggprocess for web och mobil med tydlig ansvarsskillnad.
### Fas 6: Test- och verifieringsplan
1. Statisk kvalitet:
- `flutter analyze`
- `flutter test`
2. Plattformsverifiering:
- Web via befintlig container
- Android emulator + fysisk enhet
- iOS simulator (pa macOS)
3. Natverksverifiering:
- Bekrafta att mobil kan na vald `API_BASE_URL` over HTTPS/CORS/proxyregler.
4. Regression:
- Inloggning, token-refresh/logout
- CRUD i inventarie/recept
- Importendpoints med storre payloads
**Leverabel:** Checklista med passerade verifieringspunkter innan distribution.
## Konkreta filer som sannolikt berors
- `flutter/pubspec.yaml` (nytt beroende for secure storage)
- `flutter/lib/core/platform/platform_providers.dart`
- `flutter/lib/core/platform/token_storage.dart` (ev. endast kontraktsjustering)
- Ny fil: `flutter/lib/core/platform/secure_token_storage.dart`
- Mobilplattformar som genereras: `flutter/android/**`, `flutter/ios/**`
- Dokumentation: `README.md` och/eller `TEKNISK_BESKRIVNING.md` (kommandon och miljoexempel)
## Risker och hantering
- iOS-bygg kan inte verifieras i Windows/Linux-miljo -> hanteras med separat macOS-steg.
- Hardkodade URL:er kan smyga sig in i featurekod -> hanteras med kodsok + central konfigpolicy.
- UI-regression pa web vid responsiva andringar -> hanteras med web-regressionstest av kritiska vyer.
## Prioriterad ordning for implementation
1. Fas 1 (plattformsstommar)
2. Fas 2 (API-konfiguration)
3. Fas 3 (secure token storage)
4. Fas 6 del 1 (analyze/test tidigt)
5. Fas 4 (mobil UI-hardning)
6. Fas 5 + Fas 6 slutlig verifiering och dokumentation
## Definition of Done
- Flutter-projektet bygger for web + android (och ios dar macOS finns).
- Mobil och web anvander samma API-konfigmodell via `--dart-define`.
- Mobil lagrar token sakert; webflodet ar oforandrat.
- Minst nyckelfloden login/inventarie/recept/import ar verifierade pa mobil.
- Dokumentationen beskriver exakt hur man bygger och kor respektive plattform i detta repo.
-358
View File
@@ -1,358 +0,0 @@
# Nästa steg
> Förslag på vad vi kan ta tag i nästa gång vi öppnar projektet.
> Se [README.md](README.md) för funktionsöversikt och [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk detaljerinformation.
> Se [AI-FUNKTIONER.md](AI-FUNKTIONER.md) för planerade AI-funktioner.
---
|---|---|
| Inventorie (CRUD, konsumtion, historik) | ✅ Klart |
| Recept (skapa, visa, importera, matchning) | ✅ Klart |
| Receptredigering och borttagning (JWT-autentisering) | ✅ Klart |
| Snabbimport (URL/PDF/bild/ICA) | ✅ Klart |
| Kvittoimport (Mistral AI, OCR, alias) | ✅ Klart |
| Import-microservice (importer-api, backend-till-backend) | ✅ Klart (2026-04-30) |
| Kvittoimport — granskningsflöde i Flutter (edit, merge, destination) | ✅ Klart (2026-05-01) |
| Matplanering (veckovy, inköpslista) | ✅ Klart |
| Matplan — portionsjustering per dag | ✅ Klart |
| Matplan — inventariejämförelse (backend) | ✅ Klart |
| Matplan — inventariejämförelse (frontend-vy) | ✅ Klart (✅/⚠️/❌ integrerat i inköpslistan) |
| Baslager (lista, lägg till, ta bort) | ✅ Klart (global v1, user-scope planerad) |
| Admin: Produkter (edit, merge, duplicate, restore, reset) | ✅ Klart |
| Admin: Bulk-kategorisering | ✅ Klart |
| Receptredigering (frontend UX) | ✅ Klart |
| Receptbilder (upload URL) | ✅ Klart |
| Autentisering (JWT, Auth.js v5, User-modell) | ✅ Klart |
| Klient-side JWT via useAuthFetch() | ✅ Klart |
| SessionProvider i root layout | ✅ Klart |
| Användarprofil (firstName, lastName, email) | ✅ Klart |
| Produktkategorier — hierarkisk struktur (3 nivåer) | ✅ Klart |
| Kategori-seed (seed_all.sql, komplett träd, enda sanningskällan) | ✅ Klart |
| Kategoritilldelning i admin-UI | ✅ Klart |
| Taggning av produkter | ✅ Klart |
| Näringsvärden på produkter | ✅ Klart (schema + API) |
| Seed produktdata med kategoritilldelning | ✅ Ersatt — seed innehåller nu enbart kategorier (2026-05-02) |
| Användarspecifika produkter (UserProduct) | ✅ Klart (2026-05-02) — `Product.ownerId` obligatorisk, globala produkter borttagna |
| Användarroller (user / admin) | ✅ Klart |
| Användarhantering i admin-UI | ✅ Klart |
| Profilsida med flikar (Min profil / Användare / Databas med undertabbar) | ✅ Klart |
| Teknisk skuld — oanvända InventoryItem-fält | ✅ Klart (migration 20260418) |
| Teknisk skuld — redirect-routes städade | ✅ Klart |
| Premium-plan (isPremium på User, Free/Paid-dropdown) | ✅ Klart |
| AI-modul (AiService, Mistral-kategorisering, fallback) | ✅ Klart |
| Admin: AI-kategorisering per produkt ("Fråga AI") | ✅ Klart |
| Admin: AI-bulk-kategorisering av okategoriserade produkter | ✅ Klart |
| Produktstatus (pending / active / rejected) | ✅ Klart |
| Admin: Väntande produktförslag (pending-sida) | ✅ Klart |
| Kvittoimport — AI-kategorisuggestion för premium-användare | ✅ Klart |
| Säkerhetshuvuden — Caddy (globala headers) | ✅ Klart |
| Säkerhetshuvuden — Next.js CSP | ✅ Klart |
| Säkerhetshuvuden — NestJS Helmet (backup) | ✅ Klart (aktiveras vid nästa rebuild) |
| Buggfix: "Vad behöver jag köpa" — flimmer och scroll | ✅ Klart |
| Startsida — produktadmin-länk borttagen | ✅ Klart |
| Avancerad AI-integration (veckoplanering, receptförslag) | ❌ Planerad |
| EAN-skanning via Open Food Facts API | ❌ Planerad |
## Status — senast genomgånget: 2026-05-02
### Nyheter och förbättringar
- **Kvittoimport — brödregler och guardrails utökade (2026-05-03)** — `ruleBasedCategorySuggestion()` och `applyContradictionGuard()` täcker nu fler brödsignaler (t.ex. roast'n toast, toastbröd, formbröd, lantbröd, fullkornsbröd, franska, limpan/brod) och remappar felaktiga AI-träffar till `Bröd & Kakor > Bröd`/`Rostbröd`.
- **Prisma-schema justerat för Product (2026-05-03)** — `Product.brand` är borttaget från Prisma-modellen och från produktuppdatering i backend eftersom kolumnen saknas i aktuell databas. Detta förhindrar Prisma-felet `The column recipe_app.Product.brand does not exist in the current database` vid t.ex. `findUnique`.
- **Produkter user-scoped — ny databasarkitektur (2026-05-02)** — `Product.ownerId` är nu obligatorisk (non-nullable). Alla globala seed-produkter är borttagna. Varje produkt ägs av en enskild användare och raderas vid kontoradering (CASCADE). `seed_all.sql` innehåller nu enbart kategorier. Kvittoimportens matchning filtrerar på `ownerId = userId` från JWT. Se TEKNISK_BESKRIVNING.md för fullständig beskrivning.
- **Kategorier utökade (2026-05-02)** — Nya L2/L3-noder: `Bröd & Kakor > Kondis & fika > Kaffebröd` (wienerbröd, donuts, munkar m.m.) och `Dryck > Te & choklad > Te` (chai, vanilla chai, ceylon te m.m.). Nya L3-noder under `Mejeri, ost & ägg > Allergi mejeri`: Laktosfri mjölk, Filmjölk & Yoghurt, Kvarg & Cottage cheese, Matfett, Allergi matlagning.
- **Regelbaserad kategoridetektion utökad (2026-05-02)** — `ruleBasedCategorySuggestion()` täcker nu Te (te, tea, chai, tepas) och Kaffebröd (wienerbröd, donut, munk, croissant, kanelbulle, bakelse, semla m.fl.) utöver befintliga mejeri-regler.
- **AI-guardrail (2026-05-02)** — `AiService.suggestCategory()` remappar nu `low`/`medium`-konfidenspoäng till L1-föräldern istället för att returnera potentiellt fel L2/L3-kategori.
- **Förbättrad produktmatchning (2026-05-02)** — `findWordMatch` normaliserar nu diakritik (ä→a, ö→o, å→a) före jämförelse och tillåter enstaka stark partiell matchning för ord ≥5 tecken (löser t.ex. "vispgrädde" → produkt "grädde").
- **Kategorisuggest för matchade produkter (2026-05-02)** — `matchProducts()` läser nu in `categoryRef` för matchade produkter och sätter `categorySuggestion` direkt. `enrichWithAiCategories()` körs för alla items utan `categorySuggestion`, inte bara ej matchade.
- **Kvittoimport Fas 6b klar (2026-05-01)** — Flutter-granskningsflödet färdigt: per-rad checkbox, redigeringsdialog med destination-väljare (Inventarie/Baslager), merge-förhandsvisning, parallell laddning av inventarie och baslager, snackbar med separat räkning.
- **Kvittoimport Fas 6c klar (2026-05-01)** — Separering av AI-chip och produktsuggestions-chip, produktnamns-normalisering, och validering av AI-kategorier.
- **Microservice-importer integrerad (2026-04-30)** — All import-logik (URL-skrapning, OCR, PDF-parsning, AI-kvittoparsning) delegeras nu till `importer-api` som körs som intern Docker-tjänst. `recipe-api` behåller produktmatchning och AI-kategorisering. Se [migrering-MSI.md](migrering-MSI.md) för detaljer.
- **User-scope för pantry och matplan** — Alla baslager- och matplansdata är nu per användare. Backend och Prisma-schema är migrerade.
- **Robust bildimport** — Bild-URL normaliseras, laddas ner och optimeras i backend. Bilden kopplas till receptet och raderas vid delete. Diagnostikloggning på alla steg.
- **Importflöde** — Quick-import och receipt-import har förbättrats med robust multipart-hantering, timeout, och felhantering.
- **Flutter-parity** — Matplan, inventarie, baslager och receptflöden är nu fullt migrerade till Flutter med user-scope och robust felhantering.
### Kända begränsningar
- Kvittoimport: användare utan egna produkter får inga produktmatchningar — enbart kategorisuggestioner via regler/AI (förväntat beteende, byggs upp allt eftersom).
- `ReceiptAlias` är ännu inte user-scoped (alias är fortfarande globala — se aliasstrategin nedan).
- `receipt_import_tab.dart` (~1 400 rader) och `swipeable_inventory_tile.dart` är ännu inte lokaliserade.
- Vissa adminfunktioner och avancerad AI-integration är planerade men ej migrerade.
---
## Nästa steg
1. Bygg upp UI-flöde för att skapa/spara egna produkter vid kvittobekräftelse (POST /products med `ownerId` från JWT).
2. Inför user-scope för `ReceiptAlias` (lägg till `userId`, unika index, admin-godkänd global fallback).
3. Implementera automatisk alias-inlärning vid manuell korrigering i importflödet.
4. Kvittoimport steg 2: persistenta förpackningsfält i inventarie (packCount, packSizeQuantity, packSizeUnit) + visning/redigering i inventory-UI.
5. Lokalisera `receipt_import_tab.dart` och `swipeable_inventory_tile.dart`.
6. Smoke-test på testdomän och avstämning.
7. Planera och påbörja avancerad AI-integration och EAN-skanning.
8. AI-kategorisering: kandidatbegränsning före AI (narrowing till relevant gren/kandidatlista istället för hela kategoriträdet).
9. AI-kategorisering: egen confidence-score i backend (regel/lexikal signal + AI-signal), inte enbart modellens confidence.
10. AI-kategorisering: logga användarkorrigeringar och bygg feedbackloop för nya regler/synonymer.
11. AI-kategorisering: utvärdera alternativ modell för kategorisering med A/B-test på historiska kvittorader.
## Beslut 2026-05-02 - Aliasstrategi för kvittoimport
- Alias ska vara hybrid: user-scope som standard, globalt alias endast via admin-verifiering.
- Syfte: minimera felträffar mellan användare och samtidigt bygga en kvalitetssäkrad global kunskapsbas.
- Tekniskt nästa steg i backend:
- Lägg till user-scope för `ReceiptAlias` (t.ex. `userId` + relevanta unika index).
- Behåll global alias som fallback för admin-godkända mappningar.
- Tekniskt nästa steg i klient:
- Spara manuell korrigering som alias (default: user-scope).
- Eventuell admin-toggle för att spara alias globalt.
---
## Beslut 2026-04-22 — Pantry och matplan på user-nivå
Pantry och matplan ska inte vara globala. De ska vara användarspecifika i backend.
### Varför
- Nuvarande implementation delar baslager och matplan mellan alla användare.
- Det ger felaktig inköpslogik och otydligt ägarskap i multi-user-läge.
### Krävda backend-ändringar (prio)
1. **PantryItem user-scope i Prisma**
- Lägg till `userId` i `PantryItem` + relation till `User`.
- Ändra unik regel från global `productId` till kombinationen `userId + productId`.
- Lägg index på `userId`.
2. **Pantry API user-scope**
- `GET /api/pantry` ska returnera inloggad användares baslager.
- `POST /api/pantry` ska skapa för inloggad användare.
- `DELETE /api/pantry/:id` ska bara kunna ta bort poster som ägs av inloggad användare.
3. **MealPlan user-scope i Prisma och service**
- Lägg till `userId` i `MealPlanEntry` + relation till `User`.
- Ändra unik regel från global `date` till `userId + date`.
- Filtrera `findByRange`, `upsert` och `removeByDate` per inloggad användare.
4. **Matplanens inventory-compare ska använda user-scope pantry**
- Pantry-uppslag i `inventoryCompare` ska filtreras på samma `userId`.
### Migrering och release-strategi
1. Ta migration för `PantryItem` + `MealPlanEntry`.
2. Backfill: tilldela befintliga poster till vald default-användare (eller rensa och starta om datan i testmiljö).
3. Uppdatera backend-kontrollers till `@CurrentUser()`-mönster.
4. Verifiera med två testanvändare att data är isolerad.
5. Därefter uppdateras Flutter/Next-klienterna mot de nya kontrakten.
---
## Notering 2026-04-20
## Notering 2026-04-21
- Rollskydd i frontend (middleware) och backend (admin på endpoints) är nu heltäckande.
- Rate limiting, CORS-låsning och JWT_SECRET-krav är på plats.
- Konsol-loggar borttagna ur backend.
- Kvar: Kryptering av PII (e-post, namn i databasen), automatisering av seed_all.sql vid fresh install.
Den senaste versionen av `db/seeds/seed_all.sql` är pushad till utvecklingsmiljön, men har **ännu inte körts** på databasen. Nästa gång vi vill uppdatera kategoriträdet måste seed-scriptet köras manuellt:
```bash
docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql
```
Se även TEKNISK_BESKRIVNING.md för detaljer om seed-processen.
---
## Vägen till produktlansering
- [Säkerhet & Data](produktlansering.md#1-säkerhet--data)
- [DevOps & Stabilitet](produktlansering.md#2-devops--stabilitet)
- [Testning & Kvalitet](produktlansering.md#3-testning--kvalitet)
- [Funktionella förbättringar](produktlansering.md#4-funktionella-förbättringar)
- [Risker & Flaskhalsar](produktlansering.md#5-risker--flaskhalsar)
---
## Prioriterade förbättringar
### 1. ICA-skraparen missar receptbild ✅
**Problem:** Vid import av ICA-recept via URL hämtas inte receptbilden korrekt.
- Felsök parsern i `recipe-document-converter/src/parser.ts` — kontrollera att ICA:s bild-selektor fortfarande matchar sidans HTML-struktur
- ICA kan ha ändrat klassnamn eller lazy-load-mekanism sedan skraparen skrevs
- Verifiera med ett konkret ICA-recept-URL och logga vad `imageUrl` returnerar
### 2. Seed-data ✅
**Klart.** `db/seeds/seed_all.sql` är den enda sanningskällan för kategorier och produkter.
Filen:
- TRUNCATEar `Category` och nollställer `Product.categoryId`
- Bygger hela kategoriträdet (nivå 13) utan dubbletter
- Insertar ~190 produkter (INSERT IGNORE)
- Kopplar produkterna till rätt kategori via JOIN-subqueries
Körs manuellt: `docker exec -i recipe-db mariadb -uroot -p"$DB_PASS" recipe_app < db/seeds/seed_all.sql`
### 2. Refactor: seed_all.sql som del av fresh install
**Mål:** Fresh install ska inte kräva ett manuellt seed-steg.
Nuläge:
- `db/init/001-init.sql` (MariaDB auto-init) skapar bara en bootstrap-check-tabell
- Prisma-migrationen `20260417310000_add_category_tree` seedar kategorier en gång, men seed_all.sql skriver över dem
- `seed_all.sql` körs **inte** automatiskt vid deploy eller fresh install
Alternativ:
- **Alt A:** Flytta seed_all.sql-logiken till `db/init/001-init.sql` — körs automatiskt av MariaDB-containern vid första start
- **Alt B:** Skapa en `prisma/seed.ts` som körs via `prisma db seed` — standard NestJS/Prisma-mönster, kan inkluderas i deploy.sh
- **Alt C:** Lägg till `seed_all.sql` som ett steg i `deploy.sh` (körs alltid, idempotent p.g.a. TRUNCATE+INSERT IGNORE)
Oavsett alternativ bör de gamla kategori-INSERTs i `20260417310000_add_category_tree/migration.sql` tas bort eller kommenteras ur för att undvika förvirring.
### 2. Användarroller och full användarhantering ✅
**Klart.**
Systemet har nu fullständig rollbaserad åtkomstkontroll och ett komplett användarhanteringsgränssnitt inbyggt i profilsidan.
**Rollsystemet:**
- **Prisma-migration** (`20260418100000_add_user_role`) — fältet `role String @default("user")` lades till på `User`-modellen
- **`@Roles('admin')`-dekoratorn** (`auth/decorators/roles.decorator.ts`) — använder `SetMetadata` för att markera endpoints
- **`RolesGuard`** (`auth/roles.guard.ts`) — registrerad globalt som `APP_GUARD`; läser rollmetadata, kastar 403 om rätt roll saknas
- **JWT inkluderar nu `role`** — `jwt.strategy.ts` returnerar `{userId, username, role}`, `auth.service.ts` signerar med `role` i payload
- **Bootstrap-användare** (`users/admin-bootstrap.service.ts`) — `OnApplicationBootstrap` skapar/uppdaterar Nadmin, Padmin, user1 och user2 vid varje uppstart via miljövariabler
- **Skyddade produkt-endpoints** — `@Roles('admin')``merge`, `delete`, `restore`, `reset-all`, `bulk-update`, `backfill-canonical` i `products.controller.ts`
**Backend-endpoints för användarhantering (alla kräver admin-roll):**
- `GET /api/users` — lista alla användare
- `PATCH /api/users/:id/role` — ändra roll
- `POST /api/users` — skapa ny användare (validering: unikt användarnamn och e-post)
- `DELETE /api/users/:id` — ta bort användare (skyddad: kan inte ta bort sig själv)
- `POST /api/users/:id/reset-password` — genererar tillfälligt lösenord, returnerar meddelandetext + lösenord
- `PATCH /api/users/:id/email` — uppdatera e-postadress
**Profilsidan med flikar (`/profil`):**
- `?tab=profil` — Min profil (alla användare)
- `?tab=anvandare` — Användare (enbart admin): skapa, ta bort, rollbyte, e-postbyte, lösenordsåterställning med kopierings-modal
- `?tab=databas` — Databas (enbart admin): produktadmin, nu med undertabbar:
- **Varor:** lista och redigera aktiva produkter
- **Skapa / Slå ihop:** skapa ny produkt, återställ produktdatabas, slå ihop dubbletter
- **Papperskorg:** visa mjukraderade produkter, återställ eller radera permanent
- `/admin/users` omdirigerar till `/profil?tab=anvandare`
- Navigeringslänken "👥 Användare" går direkt till `/profil?tab=anvandare`
### 3. Matplan-vy (frontend-polish) ✅
**Klart.**
Inköpslistan och inventariejämförelsen är sammanslagna till en enhetlig vy med tre statusnivåer:
- ❌ Saknas helt — visar hur mycket som behövs köpas
- ⚠️ Delvis hemma — visar hur mycket mer som behövs + vad som finns
- ✅ Finns hemma — markeras nedtonat, ingen köpindikering
Listan sorteras automatiskt: saknade ingredienser överst, hemma-ingredienser underst. En sammanfattningsrad visar totalt antal per statuskategori.
### 4. Teknisk skuld (underhåll)
**Mål:** Minska komplexitet och risk för buggar.
#### A. CanonicalNameForm och NameForm ✅
Filerna var redan borttagna — inga aktiva imports hittades. Inget att göra.
#### B. Oanvända fält på InventoryItem ✅
Följande 6 fält togs bort via Prisma-migration (`20260418000000_remove_unused_inventory_fields`):
`priority`, `shelfNote`, `isOnSale`, `priceLevel`, `proteinType`, `isLeftover`
- Schema, DTOs (create + update), service och frontend-typen är städade.
- `opened` och `suitableFor` behölls — de används i UI.
#### C. Validering av DTO:er i admin-actions ✅
Redan implementerat — `trim()` + max 100 tecken på alla fält i `actions.ts`. Inget att göra.
#### D. Routing-städning för kvitto och import ✅
`frontend/app/kvitto/page.tsx` och `frontend/app/recipes/import/page.tsx` är borttagna.
`/import` täcker båda use-cases via flikar.
#### E. Seed-data i versionshantering ✅
`data/matvaror_sverige.csv` och `data/seed_products.sql` behålls i git för reproducerbarhet. Inga ändringar i `.gitignore`.
### 5. Avancerad AI-integration
**Mål:** Smarta receptförslag och veckoplanering baserat på inventarie och kampanjdata.
AI-infrastrukturen är nu på plats (`AiService`, `mistral-small-2603`, premium-plan). Kategorisering för produkter och kvittoimport är implementerat. Nästa steg:
- **"Vad ska jag laga idag?"** — Receptförslag baserat på vad som finns i inventariet; kan byggas direkt ovanpå befintliga inventory- och recipe-endpoints
- **Veckoplanering med AI** — Generera ett veckoschemat baserat på inventarie, preferenser och ev. kampanjpriser (kräver extern datakälla)
- Kräver: tydlig API-design, kostnadskontroll och modellval per use-case
---
## AI-loggning och audit
- detta behöver ske innan nästa Ai-integration (receptförslag, veckoplanering) implementeras, för att säkerställa att vi har en robust lösning på plats för att spåra och analysera AI-anrop och resultat
- Utforska och utveckla lösning för loggning och audit av AI-input och output
- Syfte: möjliggöra spårbarhet, felsökning och kvalitetssäkring av AI-funktioner
- Implementera loggning på ett sätt som inte lagrar personuppgifter eller känslig data i klartext
- Utvärdera om loggar ska lagras i separat databas eller fil, och hur de kan användas för analys och förbättring
---
## Enhetsstöd och konvertering
- Se över om vi behöver utöka antalet stödda enhetstyper (t.ex. fler volym- eller portionsenheter)
- Utveckla och förbättra normaliseringen i `recipeservice.normalizeUnit`
- Se över och testa konverteringslogiken för enheter (t.ex. edge cases, felhantering)
- Fundera på om AI kan användas för att tolka, normalisera eller konvertera enheter och mängder smartare
---
## Enhetstester
Jest + ts-jest är uppsatt. Tester finns för:
- `normalize-name.ts` — 10 tester
- `base.parser.ts` (`parseIngredientLine`) — 12 tester
- `recipes.service.ts` (`normalizeUnit`, `convertUnit`) — 17 tester
Kör med `npm test` i `backend/`.
---
## Planerade funktioner
### Säkerhet — kryptering av känslig användardata
**Mål:** Kryptera känsliga fält (t.ex. e-postadresser, personuppgifter) i databasen med en hybridlösning.
Förslag på approach:
- **AES-256-GCM** för symmetrisk kryptering av data i vila (kolumnnivå i MariaDB eller på applikationsnivå i Prisma middleware)
- **Nyckelhantering** via miljövariabel eller extern nyckelhanteringstjänst (t.ex. HashiCorp Vault)
- Kräver genomtänkt migrering av befintlig data och påverkar sökbarhet (krypterade fält kan inte indexeras direkt)
### EAN-skanning — import via streckkod (Open Food Facts)
**Mål:** Låt användaren skanna eller skriva in en EAN-streckkod och få produktinformation (namn, kategori, näringsvärden) ifyllt automatiskt.
Open Food Facts är ett öppet, gratis API utan API-nyckel för grundläggande användning.
**Exempelanrop (EAN 7310960010016):**
```
GET https://world.openfoodfacts.org/api/v0/product/{ean}.json
```
Svaret innehåller:
- `product.product_name` — produktnamn
- `product.categories` — kategoristräng (kommaseparerad)
- `product.nutriments` — näringsvärden (energi, protein, fett, kolhydrater, m.fl.)
- `product.code` — EAN-koden bekräftad
**Förslag på implementation:**
1. **Backend-endpoint**`GET /api/products/ean/:code` — slår upp i Open Food Facts, returnerar normaliserat produktförslag (namn, kategori, näringsvärden) utan att spara
2. **Frontend-import** — nytt fält i produktformuläret (eller separat import-flöde): ange EAN → knapp "Hämta" → formuläret förifylls
3. **Alternativt:** kamerabaserad skanning i mobil webbläsare via `BarcodeDetector` API (eller externt bibliotek som `zxing-js`)
4. **Kategorimappning** — Open Food Facts-kategorier är på engelska/franska; en mappningstabell eller fuzzy-matchning mot systemets kategorier behövs
5. **Näringsdata** — om `product.nutriments` finns kan det sparas direkt i Prisma-modellens näringsvärdesfält (redan schema-klara)
**Begränsningar att ha i åtanke:**
- Produkttäckning varierar — svenska dagligvaror är välrepresenterade men inte allt finns
- Produktnamn är ofta på originalspråk; kan behöva redigeras
- Rate limiting: för hög användning rekommenderar Open Food Facts att kontakta dem
### Byta @Post till @Patch på backfill-canonical
Konsekvenser: ⚠️ Bryta ändring.
Frontend anropar /api/products/backfill-canonical med POST från AdminProductList.
Caddy och Next.js API-routes kan ha hårdkodade metoder.
Kräver synkroniserad ändring i backend + alla anropsställen i frontend, annars slutar funktionen fungera.
@@ -0,0 +1,820 @@
# Session 2026-05-06: Refaktor och user-scoped AI
Denna session har genomfört:
- **User-scoped AI-fallback:** AI-förslag för ingrediens- och kategorimatchning är nu individuellt aktiverbara per användare (premium).
- **Admin-toggles:** Backend och UI har stöd för att admin kan slå på/av AI per användare.
- **Premium-scope:** Flutter och backend respekterar premium-flagga och AI-tillgång i alla flöden.
- **Rematch och manuell produkt:** Flutter har stöd för ommatchning och manuell produkt vid import.
- **Lessons learned:**
- Nullable propagation i Prisma och DTO:er kräver noggrannhet.
- Fallback-first AI och tydlig separation av analyskontrakt ger robustare flöden.
- User-scoped features kräver ownerId/userId-filter i all logik.
- Manuella migrationer kan krävas vid DB-problem (se migrering-MSI.md).
Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk genomgång.
- [AI-FUNKTIONER.md](_archive/microservice-ai/AI-FUNKTIONER.md) för AI-översikt.
# Session 2026-05-09: Simplified Matching Logic (Consolidation Phase 3)
Denna session har genomfört den tredje omarbetningsfasen som konsoliderar receipt-import matching-flödet:
## Genomförda förbättringar
### 1. Unified Matcher (`matchAndEnrichReceiptItem`)
- **Tidigare:** Matching var splittrad mellan `matchProducts()` (alias + word-match) och `enrichWithAiCategories()` (~850 rader).
- **Nu:** En central metod som gör allt i explicit ordning:
1. **Alias lookup** — certifierad match från ReceiptAlias
2. **Word-match** — fuzzy produktmatchning med scoring
3. **Categorization** — regel-baserad → AI (fallback) → guards → hard overrides
### 2. Improved Context Management (`prepareMatchingContext`)
- Alla data (aliases, produkter, unit mappings, categories) hämtas **en gång** per receipt
- Parallell loading med `Promise.all()` — högre performance än tidigare sekventiell loading
- Context passeras till alla items för effektiv återanvändning
### 3. Better Decision Logging (`enrichCategoryForItem`)
- Structured trace med steg-för-steg-loggning:
- ✓ Rule-based hits with path name
- ✓ AI suggestions
- ⚠️ Guard remaps (contradiction resolution)
- ⚠️ Hard overrides (special cases)
- ✅ Final decision with confidence level
- Debug-träd med alla decision points
- Kan aktiveras per receipt via `RECEIPT_TRACE_DECISIONS` env-var
### 4. Simplified parseReceipt Flow
```
Before:
parseReceipt() → matchProducts() → enrichWithAiCategories()
After:
parseReceipt() → prepareContext() → matchAndEnrichReceiptItem() (per item)
```
## Technical Details
**Nya metoder:**
- `matchAndEnrichReceiptItem()` — unified matching pipeline per item
- `prepareMatchingContext()` — one-time context preparation
- `enrichCategoryForItem()` — consolidated categorization logic
- `findWordMatchWithScore()` — refactored word matching with explicit score return
**Datatyper:**
- `MatchDecision` interface för strukturerad result (i `dto/match-decision.ts`)
**Build Status:**
- ✅ Full TypeScript compilation successful
- ✅ No breaking changes to existing API contracts
## Cleanup Pending
De gamla metoderna är fortfarande i koden men kan nu ta bort:
- `private async matchProducts()` — deprecated by unified matcher
- `private async enrichWithAiCategories()` — deprecated by unified matcher
- `private findWordMatch()` — replaced by findWordMatchWithScore()
**Cleanup tasks:**
1. Ta bort `matchProducts()`
2. Ta bort `enrichWithAiCategories()`
3. Ta bort `findWordMatch()` (gammal version)
4. Uppdatera kommentarer/docstrings
5. Kör full test suite för regression detection
Cleanup bör göras i nästa session för att ge tid för monitoring/QA.
# Plan för omarbetning av receptimport
# 2026-05-07: Säkerhets- och deployförbättringar
- **Inventory är nu user-scopad:** Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester).
- **IDOR-skydd för inventory:** Det är nu omöjligt för användare att läsa eller ändra andras inventarieposter. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **.gitignore och deploy-hygien:** backend/dist och backend/tsconfig.tsbuildinfo ignoreras och är ej längre spårade i git. .env och .env.* ignoreras, men .env.example finns och är uppdaterad.
- **CI/CD-härdning:** npm audit och prisma validate körs i pipeline. Alla tester och byggen måste passera.
## Bakgrund
Nuvarande importflöde blandar ihop två olika ansvar:
1. Att importera och spara receptet så troget källan som möjligt.
2. Att matcha receptets ingredienser mot interna produkter.
Det som fungerar bra i dag är:
- webskrapning
- import med text och bild
- presentation av receptbild och text när receptet väl är sparat
Det som fungerar sämre är:
- ingrediensimporten
- tidig produktmatchning i importsteget
- att receptets innehåll förvrängs eller tappar ingredienser om matchning saknas
## Målbild
Importsteget ska bara göra detta:
- hämta receptets titel, bild, beskrivning, instruktioner och ingrediensrader
- strukturera ingrediensraderna så gott det går
- spara receptet även när ingen produktmatchning finns
- låta användaren granska receptet utan att behöva välja produkter
Ett senare analyssteg ska göra detta:
- jämföra receptets ingredienser med inventory och pantry
- avgöra exakt träff, trolig ersättningsvara eller saknad vara
- ge underlag för shoppinglista
- ge underlag för AI-förslag och substitutionsförslag
## Arkitekturprincip
Receptet ska vara källtroget.
Produktmatchning ska vara ett separat lager ovanpå receptet.
Det innebär att vi behöver separera:
- receptets råa ingredienser
- användarens lager och skafferi
- matchnings- och analyslogik
## Föreslagen datamodell
### Nuvarande problem
I dag kräver `RecipeIngredient` i Prisma:
- `productId`
- `quantity`
- `unit`
Det gör att en ingrediens inte kan sparas om vi inte redan vet vilken intern produkt den motsvarar.
### Ny målmodell
Inför två nivåer för ingredienser:
1. Receptets egen ingrediensrad
2. Matchning/analys mot interna produkter
### Förslag A: utöka befintlig `RecipeIngredient`
Det enklaste spåret är att behålla `RecipeIngredient`, men göra den receptcentrisk i stället för produktcentrisk.
Nya eller ändrade fält:
- `rawLine String?`
- `rawName String`
- `productId Int?` i stället för required
- `quantity Decimal?`
- `unit String?`
- `note String?`
- `matchConfidence Float?`
- `matchSource String?` (`heuristic`, `ai`, `manual`)
- `analysisStatus String?` (`unmatched`, `exact`, `substitutable`, `missing`)
Behåll:
- `alternativeProductIds Json?`
- `recipeId`
### Förslag B: dela upp i två tabeller
Mer robust på sikt men större ombyggnad:
- `RecipeIngredient` blir rå ingrediensrad
- `RecipeIngredientMatch` blir separat matchningslager
Första implementationen bör använda Förslag A för lägre risk.
## Rekommenderad genomförandeordning
1. Gör datamodellen tolerant för omatchade ingredienser.
2. Ändra backend så att import och sparning fungerar utan produktmatchning.
3. Förenkla frontendens importgranskning.
4. Lägg till separat analyssteg mot inventory och pantry.
5. Lägg till AI-stöd först när grundflödet är stabilt.
---
# Fil-för-fil-plan
## 1. Databas och Prisma
### Fil: `backend/prisma/schema.prisma`
### Ändringar
- Gör `RecipeIngredient.productId` optional.
- Gör `RecipeIngredient.product` optional relation.
- Gör `quantity` optional.
- Gör `unit` optional.
- Lägg till `rawName`.
- Lägg till `rawLine`.
- Lägg till `matchConfidence`.
- Lägg till `matchSource`.
- Lägg till `analysisStatus`.
### Exempel på målmodell
```prisma
model RecipeIngredient {
id Int @id @default(autoincrement())
recipe Recipe @relation(fields: [recipeId], references: [id])
recipeId Int
product Product? @relation(fields: [productId], references: [id])
productId Int?
rawName String
rawLine String? @db.Text
quantity Decimal? @db.Decimal(10, 2)
unit String?
note String?
alternativeProductIds Json?
matchConfidence Float?
matchSource String?
analysisStatus String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
```
### Leverabler
- Prisma-schema uppdaterat
- ny migration skapad
- Prisma Client regenererad
### Risk
- All kod som i dag antar att `productId`, `quantity` och `unit` alltid finns måste uppdateras
---
## 2. DTO:er för receptskapande
### Fil: `backend/src/recipes/dto/create-recipe.dto.ts`
### Nuvarande problem
DTO:n kräver att varje ingrediens har:
- `productId`
- `quantity`
- `unit`
Det blockerar trogen import.
### Ändringar
- Gör `productId` optional.
- Gör `quantity` optional.
- Gör `unit` optional.
- Lägg till `rawName` som required.
- Lägg till `rawLine` som optional.
- Lägg till `matchConfidence` och `matchSource` som optional om vi vill bevara preliminär heuristik.
### Mål
Backend ska kunna acceptera ett recept där ingrediensen bara är:
- `rawName`
- eventuellt `rawLine`
- eventuellt `quantity`, `unit`, `note`
### Konsekvens
`ArrayMinSize(1)` kan sannolikt vara kvar, men valideringen måste vara råingrediens-baserad i stället för produkt-baserad.
---
## 3. DTO för parse-resultat
### Fil: `backend/src/recipes/dto/parse-markdown.dto.ts`
### Ändringar
Ingen stor strukturändring behövs här om endpointen fortsatt bara tar emot markdown.
### Eventuell komplettering
Om importen senare ska stödja flera källor mer explicit kan ett `sourceType` läggas till:
- `markdown`
- `web`
- `ocr`
Detta är inte nödvändigt i första omgången.
---
## 4. Receptservice: parse ska sluta göra för mycket
### Fil: `backend/src/recipes/recipes.service.ts`
### Nuvarande problem
`parseMarkdown()` gör i dag flera saker:
- anropar importer-api eller lokal parser
- hämtar alla produkter
- kör heuristisk matchning med normalisering och Levenshtein
- returnerar suggestions
Det gör att importsteget implicit blir ett matchningssteg.
### Rekommenderad ändring i etapp 1
Behåll parse men ändra dess ansvar:
- parse ska alltid returnera råingredienser
- suggestions får vara optional och sekundära
- frontend ska inte vara beroende av suggestions för att kunna spara
### Konkret refaktorering
Dela upp i privata metoder:
- `parseRecipeContent(markdown)`
- `buildIngredientSuggestions(parsedIngredients)`
- `createRecipeFromImport(dto, userId)`
### Ändringar i `create()`
- sluta kräva att alla ingredienser har `productId`
- kör `assertProductsActive()` bara för ingredienser som faktiskt har `productId`
- spara `rawName` även när produktmatchning finns
- spara `rawLine` när det finns
### Ny metod att lägga till
- `analyzeRecipeIngredients(recipeId, userId)`
Ansvar för `analyzeRecipeIngredients`:
- ladda receptets råingredienser
- jämför mot inventory
- jämför mot pantry
- returnera analysstatus per ingrediens
- returnera förslag på ersättningsvaror och inköp
### Ny privat logik i service
- `findExactInventoryMatches(...)`
- `findPantryMatches(...)`
- `findSubstituteCandidates(...)`
- `buildShoppingNeeds(...)`
### Viktig designregel
`parseMarkdown()` får gärna returnera suggestions, men `create()` får inte vara beroende av att de finns.
---
## 5. Receptcontroller: separera import från analys
### Fil: `backend/src/recipes/recipes.controller.ts`
### Ändringar
Behåll:
- `POST /recipes/parse-markdown`
- `POST /recipes`
Lägg till:
- `GET /recipes/:id/analysis`
- eventuellt `POST /recipes/:id/rematch`
### Förslag på ansvar
`GET /recipes/:id/analysis`
- returnerar en användarspecifik analys av receptet mot inventory och pantry
`POST /recipes/:id/rematch`
- kör ny matchning om användaren har ändrat inventory/pantry eller om heuristiken förbättrats
### Mål
Importen blir en egen sak.
Analysen blir en egen endpoint.
---
---
## 7. Alias-strategi och matchedVia (2026-05-07)
- Alias-strategin är nu fullt implementerad:
- Backend och Flutter stödjer user-alias och globala alias.
- matchedVia-badge visas i UI (Alias/Ordmatch/AI).
- Användare kan spara egna alias, admins kan spara globala.
- Profilsidan har alias-lista för användare, admin-panelen visar produkt-ID och radstruktur.
- Backend har 3 nya tester för matchedVia, totalt 66 tester gröna.
### Fil: `backend/src/recipes/recipes.module.ts`
### Ändringar
Lägg till eventuella nya providers när analyslogik bryts ut:
- `RecipeAnalysisService`
- eventuellt `RecipeMatchingService`
### Rekommendation
Om analysen växer, bryt ut den från `RecipesService` för att undvika att samma service blir för stor.
---
## 7. Ny analysservice
### Ny fil: `backend/src/recipes/recipe-analysis.service.ts`
### Ansvar
- ta ett sparat recept som indata
- analysera varje ingrediens mot inventory och pantry
- ta fram:
- exakt träff
- täcks av pantry
- möjlig ersättning
- saknas
### Förslag på output
```ts
{
recipeId: 123,
ingredients: [
{
ingredientId: 1,
rawName: "gul lök",
quantity: 1,
unit: "st",
status: "exact_match",
matchedProductId: 456,
matchedProductName: "Gul lök",
source: "inventory"
}
],
summary: {
exactCount: 3,
pantryCount: 2,
substituteCount: 1,
missingCount: 4
},
shoppingListCandidates: []
}
```
### Nytta
Detta blir grunden för:
- "kan jag laga detta?"
- shoppinglista
- AI-förslag
---
## 8. Ny matchningstjänst
### Ny fil: `backend/src/recipes/recipe-matching.service.ts`
### Ansvar
- matcha rå ingrediens mot produktkatalog
- inte spara något själv
- kunna användas av både import och analys
### Matchningsnivåer
1. Exakt match på normaliserat namn
2. Synonym/alias-match
3. Kategori- eller taggmatchning
4. AI-fallback
### Varför separat tjänst
Det gör att heuristik, aliaslogik och AI-stöd inte behöver ligga inbyggt i `parseMarkdown()`.
---
## 9. AI-integration för ingredienstolkning
### Fil: `backend/src/ai/ai.service.ts`
### Nuvarande läge
AI används redan för kategorisering.
### Nytt användningsområde
Lägg till en separat metod, till exempel:
- `suggestIngredientMatches(rawIngredient, candidates)`
- `suggestSubstitutions(rawIngredient, availableProducts)`
### Viktig regel
AI ska inte styra om receptet går att spara.
AI ska bara förbättra analys och förslag efteråt.
### Mål
AI används där osäkerhet är acceptabel:
- substitutionsförslag
- kompletterande matchning
- shoppingförslag
---
## 10. Importskärm i Flutter
### Fil: `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
### Nuvarande problem
Skärmen gör i dag detta i review-steget:
- checkbox per ingrediens
- produktval via suggestions
- manuellt tillagda ingredienser med produktdropdown
- vid sparning ignoreras ingredienser utan `productId`
Det gör att användaren i praktiken förväntas produktmatcha importen.
### Målbild för skärmen
Importgranskningen ska fokusera på receptet, inte lagerkoppling.
### Ändringar
Ta bort eller dölja i importläget:
- produktdropdownar
- krav på produktmatchning
- manuell ingrediensskapning via produktlista
Behåll eller lägg till:
- justera titel
- justera beskrivning vid behov
- justera servings
- visa importerade ingrediensrader som text
- tillåt enklare rättningar av mängd, enhet, notering och namn
- inkludera/uteslut ingrediensrad
### Ny save-logik
Vid `_save()` ska frontend skicka:
- `rawName`
- `rawLine`
- `quantity`
- `unit`
- `note`
- `productId` bara om en automatisk eller manuell matchning redan finns
### Rekommenderad UI-förändring
Dela upp skärmen i två modes:
- `import review`
- `manual recipe editing`
Import review ska vara avsevärt enklare än manuell receptredigering.
---
## 11. Parsed recipe-domain i Flutter
### Fil: `flutter/lib/features/recipes/domain/parsed_recipe.dart`
### Ändringar
Lägg till fält som tydligare representerar rå importdata:
- `rawLine`
- `matchState`
- `isParsedSafely`
### Rekommendation
`ParsedIngredient` ska behandlas som importdata, inte som nästan färdig `RecipeIngredient`.
### Ny riktning
`suggestions` ska vara optional hjälpdata i UI, inte en förutsättning för att spara.
---
## 12. Flutter-repository för recept
### Fil: `flutter/lib/features/recipes/data/recipe_repository.dart`
### Ändringar
- uppdatera `createRecipe()` så att request body tillåter råingredienser utan `productId`
- lägg till metod:
- `fetchRecipeAnalysis(int id)`
- lägg till eventuell metod:
- `rematchRecipeIngredients(int id)`
### Mål
Frontend ska kunna:
- spara troget importerat recept
- senare hämta analys mot inventory/pantry
---
## 13. API-paths i Flutter
### Fil: `flutter/lib/core/api/api_paths.dart`
### Ändringar
Lägg till:
- `static String analysis(int id) => '/recipes/$id/analysis';`
- eventuellt `static String rematch(int id) => '/recipes/$id/rematch';`
---
## 14. Receptdomän i Flutter
### Fil: `flutter/lib/features/recipes/domain/recipe.dart`
### Nuvarande problem
Nuvarande domän ser sannolikt ingredienser som produktkopplade objekt.
### Ändringar
Gör att `RecipeIngredient` klarar:
- `productId == null`
- `productName == null`
- `rawName` finns alltid
- `quantity` och `unit` kan vara null eller tomma
### Viktigt
UI för receptdetalj ska kunna visa rå ingredienstext även när ingen produktmatchning finns.
---
## 15. Receptdetaljskärm i Flutter
### Fil: `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
### Ändringar
Ingredienslistan ska rendera enligt följande prioritet:
1. `rawName` och råa fält
2. matchat produktnamn om det finns
### Exempel
Om produktmatchning saknas ska användaren ändå se:
- `2 msk olivolja`
- `1 gul lök`
- `400 g krossade tomater`
### Lägg till ny sektion
- "Har du hemma?"
Den sektionen ska bygga på `GET /recipes/:id/analysis` i stället för att anta att receptingredienser redan är perfekt produktmatchade.
---
## 16. Ny analysdomän i Flutter
### Ny fil: `flutter/lib/features/recipes/domain/recipe_analysis.dart`
### Ansvar
Representera analysresultat från backend.
### Förslag på typer
- `RecipeAnalysis`
- `RecipeIngredientAnalysis`
- `RecipeIngredientAvailabilityStatus`
### Statusvärden
- `exactMatch`
- `coveredByPantry`
- `substitutable`
- `missing`
---
## 17. Ny provider för analys
### Fil: `flutter/lib/features/recipes/data/recipe_providers.dart`
### Ändringar
Lägg till provider:
- `recipeAnalysisProvider`
### Ansvar
- ladda analys för ett recept
- kunna invalidateras när inventory eller pantry ändras
---
## 18. Manual recipe editing ska vara separat
### Fil: `flutter/lib/features/recipes/presentation/recipe_edit_screen.dart`
### Rekommendation
Behåll denna skärm för riktig redigering av receptets struktur.
### Viktig skillnad mot import
- import review = bevara originalet
- recipe edit = medveten redigering av receptet
### Praktisk åtgärd
Undvik att återanvända fullständig edit-UI direkt i importflödet.
---
## 19. Alias- och produktmatchning
### Relevanta filer
- `backend/src/receipt-import/...`
- `backend/src/products/...`
- `backend/src/recipes/recipes.service.ts`
### Åtgärd
Flytta all generell ingrediensmatchning till en central tjänst i stället för att ha den dold i `parseMarkdown()`.
### Mål
Samma logik ska kunna användas av:
- receptimport
- analys mot inventory/pantry
- AI-förslag
- eventuellt framtida shoppinglista
---
## 20. Migration av befintliga data
### Vad behöver hanteras
Befintliga recept har ingredienser som redan är produktkopplade.
### Strategi
- behåll gamla värden
- fyll `rawName` med bästa tillgängliga namn från produkt eller befintlig text
- sätt `matchSource = 'legacy'` för migrerade rader
- sätt `analysisStatus = null` initialt
### Mål
Gamla recept fortsätter fungera utan att behöva byggas om manuellt.
---
# Implementationsfaser
## Fas 1: Gör receptingredienser omatchningsbara
### Filer
- `backend/prisma/schema.prisma`
- `backend/src/recipes/dto/create-recipe.dto.ts`
- `backend/src/recipes/recipes.service.ts`
- `flutter/lib/features/recipes/data/recipe_repository.dart`
- `flutter/lib/features/recipes/domain/recipe.dart`
### Resultat
Ett recept kan sparas även när inga ingredienser är produktmatchade.
---
## Fas 2: Förenkla importgranskningen
### Filer
- `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
- `flutter/lib/features/recipes/domain/parsed_recipe.dart`
### Resultat
Användaren granskar receptet som recept, inte som lagerobjekt.
---
## Fas 3: Lägg till analys mot inventory/pantry
### Filer
- `backend/src/recipes/recipe-analysis.service.ts`
- `backend/src/recipes/recipes.controller.ts`
- `backend/src/recipes/recipes.module.ts`
- `flutter/lib/core/api/api_paths.dart`
- `flutter/lib/features/recipes/domain/recipe_analysis.dart`
- `flutter/lib/features/recipes/data/recipe_repository.dart`
- `flutter/lib/features/recipes/data/recipe_providers.dart`
- `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
### Resultat
Appen kan svara på:
- vad har jag exakt
- vad täcks av pantry
- vad kan ersättas
- vad saknas
---
## Fas 4: Förbättra matchning och AI
### Filer
- `backend/src/recipes/recipe-matching.service.ts`
- `backend/src/ai/ai.service.ts`
- eventuellt fler UI-filer för ersättningsförslag och shoppinglista
### Resultat
AI blir en förbättrare, inte ett krav för importen.
---
# Beslut som bör tas innan implementation
## 1. Ska `RecipeIngredient` byggas om eller delas upp?
Rekommendation:
- börja med att bygga om `RecipeIngredient`
- dela upp i fler tabeller först om behovet blir tydligt senare
## 2. Ska suggestions visas under import?
Rekommendation:
- ja, men bara som passiv hjälp
- inte som något användaren måste välja för att kunna spara
## 3. Ska användaren kunna redigera ingrediensrader under import?
Rekommendation:
- ja, men bara lätt redigering
- ingen tung produktkoppling i detta steg
## 4. När ska AI användas?
Rekommendation:
- efter att receptet är sparat
- för matchning, substitutioner och shoppingstöd
- inte för att avgöra om receptet får sparas
---
# Definition av klart
Denna omarbetning är klar när följande gäller:
1. Ett importerat recept kan sparas utan att någon ingrediens är kopplad till en intern produkt.
2. Receptets ingredienslista visas troget även utan produktmatchning.
3. Importskärmen kräver inte produktval.
4. Receptdetaljen kan analysera receptet mot inventory och pantry i ett separat steg.
5. Analysen kan särskilja exakt träff, pantry-träff, ersättningsbar och saknad ingrediens.
6. AI används bara som hjälplager ovanpå detta.
---
# Rekommenderat första arbetsblock
Om implementationen ska påbörjas direkt är detta bästa första block:
1. Uppdatera `schema.prisma` för råingrediensstöd.
2. Skapa migration.
3. Uppdatera `create-recipe.dto.ts`.
4. Uppdatera `recipes.service.ts#create()` så att recept kan sparas utan `productId`.
5. Uppdatera `create_recipe_screen.dart` så att `_save()` skickar råingredienser i stället för att kasta bort omatchade rader.
Det blocket ger störst effekt med lägst risk, eftersom det löser kärnproblemet: att importen inte längre förstör receptets innehåll.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
@@ -0,0 +1,248 @@
# Nyheter och förbättringar (2026-05-10)
- **Admin-inventarie:** Full CRUD, merge, filter, sortering, preview och säkerhet för admin i inventarietabellen. Endast admin kan se och hantera alla användares inventarieposter via nya endpoints och adminpanel i Flutter.
- **User-scope och IDOR-skydd:** Inventory och produkter är nu strikt user-scopade. Alla operationer kräver och filtrerar på userId. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **Säkerhetshärdning:** DTO-validering, guard-ordning, logging, throttling, merge abuse-skydd, och rollbaserad access är implementerat och testat.
- **Optimeringar:** DRY i service-lager, striktare query parsing, preview-cache, API-cleanup, och kodduplication eliminerad.
- **Testtäckning:** Utökade enhets-, integrations- och säkerhetstester för alla kritiska flöden.
# Sessionlogg: Receipt Import Cleanup & Optimization
Datum: 2026-05-09
## Mål under sessionen
- Rensa bort legacy/deprecated kod i receipt-import.
- Förenkla och optimera kvarvarande kod på ett säkert sätt.
- Säkerställa att kategori-förslag visas för okända varor i import-UI.
## Genomförda förändringar
### 1) Legacy/deprecated kod borttagen (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Borttaget: `inferPackageDebugFromRawName()`
- Borttaget: `cachedCategories` + `loadCategories()` + constructor-anrop
- Borttaget: `matchProducts()`
- Borttaget: `findWordMatch()` (gammal variant)
- Borttaget: `enrichWithAiCategories()`
Fil: `backend/src/receipt-import/receipt-import.controller.ts`
- Borttaget endpoint: `POST /receipt-import/refresh-categories` (obsolet efter borttagen cache-metod)
### 2) Tester uppdaterade till unified matcher
Fil: `backend/src/receipt-import/receipt-import.service.spec.ts`
- Tester migrerade från anrop av borttagna `matchProducts()` till `matchAndEnrichReceiptItem()`.
- Tester gröna efter uppdatering.
### 3) Säkra förenklingar/optimeringar (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Infört tydliga typer för matchningskontext (`MatchingContext`) och debug-objekt.
- Extraherat helpers för:
- signaltextbyggande
- enhetsmappning
- Reducerad duplicering i kategoriseringsflöde.
- In-memory index i matchningskontext för snabbare uppslag:
- `aliasByReceiptName`
- `unitMappingByKey`
- Bakåtkompatibilitet behållen via fallback när index-fält saknas.
### 4) UI-fix: kategori-förslag visades inte för okända varor
Fil: `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- Fixat så kategori-förslag visas även om rad saknar matchad produkt.
- Edit-state fylls nu även för rader med endast kategori-förslag.
- Label i UI visar `Kategoriförslag` när produkt saknas.
### 5) Diagnostik tillagd (backend)
Fil: `backend/src/receipt-import/receipt-import.service.ts`
- Varningslogg om kategorier inte kunde laddas eller om listan blev tom.
- Syfte: snabbare felsökning när kategori-förslag uteblir.
## Verifiering under sessionen
- Backend build: OK (`npm run build`)
- Backend tests: OK (66/66)
- Flutter analyze (berörda filer): OK
Notering: terminalhistorik innehöll äldre felutskrifter, men senaste verifieringarna var gröna.
## Kvar att göra nästa gång
1. Deploya senaste backend + flutter till servern.
2. Re-testa receipt import med PDF i produktion.
3. Bekräfta att rader utan produkt nu visar `Kategoriförslag` direkt i listan.
4. Vid fortsatt problem: kontrollera nya varningsloggar från `prepareMatchingContext` i backend-loggar.
## Snabb fortsättning (checklista)
- [ ] Deploy backend
- [ ] Deploy flutter
- [ ] Import-test med samma PDF
- [ ] Verifiera kategori-förslag för okända varor
- [ ] Dela loggutdrag om något saknas
---
# Sessionlogg: Produkthantering & UI-optimeringar (samma dag, senare)
## Mål under denna del
- Fixa scroll-issue i kvittoimport-gränssnittet (7 rader men bara 5 synliga).
- Implementera product rename/merge för både admins och regular users.
- Eliminera kodduplicering i backend-service.
- Optimera admin-panel och deploy-process.
## Genomförda förändringar
### 1) Scroll-issue fixat i receipt import UI
Fil: `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- **Problem:** `SizedBox(height: 620px, child: ListView.builder(...))` inuti `SingleChildScrollView` skapade konfligerade scroll-handlers.
- **Lösning:** Tog bort `SizedBox`-begränsningen, använd `shrinkWrap: true` och `physics: NeverScrollableScrollPhysics()` på ListView så parent `SingleChildScrollView` hanterar all scrolling.
- **Resultat:** ✅ Alla 7 rader nu synliga.
### 2) Product rename & merge för admin (backend)
Fil: `backend/src/products/products.controller.ts`
- Två nya endpoints:
- `PATCH /products/:id/canonical-name` — admin kan byta namn på vilken produkt som helst
- `POST /products/merge` — admin kan slå ihop två produkter
- Decorator: `@Roles('admin')`
### 3) Private product endpoints för vanliga users (backend)
Fil: `backend/src/products/products.controller.ts`
- Två nya parallella endpoints för user-owned private products:
- `PATCH /products/private/:id/canonical-name` — user kan byta namn på egen privat produkt
- `POST /products/private/merge` — user kan slå ihop egna privata produkter
- JWT extraction: `const userId = req.user.id` (ingen `@Roles`-behov, user kan bara redigera sin egna data)
- Security: `ForbiddenException` om produkt inte är privat eller inte ägs av user
### 4) Backend service refaktorering — kodduplicering eliminerad
Fil: `backend/src/products/products.service.ts`
- Två nya **private helper-metoder:**
- `_updateCanonicalNameCore(id, canonicalName)` — shared logik för trim + Prisma update
- `_mergeCore(sourceId, targetId)` — shared logik för transaction, inventory transfer, soft-delete
- Alla fyra public metoder (`updateCanonicalName`, `updateCanonicalNamePrivate`, `merge`, `mergePrivate`) använder nu dessa helpers
- **Exception fix:** Bytte `throw new Error(...)` till `throw new ForbiddenException(...)` för authorization-fel (korrekt HTTP 403)
- **Resultat:** ~80 rader kodduplicering eliminerad, bättre underhållbarhet
### 5) API-path konstanter för Flutter (frontend)
Fil: `flutter/lib/core/api/api_paths.dart`
- Lade till två nya constants i `ProductApiPaths`:
- `static const mergePrivate = '/products/private/merge'`
- `static String canonicalNamePrivate(int id) => '/products/private/$id/canonical-name'`
### 6) Admin repository uppdaterad (frontend)
Fil: `flutter/lib/features/admin/data/admin_repository.dart`
- Två nya metoder:
- `updateCanonicalNamePrivate(int productId, String canonicalName)` — user rename
- `mergeProductsPrivate({required sourceId, required targetId})` — user merge
- Komment: "Admin kan uppdatera vilken produkt som helst; users kan bara uppdatera sina egna privata produkter"
### 7) Admin panel optimeringar (frontend)
Fil: `flutter/lib/features/admin/presentation/admin_products_panel.dart`
- **Caching av kategorierna:** `_cachedCategoryOptions` beräknas en gång istället för varje build
- **Enklare `_nameForId()`:** Bytte från `where().toList()` till en enkel for-loop med early return
- **Parallell restore:** `_restoreSelected()` använder `Future.wait()` istället för seriebaserade await
- **Expression switch:** `_sortLabel()` förkortat från 12-radigt switch till en enda rad med expression switch
### 8) Deploy-script förbättring
Fil: `deploy.sh`
- Nya flaggor för selektiv build:
- `--backend` — bygga bara backend
- `--flutter` — bygga bara Flutter
- `--importer` — bygga bara microservice-importer
- `--pull-always` — tvinga Docker att hämta senaste base image (för prod-säkerhet)
- Default: bygger alla tre, använder `--pull=false` för snabbhet (ej prod)
- `--seed` flag för opt-in databaskällning
- Help: `./deploy.sh --help` visar användning
## Verifiering
- ✅ Backend build: OK (`npm run build`)
- ✅ Backend tests: OK
- ✅ Flutter analyze: OK (alla berörda filer)
- ✅ TypeScript-fel: Inga
- ✅ Git diff: Alla 4 filer granskat och godkänt
## Nyheter och förbättringar (2026-05-11)
- **Inventarie och baslager:** Möjlighet att se, sätta och ändra kategori på produkter direkt i inventarie- och baslagervyn. Identisk, sökbar/autocomplete category-picker i alla relevanta vyer (inventarie, baslager, admin, import). UX-standardisering: samma dropdown och interaktionsmönster överallt.
- **Seed-data:** Nya kategorier under `Skafferi > Sylt, mos & marmelad`: `Marmelad`, `Sylt`, `Mos` tillagda i seed.
- **Navigation/UI:** Admin-knappen flyttad från sidomenyn till profilmenyn ovanför logout.
- **Deploy och seed:** Förtydligande om att seed-data körs automatiskt vid `./deploy.sh --backend` och att seed-kontroll sker i deploy-scriptet. Hur man verifierar att seed körts och att nya kategorier finns.
- **Kodförenklingar/optimeringar:** Samtliga tre förenklingar/optimeringar från senaste commit är nu implementerade (se nedan för detaljer).
## Öppna uppgifter (nästa steg)
1. Deploy backend + Flutter med `./deploy.sh --backend --flutter`
2. Testa i produktion:
- Verifiera 7 rader nu synliga i receipt import
- Verifiera admin kan byta namn på produkter
- Verifiera admin kan slå ihop produkter
- Verifiera users kan redigera sina egna privata produkter
3. UI för users: Om private rename/merge ska exponeras i användar-app (backend redan klart, saknas bara UI)
4. Unit/integration tests för private endpoints
---
# Sessionlogg: Aliasstrategi i kvittoimport (samma dag, senare)
## Mål under denna del
- Göra aliasstrategin konsekvent med user-scope som standard och global fallback via admin.
- Sluta lära alias automatiskt vid manuell korrigering och kräva explicit val i UI.
- Härda backend mot brusiga eller ogiltiga alias.
## Genomförda förändringar
### 1) Gemensam aliasnormalisering och guardrails (backend)
Filer:
- `backend/src/common/utils/receipt-alias.ts`
- `backend/src/receipt-alias/receipt-alias.service.ts`
- `backend/src/receipt-import/receipt-import.service.ts`
- Infört gemensam utility för aliasnormalisering (`trim`, lowercase, kollapsad whitespace).
- Infört validering som blockerar tomma alias och brusiga alias som `rabatt`, `summa`, `pant`, `att betala`, `totalt`, m.fl.
- Receipt import och alias-API använder nu samma regler för både lookup och sparande.
### 2) Receipt import lär inte längre alias automatiskt (Flutter)
Filer:
- `flutter/lib/features/import/data/receipt_import_session.dart`
- `flutter/lib/features/import/presentation/edit_dialog.dart`
- `flutter/lib/features/import/presentation/receipt_import_tab.dart`
- Infört explicit `learnAlias`-val i edit-dialogen.
- Alias sparas nu bara om användaren aktivt markerar att kvittonamnet ska läras in.
- Valet persisteras i receipt import-sessionen så att tabbyte inte tappar användarens val.
- Om raden redan matchades via alias visas förklarande text i stället för ny aliasinlärning.
### 3) Aliasöversikter visar scope tydligare (Flutter)
Filer:
- `flutter/lib/features/admin/domain/receipt_alias.dart`
- `flutter/lib/features/profile/presentation/user_aliases_screen.dart`
- `flutter/lib/features/admin/presentation/admin_aliases_panel.dart`
- Aliasmodellen utökad med `ownerId` och `isGlobal`.
- User alias-skärmen visar nu skillnad mellan `Privat alias` och `Global fallback`.
- Delete-knapp visas bara för privata alias i användarvyn, så UI:t matchar backend-behörigheten.
- Adminpanelen visar scope även för aliasposter.
### 4) Tester för aliasflödet
Filer:
- `backend/src/receipt-import/receipt-import.service.spec.ts`
- `backend/src/receipt-alias/receipt-alias.service.spec.ts`
- Tester tillagda för normalisering av whitespace vid alias-lookup.
- Tester tillagda för alias-upsert med normalisering.
- Tester tillagda för blockering av brusalias.
- Tester tillagda för behörighetsregler kring globala alias och borttagning.
## Verifiering
- ✅ Backend tests: 31/31 gröna för berörda aliasspecar
- ✅ Flutter analyze: OK för alla berörda alias/import-filer
## Kvar att göra
1. Manuell test i appen: receipt import med explicit alias-inlärning.
2. Produktionstest: verifiera att privata alias och global fallback beter sig rätt mot riktiga kvitton.
3. Bedöm om aliasöversikterna behöver mer avancerad filtrering eller redigering senare.
## Snabb checklista för nästa session
- [ ] Deploy backend + Flutter
- [ ] Testa scroll-fix i prod
- [ ] Testa admin rename/merge
- [ ] Testa private endpoints (API-test eller manual)
- [ ] Implementera user-UI för private rename/merge (valfritt)
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
@@ -0,0 +1,109 @@
# Session Checkpoint (2026-05-11)
## Status
- Branch/worktree: ändringar finns lokalt och är analyserade med `flutter analyze`.
- Senaste verifiering: `flutter analyze lib/core/utils/display_labels.dart lib/features/inventory/presentation/swipeable_inventory_tile.dart lib/features/pantry/presentation/pantry_screen.dart` -> inga fel.
## Klart i denna session
1. Kategori-chip i `/inventory` och `/baslager` visar nu djupaste kategori (L3 om den finns, annars L2/L1) utan prefix (`L1/L2/L3`).
2. Hover/long-press på chip visar full kategoriväg (`L1 > L2 > L3`) via tooltip.
3. Cache-invalidation fixad efter kategoriändring i admin:
- Invaliderar både `inventoryProvider` och `pantryProvider` i globala produktflöden.
4. Kodstädning/optimering:
- Gemensam helper returnerar både chip-label och tooltip i en parsing.
- Oanvänd helper borttagen.
5. Dokumentation uppdaterad och rubrik-konflikt städad för 2026-05-11-sektionerna.
## Ändrade filer
- `flutter/lib/features/admin/presentation/admin_products_panel.dart`
- `flutter/lib/core/utils/display_labels.dart`
- `flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart`
- `flutter/lib/features/pantry/presentation/pantry_screen.dart`
- `TEKNISK_BESKRIVNING.md`
## Viktiga beteenderegler som nu gäller
- Chiptext: visar endast djupaste kategorinamn.
- Tooltip: visar full kategori-path.
- Om path saknas: fallback till L1/"Övrigt" enligt befintlig modell.
## Kvar / nästa steg (rekommenderat)
1. Lägg widgettester för category-chip:
- inventory: djupaste label + full tooltip
- baslager: djupaste label + full tooltip
2. Besluta om sortering ska följa visad kategori (djupaste nod) eller fortsatt L1.
3. Commit/pusha ändringarna när du är nöjd.
---
## Uppdatering 2026-05-12
### Klart nu
1. Widgettester tillagda för category-chip/tooltip:
- `flutter/test/features/inventory/presentation/swipeable_inventory_tile_test.dart`
- `flutter/test/features/pantry/presentation/pantry_screen_category_chip_test.dart`
2. Sorteringsbeslut implementerat:
- Sortering på `Kategori (A-O)` följer nu visad kategori (djupaste nod), inte enbart L1.
- Uppdaterat i både inventory- och baslagervyn.
### Deploy + manuell produktionstest (körplan)
Kör deploy:
```bash
./deploy.sh --backend --flutter
```
Om du vill tvinga kontroll av nya basimages:
```bash
./deploy.sh --backend --flutter --pull-always
```
Verifiera drift efter deploy:
```bash
docker compose -f compose.yml -f compose.flutter.yml ps
curl http://localhost:8080/api/health
curl http://localhost:8080/api/health/db
```
Manuell produktionschecklista:
- [ ] Receipt import: verifiera att alla rader syns (scroll-fix).
- [ ] Receipt import: verifiera att okända varor visar `Kategoriförslag`.
- [ ] Inventory: verifiera category-chip (djupaste nod) + tooltip (full path).
- [ ] Baslager: verifiera category-chip (djupaste nod) + tooltip (full path).
- [ ] Admin: verifiera rename/merge av produkter.
- [ ] User: verifiera private rename/merge-behörighet och funktion.
Fallback vid avvikelse:
- Spara backend-loggar för receipt import och kategori-matchning.
- Notera exakt testdata (kvitto/PDF, produktnamn, förväntat vs faktiskt utfall).
## Snabb återstart nästa gång
Kör:
```bash
git status
git diff -- flutter/lib/core/utils/display_labels.dart \
flutter/lib/features/inventory/presentation/swipeable_inventory_tile.dart \
flutter/lib/features/pantry/presentation/pantry_screen.dart \
flutter/lib/features/admin/presentation/admin_products_panel.dart \
TEKNISK_BESKRIVNING.md
```
Och därefter:
```bash
cd flutter
flutter analyze lib/core/utils/display_labels.dart \
lib/features/inventory/presentation/swipeable_inventory_tile.dart \
lib/features/pantry/presentation/pantry_screen.dart
```
@@ -0,0 +1,134 @@
# Session Checkpoint (2026-05-12)
> Föregående checkpoint: [SESSION_CHECKPOINT_2026-05-11.md](SESSION_CHECKPOINT_2026-05-11.md)
## Status
- Alla ändringar är lokalt implementerade och verifierade (tester gröna, inga type-fel, 0 npm-sårbarheter).
- Ej committat/pushat.
## Klart i denna session
### 1. Alias-scopehantering (admin kan höja privat → globalt)
**Backend (`receipt-alias`):**
- `UpdateReceiptAliasDto` utökat med `isGlobal?: boolean` (`@IsBoolean()`).
- `receipt-alias.service.ts`: `update()` hanterar scope-byte med:
- Admin-only-regel: vanlig användare → `ForbiddenException`.
- Global → privat utan owner → `BadRequestException`.
- Beräknar `nextIsGlobal` + `nextOwnerId` korrekt och sparar.
- Konfliktcheck baseras på nästa scope, inte gammalt.
**Admin-UI (`flutter/lib/features/admin/presentation/admin_aliases_panel.dart`):**
- Edit-dialog har scope-switch.
- Switch är **låst (disabled)** om aliaset redan är globalt — endast privat → global är möjligt.
- Subtitel förklarar läget dynamiskt.
- Bekräftelse-chip visas i listan i 6 sekunder efter genomförd scope-ändring.
- `_scopeChangedAliasId` / `_scopeChangedToGlobal` state-fält med Timer + dispose-cleanup.
**Admin repository (`flutter/lib/features/admin/data/admin_repository.dart`):**
- `updateReceiptAlias` tar emot `bool? isGlobal` och inkluderar det i PATCH-body.
**Tester (16/16 passerar):**
- `receipt-alias.service.spec.ts`: admin kan höja privat → global; användare blockeras; global → privat utan owner ger `BadRequestException`.
- `receipt-alias.security.spec.ts`: controller delegerar DTO med `isGlobal` korrekt.
### 2. Kategori-chip fallback
- Den gemensamma chip-buildern returnerar nu `SizedBox.shrink()` vid tom kategoriväg — "okänd"-chip visas inte längre i admin- eller importpaneler.
### 3. Beroendehärdning
**recipe-app backend:**
- Uppgraderat till **NestJS 11** (`@nestjs/common/core/platform-express/testing` 11.1.19, `@nestjs/jwt` 11.0.2, `@nestjs/passport` 11.0.5).
- `multer` uppgraderat till **2.1.1** (åtgärdar high-severity CVE i transitivt beroende).
- `@types/express`**5.0.5**, `@nestjs/cli`**11.0.21**, `@nestjs/schematics`**11.1.0**.
- `jwt.strategy.ts`: fail-fast check för `JWT_SECRET` + typed `secretOrKey`.
- `JWT_SECRET: ci-test-secret` satt i CI-env i `.gitea/workflows/test.yml`.
- `npm audit --audit-level=high` = **0 sårbarheter**.
**microservice-importer backend:**
- Samma uppgradering till Nest 11 + multer 2.1.1 + `@types/express` 5.
- `npm audit --audit-level=high` = **0 sårbarheter**.
### 4. Quality-gate scripts
**recipe-app/backend/package.json** nya scripts:
| Script | Syfte |
|---|---|
| `prisma:validate` | `prisma validate --schema prisma/schema.prisma` |
| `typecheck` | `tsc --noEmit` |
| `audit:high` | `npm audit --audit-level=high` |
| `quality:ci` | Kedja: validate → generate → typecheck → test → build → audit |
**recipe-app/.gitea/workflows/test.yml** uppdateringar:
- Prisma-steg kör `npm run prisma:validate`.
- Audit-steg kör `npm run audit:high`.
- Nytt **Typecheck backend**-steg i PR-snabbjobb och push-fulljobb.
**microservice-importer/backend/package.json** nya scripts:
| Script | Syfte |
|---|---|
| `typecheck` | `tsc --noEmit` |
| `audit:high` | `npm audit --audit-level=high` |
| `quality:ci` | Kedja: typecheck → build → audit |
**microservice-importer/.github/workflows/test.yml** uppdateringar:
- `npm install``npm ci`.
- Prisma/test-steg ersatta med: typecheck → build → audit.
- `continue-on-error` borttagen från build.
## Ändrade filer (recipe-app)
- `backend/src/receipt-alias/dto/update-receipt-alias.dto.ts`
- `backend/src/receipt-alias/receipt-alias.service.ts`
- `backend/src/receipt-alias/receipt-alias.service.spec.ts`
- `backend/src/receipt-alias/receipt-alias.security.spec.ts`
- `backend/src/auth/jwt.strategy.ts`
- `backend/package.json`
- `backend/package-lock.json`
- `.gitea/workflows/test.yml`
- `flutter/lib/features/admin/data/admin_repository.dart`
- `flutter/lib/features/admin/presentation/admin_aliases_panel.dart`
- `TEKNISK_BESKRIVNING.md`
- `NEXT_STEPS.md`
- `_archive/docs/SESSION_CHECKPOINT_2026-05-12.md` (denna fil)
## Ändrade filer (microservice-importer)
- `backend/package.json`
- `.github/workflows/test.yml`
- `TEKNISK_BESKRIVNING.md`
- `next_steps_MSImporter.md`
## Snabb återstart nästa gång
```bash
# Verifiera status
cd recipe-app/backend
npm run quality:ci
cd ../../microservice-importer/backend
npm run quality:ci
```
Flutter-analys:
```bash
cd recipe-app/flutter
flutter analyze lib/features/admin/presentation/admin_aliases_panel.dart
```
Alias-tester:
```bash
cd recipe-app/backend
npm test -- src/receipt-alias/receipt-alias.service.spec.ts src/receipt-alias/receipt-alias.security.spec.ts --runInBand
```
## Nästa rekommenderade steg
1. Commit och push av samtliga lokala ändringar.
2. Deploy till produktionsmiljö och verifiera alias-flöde med riktiga kvitton.
3. Manuell verifiering: admin kan höja privat → globalt via UI; vanlig användare kan inte.
4. Widgettester för alias-panelens switch (privat → global, disabled-state om globalt).
@@ -0,0 +1,87 @@
# Session Checkpoint (2026-05-21)
> Föregående checkpoint: [SESSION_CHECKPOINT_2026-05-12.md](SESSION_CHECKPOINT_2026-05-12.md)
## Status
- Arbetsytan är ren (`git status --short` gav ingen output).
- Kritiska build-blockers för Flutter-l10n är åtgärdade.
- Backend build + backend tester + Flutter tester verifierade gröna i denna session.
## Klart i denna session
### 1. Felsökning och fix av Docker-fel i Flutter `gen-l10n`
**Problem:** Docker-bygg kraschade vid `flutter gen-l10n` p.g.a. ogiltig ARB-JSON och konflikt i locale-filer.
**Åtgärder:**
- `flutter/lib/l10n/app_en.arb` reparerad (felaktig JSON-struktur, saknade/utanförliggande nycklar).
- Krock mellan engelska locale-filer hanterad (dubbla `en`-källor var en del av tidigare felsymptom).
- `flutter gen-l10n` kördes om utan formatteringsfel.
### 2. Fix av Flutter test-fel: saknad l10n-nyckel `required`
**Problem:** `flutter test` föll på:
- `The getter 'required' isn't defined for the type 'AppLocalizations'`
- fel i `lib/features/admin/presentation/admin_users_panel.dart`.
**Åtgärder:**
- Återställde saknade nycklar i `flutter/lib/l10n/app_en.arb`:
- `required`
- `logoutAction`
- `adminAiDescription`
- `adminPagePrefix`
- Synkade svenska ARB-filen och la till saknad nyckel:
- `profileDatabaseDescription`
- Regenererade lokaliseringar med `flutter gen-l10n`.
### 3. Kvalitetsverifiering
Körda verifieringar:
```bash
# Backend
cd backend
npm run build
npm run test
# Flutter
cd ../flutter
flutter gen-l10n
flutter test --reporter compact
```
**Resultat:**
- Backend build: OK
- Backend tests: OK (29/29 suites, 245/245 tester)
- Flutter tests: OK (alla passerar)
## Viktig kontext inför nästa session
- Root-varningen från Flutter i Docker (`trying to run flutter as root`) är en varning och blockerar inte i sig.
- Den blockerande orsaken var ARB/l10n-konsistens, inte root-varningen.
- Nuvarande l10n-läge är stabilt efter regeneration.
## Rekommenderad snabbstart imorgon
```bash
# 1) Verifiera ren arbetsyta
git status --short
# 2) Reprova hela lokala verifieringen
cd backend
npm run build && npm run test
cd ../flutter
flutter gen-l10n
flutter test --reporter compact
# 3) Om allt är grönt, kör deploy/build-pipeline igen
```
## Ändrade filer i denna session (huvudsakligen)
- `flutter/lib/l10n/app_en.arb`
- `flutter/lib/l10n/app_sv.arb`
- genererade l10n-filer under `flutter/lib/l10n/generated/*`
- mindre korrigeringar i backend-test/service under felsökningen, slutläge verifierat grönt.
@@ -0,0 +1,414 @@
# 🔒 Säkerhetshärdningsplan för Recipe-App (Flutter + NestJS + MariaDB)
# Nyheter och förbättringar (2026-05-10)
- **Admin-inventarie:** Endast admin har tillgång till CRUD, merge, filter, sortering och preview för alla användares inventarieposter. Endpoints och UI är skyddade med @Roles('admin') och testade.
- **User-scope och IDOR-skydd:** Inventory och produkter är strikt user-scopade. Alla operationer kräver och filtrerar på userId. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **Säkerhetshärdning:** DTO-validering, guard-ordning, logging, throttling, merge abuse-skydd, och rollbaserad access är implementerat och testat.
- **Optimeringar:** DRY i service-lager, striktare query parsing, preview-cache, API-cleanup, och kodduplication eliminerad.
- **Testtäckning:** Utökade enhets-, integrations- och säkerhetstester för alla kritiska flöden.
**Reviderad:** 2026-05-07 — baserad på faktisk kodgranskning av repo.
**Mål:** Täppa till IDOR, Full Table Dump, och andra kritiska säkerhetshål i **backend (NestJS/Prisma/MariaDB)**, **Flutter-frontend**, och **infrastruktur (Docker/Gitea/Ubuntu)**.
**Prioritet:** CRITICAL → HIGH → MEDIUM
---
## ✅ **Redan implementerat — kräver ingen åtgärd**
Följande är bekräftat implementerat i koden och behöver inte åtgärdas:
| Åtgärd | Var |
|---|---|
| Helmet med HSTS, X-Frame-Options, CORP, COOP, Referrer-Policy | `backend/src/main.ts` |
| CORS begränsat till `ALLOWED_ORIGIN` (env-var) | `backend/src/main.ts` |
| Global `JwtAuthGuard` (alla endpoints kräver JWT om inte `@Public()`) | `backend/src/app.module.ts` |
| Global `RolesGuard` med `@Roles('admin')` decorator | `backend/src/app.module.ts` |
| Global `ThrottlerGuard` (120 anrop/min), 10/min på login/register | `backend/src/app.module.ts`, `auth.controller.ts` |
| `ValidationPipe` med `whitelist` + `forbidNonWhitelisted` (förhindrar mass assignment) | `backend/src/main.ts` |
| bcrypt salt 12 för lösenordshashning | `backend/src/auth/auth.service.ts` |
| IDOR-skydd: `recipes`, `pantry`, `meal-plan`, `receipt-alias` filtrerar på `userId` | Respektive controllers |
| Produkter är user-scoped (`ownerId``Product`) | `backend/prisma/schema.prisma` |
| Hemligheter som env-vars i compose, inga hårdkodade värden | `compose.yml` |
| `.env.*` i `.gitignore` | `.gitignore` |
| Token-lagringsabstraktion i Flutter (`ITokenStorage`) | `flutter/lib/core/platform/token_storage.dart` |
| Premium/admin-guard (`PremiumOrAdminGuard`) | `backend/src/auth/premium-or-admin.guard.ts` |
---
---
## 📌 **0. Förberedelser**
### 0.1. Miljö och verktyg
- **Skapa en `security-audit`-gren:**
```bash
git checkout -b security-audit-$(date +%Y%m%d)
```
---
---
## 🚨 **1. IDOR + Full Table Dump i Inventory — CRITICAL**
**Problem:** `InventoryItem` saknar `userId`-fält i Prisma-schemat och `inventory.controller.ts` använder inte `@CurrentUser()` alls. Alla autentiserade användare kan läsa, modifiera och radera varandras inventarieposter via `GET/POST/PATCH/DELETE /api/inventory`.
**Nuläge i koden:**
- `InventoryItem`-modellen i `schema.prisma` saknar `userId`-kolumn
- `inventory.controller.ts` har noll anrop till `@CurrentUser()`
- `inventory.service.ts` metoder `findAll()`, `findExpiring()`, `update()`, `remove()` tar inte in `userId`
**Lösning — 3 steg:**
### Steg 1: Lägg till `userId` i Prisma-schemat
```prisma
// backend/prisma/schema.prisma
model InventoryItem {
id Int @id @default(autoincrement())
userId Int // NY KOLUMN
productId Int
// ...övriga fält oförändrade...
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
product Product @relation(fields: [productId], references: [id], onDelete: Cascade)
consumptions InventoryConsumption[]
@@index([productId])
@@index([userId]) // NY INDEX
}
// Lägg till i User-modellen:
model User {
// ...
inventoryItems InventoryItem[] // NY RELATION
}
```
Skapa migrerings-SQL i `backend/prisma/migrations/{timestamp}_add_userid_to_inventory/migration.sql`:
```sql
ALTER TABLE `InventoryItem` ADD COLUMN `userId` INT NOT NULL DEFAULT 1;
-- Backfill: sätt userId baserat på produktens ägare
UPDATE `InventoryItem` ii
JOIN `Product` p ON ii.productId = p.id
SET ii.userId = p.ownerId;
ALTER TABLE `InventoryItem` ADD CONSTRAINT `fk_inventory_user`
FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE CASCADE;
CREATE INDEX `InventoryItem_userId_idx` ON `InventoryItem`(`userId`);
```
> **OBS:** Granska backfill-SQL mot din data innan körning — om produkter saknar ägare kan DEFAULT 1 ge fel.
### Steg 2: Uppdatera service-metoderna
```typescript
// backend/src/inventory/inventory.service.ts
// findAll — lägg till userId-parameter och where-klausul
async findAll(userId: number, query?: InventoryQuery) {
const where: Prisma.InventoryItemWhereInput = { userId };
// ...resten oförändrat...
}
// findExpiring — filtrera på userId
async findExpiring(userId: number) {
return this.prisma.inventoryItem.findMany({
where: { userId, bestBeforeDate: { not: null, gte: new Date() } },
// ...
});
}
// create — spara userId
async create(userId: number, data: CreateInventoryDto) {
return this.prisma.inventoryItem.create({
data: { ...data, userId, quantity: new Prisma.Decimal(data.quantity) },
// ...
});
}
// update — verifiera ägarskap innan uppdatering
async update(id: number, userId: number, data: UpdateInventoryDto) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
// ...resten oförändrat...
}
// remove — verifiera ägarskap innan borttagning
async remove(id: number, userId: number) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
return this.prisma.inventoryItem.delete({ where: { id } });
}
// consume — verifiera ägarskap
async consume(id: number, userId: number, body: ConsumeInventoryDto) {
const existing = await this.findInventoryItemByIdOrThrow(id);
if (existing.userId !== userId) throw new ForbiddenException('Åtkomst nekad');
// ...resten oförändrat...
}
```
### Steg 3: Uppdatera controller
```typescript
// backend/src/inventory/inventory.controller.ts
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import { ForbiddenException } from '@nestjs/common';
@Controller('inventory')
export class InventoryController {
@Get()
findAll(
@CurrentUser() user: { userId: number },
@Query('location') location?: string,
@Query('sort') sort?: string,
) {
return this.inventoryService.findAll(user.userId, { location, sort });
}
@Get('expiring')
findExpiring(@CurrentUser() user: { userId: number }) {
return this.inventoryService.findExpiring(user.userId);
}
@Post()
create(
@CurrentUser() user: { userId: number },
@Body() body: CreateInventoryDto,
) {
return this.inventoryService.create(user.userId, body);
}
@Patch(':id')
update(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
@Body() body: UpdateInventoryDto,
) {
return this.inventoryService.update(id, user.userId, body);
}
@Delete(':id')
remove(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
) {
return this.inventoryService.remove(id, user.userId);
}
@Post(':id/consume')
consume(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
@Body() body: ConsumeInventoryDto,
) {
return this.inventoryService.consume(id, user.userId, body);
}
@Get(':id/consumption-history')
findConsumptionHistory(
@CurrentUser() user: { userId: number },
@Param('id', ParseIntPipe) id: number,
) {
// Verifiera ägarskap på item innan historik returneras
return this.inventoryService.findConsumptionHistory(id, user.userId);
}
}
```
### Steg 4: Testa
```typescript
// backend/src/inventory/inventory.service.spec.ts
describe('IDOR-skydd inventory', () => {
it('nekar åtkomst till annan användares post vid update', async () => {
prismaMock.inventoryItem.findUnique.mockResolvedValue({ id: 1, userId: 2 } as any);
await expect(service.update(1, 1, {})).rejects.toThrow(ForbiddenException);
});
it('nekar åtkomst till annan användares post vid remove', async () => {
prismaMock.inventoryItem.findUnique.mockResolvedValue({ id: 1, userId: 2 } as any);
await expect(service.remove(1, 1)).rejects.toThrow(ForbiddenException);
});
});
```
---
---
## **2. dist/ saknas i .gitignore — HIGH**
**Problem:** `backend/dist/` och `backend/tsconfig.tsbuildinfo` är committade i repot och orsakar merge-konflikter vid deploy (bekräftat 2026-05-07).
**Lösning:**
Lägg till i `.gitignore` (rotnivå):
```gitignore
# Kompilerat backend-output — byggs i Docker, ska ej spåras
backend/dist/
backend/tsconfig.tsbuildinfo
```
Rensa sedan från git-historiken (enbart lokal tracking):
```bash
git rm -r --cached backend/dist/ backend/tsconfig.tsbuildinfo
git commit -m "chore: untrack compiled backend dist files"
git push
```
På servern, kör sedan:
```bash
git checkout -- backend/dist/ backend/tsconfig.tsbuildinfo 2>/dev/null; git pull
```
---
---
## 🔑 **3. .gitignore täcker inte bare `.env` — MEDIUM**
**Problem:** `.gitignore` har `.env.*` (täcker `.env.local`, `.env.production` etc.) men INTE ett eventuellt `.env` utan suffix.
**Lösning:** Lägg till i `.gitignore`:
```gitignore
.env
.env.*
!.env.example
```
**Skapa `.env.example`** (finns ej i repot):
```env
DATABASE_URL=mysql://user:password@localhost:3306/recipe_app
JWT_SECRET=minst-32-tecken-slumpad-sträng
MARIADB_ROOT_PASSWORD=
MARIADB_DATABASE=recipe_app
MISTRAL_API_KEY=
NEXT_PUBLIC_APP_URL=https://recept.example.com
ADMIN_NADMIN_PASSWORD=
ADMIN_PADMIN_PASSWORD=
SEED_USER1_PASSWORD=
SEED_USER2_PASSWORD=
ALLOWED_ORIGIN=https://recept.example.com
RECEIPT_TRACE_DECISIONS=0
```
---
---
## 📱 **4. Flutter — tokenlagring på webben — MEDIUM**
**Nuläge:** `WebTokenStorage` använder `SharedPreferences` som mappar till `localStorage` i webbläsaren. JWT i localStorage är åtkomlig via JavaScript och kan stjälas vid XSS.
**Realistisk åtgärd för en Flutter Web-app:** Det finns inget httpOnly-cookie-alternativ direkt i Flutter Web utan backend-stöd. Om XSS-risken bedöms hög, överväg:
1. Backend sätter token som httpOnly cookie vid login — Flutter Web läser aldrig token direkt
2. Alternativt: sessionStorage (rensas vid stängt fönster) via JS-interop
**Nuvarande abstraktionslager är korrekt strukturerat** (`ITokenStorage` → `WebTokenStorage`). Byt ut implementationen vid behov utan att ändra resten av koden.
> ️ `flutter_secure_storage` fungerar **inte** på web — det är korrekt att web-adaptern använder SharedPreferences.
---
---
## 🔌 **5. Webhook-säkerhet — LOW (om Gitea-webhooks används)**
**Nuläge:** Inga webhook-endpoints hittades i koden. Åtgärd krävs endast om Gitea-webhooks kopplas in.
**Om webhooks läggs till** — validera `X-Gitea-Signature` med `crypto.timingSafeEqual`:
```typescript
// src/webhooks/gitea.controller.ts
import { Controller, Post, Headers, Body, RawBodyRequest, Req, UnauthorizedException } from '@nestjs/common';
import * as crypto from 'crypto';
import { Public } from '../auth/decorators/public.decorator';
@Controller('webhooks/gitea')
export class GiteaController {
@Public()
@Post()
async handleWebhook(
@Headers('x-gitea-signature-256') signature: string,
@Req() req: RawBodyRequest<Request>,
) {
const secret = process.env.GITEA_WEBHOOK_SECRET;
if (!secret) throw new Error('GITEA_WEBHOOK_SECRET saknas');
const hmac = crypto.createHmac('sha256', secret);
hmac.update(req.rawBody!);
const expected = `sha256=${hmac.digest('hex')}`;
if (!crypto.timingSafeEqual(Buffer.from(signature ?? ''), Buffer.from(expected))) {
throw new UnauthorizedException('Ogiltig signatur');
}
// Hantera event...
}
}
```
Lägg till `GITEA_WEBHOOK_SECRET` i `.env.example`.
---
---
## 🧪 **6. CI/CD-säkerhetstester — MEDIUM**
> ️ Projektet använder **Gitea** (inte GitHub), så GitHub Actions-workflows i originalplanen fungerar inte.
Nuvarande CI: tester körs via `npm test` på push (pipeline finns i Gitea).
**Utöka befintlig pipeline** med:
- `npm audit --audit-level=high` — kontrollera kända CVE:er i beroenden
- `npx prisma validate` — verifiera schema-integritet
- Lägg till `gitleaks` som pre-commit hook lokalt (körs inte i Docker-build)
```bash
# Installera som lokal pre-commit hook
npm install -g gitleaks
gitleaks protect --staged # kör före varje commit
```
---
---
## ✅ **7. Avslutande Checklista**
| **Åtgärd** | **Status** | **Ansvarsområde** |
| -------------------------------- | ---------- | ----------------------- |
| IDOR-skydd: recipes, pantry, meal-plan, receipt-alias | ✅ Klart | Backend |
| IDOR-skydd + userId-filtrering för **inventory** | ✅ Klart | Backend (NestJS/Prisma) |
| `dist/` tillagd i `.gitignore` | ✅ Klart | Git |
| `.env` (utan suffix) i `.gitignore` + `.env.example` | ✅ Klart | Git |
| Helmet, CORS, ThrottlerGuard, ValidationPipe | ✅ Klart | Backend |
| Flutter: token-abstraktion (`ITokenStorage`) | ✅ Klart | Flutter |
| Flutter: httpOnly cookie-alternativ (om XSS är reell risk) | ⬜ LOW | Flutter + Backend |
| Gitea webhook-signaturvalidering (om webhooks används) | ⬜ LOW | Backend |
| `npm audit` i CI-pipeline | ✅ Klart | CI/CD (Gitea) |
---
---
## 🎯 **8. Prioriterad Ordning för Implementering**
1. **Inventory IDOR + userId-fält** (CRITICAL) — ✅ KLART 2026-05-07
2. **dist/ i .gitignore** (HIGH) — ✅ KLART 2026-05-07
3. **bare .env + .env.example** (MEDIUM) — ✅ KLART 2026-05-07
4. **npm audit i CI** (MEDIUM) — ✅ KLART 2026-05-07
5. **Flutter httpOnly cookies** (LOW — kräver arkitekturförändring)
6. **Gitea webhook-validering** (LOW — bara relevant om webhooks används)
---
## 2026-05-07: Sammanfattning av senaste säkerhetsförbättringar
- **Inventory är nu user-scopad:** Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester).
- **IDOR-skydd för inventory:** Det är nu omöjligt för användare att läsa eller ändra andras inventarieposter. Tester verifierar att åtkomst nekas vid försök till IDOR.
- **.gitignore och deploy-hygien:** backend/dist och backend/tsconfig.tsbuildinfo ignoreras och är ej längre spårade i git. .env och .env.* ignoreras, men .env.example finns och är uppdaterad.
- **CI/CD-härdning:** npm audit och prisma validate körs i pipeline. Alla tester och byggen måste passera.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
@@ -0,0 +1,236 @@
# Implementeringsplan: "Se kvitto"-Modal för Kvittoimporten
**Mål**: MVP-vägen för split-view UX lägg till modal som visar OCR-text från parsade kvittoraderna.
**Scope**: 2-3 timmar
**Status**: Planering
---
## 1. Ändringar i `receipt_import_tab.dart`
### 1.1 Lägg till knapp "Se kvitto" i header-raden (rad ~745-752)
**Plats**: Höger om "Välj alla/Avmarkera alla"-knappen
```dart
// Innan: Row med bara "Välj alla"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
TextButton(...), // "Välj alla/Avmarkera alla"
],
)
// Efter: Lägg till "Se kvitto"-knapp
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('${items.length} rader — tryck för att redigera', style: theme.textTheme.titleSmall),
Row(
children: [
TextButton.icon(
onPressed: items.isEmpty ? null : () => _showReceiptPreview(context, items),
icon: const Icon(Icons.description_outlined),
label: const Text('Se kvitto'),
),
const SizedBox(width: 8),
TextButton(
onPressed: () => setState(...), // Befintlig "Välj alla"
child: Text(...),
),
],
),
],
)
```
### 1.2 Implementera `_showReceiptPreview`-metod
Lägg till denna metod i `_ReceiptImportTabState`:
```dart
Future<void> _showReceiptPreview(BuildContext context, List<ParsedReceiptItem> items) async {
if (!context.mounted) return;
await showDialog(
context: context,
builder: (ctx) => _ReceiptPreviewDialog(items: items),
);
}
```
---
## 2. Ny widget: `_ReceiptPreviewDialog`
Lägg till denna widget **i samma fil** (`receipt_import_tab.dart`), efter `_ReceiptImportResultRow`-klassen:
```dart
class _ReceiptPreviewDialog extends StatelessWidget {
final List<ParsedReceiptItem> items;
const _ReceiptPreviewDialog({required this.items});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return AlertDialog(
title: const Text('Kvittotexten i sin helhet'),
content: SizedBox(
width: 600, // Responsiv bredd på desktop
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Här visas all OCR-parsad text från kvittot. En rad per artikel:',
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
const SizedBox(height: 12),
Container(
decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerLowest,
border: Border.all(color: theme.colorScheme.outlineVariant),
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.all(12),
child: SelectableText.rich(
TextSpan(
children: items.isEmpty
? [TextSpan(text: '(Inga rader)', style: theme.textTheme.bodySmall)]
: items
.asMap()
.entries
.map((entry) {
final item = entry.value;
final lineNumber = entry.key + 1;
final lineText = _formatReceiptLine(item);
return TextSpan(
children: [
TextSpan(
text: '$lineNumber. ',
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.outlineVariant,
),
),
TextSpan(
text: lineText,
style: theme.textTheme.bodySmall?.copyWith(
fontFamily: 'monospace',
),
),
const TextSpan(text: '\n'),
],
);
})
.toList(),
),
style: theme.textTheme.bodySmall,
),
),
],
),
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Stäng'),
),
],
);
}
String _formatReceiptLine(ParsedReceiptItem item) {
final parts = <String>[];
if (item.quantity != null) {
parts.add('${item.quantity}');
}
if (item.unit != null) {
parts.add(item.unit!);
}
parts.add(item.rawName);
if (item.price != null) {
parts.add('${item.price} kr');
}
return parts.join(' ');
}
}
```
---
## 3. Implementeringssteg (steg-för-steg)
1. **Läs receipt_import_tab.dart** och identifiera raden med "Välj alla/Avmarkera alla"-knappen
2. **Refaktorera Row**: Lägg "Se kvitto"-knapp bredvid befintliga knapp
3. **Lägg till `_showReceiptPreview()`-metod** i `_ReceiptImportTabState`
4. **Implementera `_ReceiptPreviewDialog`-widget** på slutet av filen
5. **Testa**:
- Ladda ett kvitto
- Klicka "Se kvitto"-knappen
- Verifiera att texten är lesbar och formaterad
- Testa responsive bredd (dialog behöver minska på mobil)
---
## 4. Responsiv förbättring (optional)
Om dialogen behöver anpassas för mobil:
```dart
// I _ReceiptPreviewDialog.build():
final isWide = MediaQuery.of(context).size.width > 600;
return Dialog(
insetPadding: const EdgeInsets.all(16),
child: SizedBox(
width: isWide ? 600 : double.maxFinite, // Full bredd på mobil
// ...
),
);
```
---
## 5. Långsiktiga förbättringar (Phase 2)
Se `next_steps_flutter.md` för split-view roadmap:
- Horisontell split-view på desktop
- Scroll-synkronisering
- Tab-fallback på mobil
- AI-guiding labels
---
## Ärendemal
**Titel**: "Se kvitto"-modal för kvittoimporten
**Branch**: `feat/receipt-preview-modal`
**Labels**: `enhancement`, `import-ux`, `phase-1-mvp`
**Estimate**: 2-3h
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
+149
View File
@@ -0,0 +1,149 @@
# Flutter Performance Profileringsguide
## Mål
| Mätpunkt | Gränsvärde |
|---|---|
| Frame build-tid (60 Hz) | < 16 ms |
| Frame build-tid (120 Hz) | < 8 ms |
| Scroll jank (tappade frames) | 0 vid normal scroll |
| Minnesfotavtryck (app) | < 200 MB |
---
## 1. Starta i profile-läge
Kör alltid profilmätningar i **profile mode**, inte debug. Debug-läget har JIT-kompilering och extra overhead.
```bash
# Mot fysisk enhet
flutter run --profile
# Mot Chrome (web)
flutter run -d chrome --profile
```
---
## 2. Flutter DevTools Öppna
```bash
flutter pub global activate devtools
flutter pub global run devtools
```
Eller anslut direkt från terminalen när appen körs i profile mode Flutter skriver ut en DevTools-URL.
---
## 3. Timeline Mät frame-tider
1. Öppna **Performance**-fliken i DevTools.
2. Klicka **Record**.
3. Utför den aktion du vill mäta (t.ex. byt vy, scrolla).
4. Klicka **Stop**.
5. Granska:
- **UI thread** (Dart-kod) bör vara < 16 ms per frame.
- **Raster thread** (GPU) bör vara < 16 ms per frame.
- Röda/gula staplar = jank.
### Kritiska mätpunkter i appen
| Scenario | Vad att leta efter |
|---|---|
| Byta vy (NavigationBar) | Frame-tid vid `StatefulShellRoute`-byte; bör vara < 32 ms totalt |
| Scrolla receptlista | Inga röda frames; `GridView.builder` bör recykla element |
| Scrolla adminpaneler | `ListView.builder` i embedded-läge; verifiera att ingen `NeverScrollableScrollPhysics` blockerar |
| Kvittoimport kryssa i rad | Endast den berörda raden bör rebuilda (`ConsumerWidget.select`) |
| Kvittoimport "Välj alla" | Batch-uppdatering via `setSelectedForAll` en enda `state =` |
---
## 4. Widget Rebuild-spårning
Aktivera rebuild-räknare i DevTools under **Inspector → Widget rebuild counts**.
Alternativt: lägg till tillfällig räknare i en widget:
```dart
int _buildCount = 0;
@override
Widget build(BuildContext context, WidgetRef ref) {
debugPrint('${widget.runtimeType} build #${++_buildCount}');
// ...
}
```
### Förväntade rebuild-mönster efter optimeringar
- `_ReceiptImportResultRow` med index X ska bara rebuilda när `selected[X]` ändras, inte när andra rader kryssas.
- `AppShell` ska inte rebuilda vid vy-byte (StatefulShellRoute bevarar grenar).
- Admin-paneler ska inte rebuilda hela listan vid en alias-ändring.
---
## 5. Memory Profiler
1. DevTools → **Memory**-fliken.
2. Klicka **Take snapshot** före och efter en tung operation.
3. Jämför levande objekt leta efter läckor (ackumulerade `StreamSubscription`, `Timer`, `Notifier`).
---
## 6. flutter analyze + dart fix
```bash
flutter analyze
dart fix --apply
```
Åtgärda alla varningar om `const` och onödiga rebuilds.
---
## 7. Identifierade optimeringar (genomförda)
| Område | Åtgärd | Effekt |
|---|---|---|
| Admin-paneler | Tog bort `NeverScrollableScrollPhysics` + `shrinkWrap` | Scroll fungerar, O(n) layout istället för O(n²) |
| Admin alias-lista | `ListView.builder` istället för spread | Virtualiserad lista |
| FABs | Explicita `heroTag` på alla FABs | Eliminerar hero-animation-krasch vid vy-byte |
| Scrollables | `PageStorageKey` på alla listvy | Scrollposition bevaras vid vy-byte |
| Router | `StatefulShellRoute.indexedStack` | Branch-state bevaras; ingen ombyggnad vid tab-byte |
| Kvittoimport resultatlista | `ListView.builder` + `SizedBox` bound height | Virtualiserad; max 620 px synlig |
| Kvittoimport radwidget | `ConsumerWidget` med `provider.select((s) => s?.selected[index])` | Endast ändrad rad rebuildar vid checkbox-toggle |
| Kvittoimport batch-API | `setSelectedForAll`, `setSelectedForIndexes`, `setImportedResult` | En `state =` och en SharedPreferences-skrivning per operation |
---
## 8. Snabbtest Verifiera förbättringar
```bash
# Kör i profile mode och öppna DevTools automatiskt
flutter run --profile --devtools-server-address=http://127.0.0.1:9100
```
Kontrollchecklista:
- [ ] Vy-byte NavigationBar: inga röda frames i Timeline
- [ ] Scroll i receptlista: < 2 tappade frames per 100 frames
- [ ] Scroll i admin-flikar: fungerar utan lock
- [ ] Kvittoimport checkbox-toggle: rebuild-räknare ökar bara för berörd rad
- [ ] Kvittoimport "Välj alla": en burst av rebuilds (en per rad), inga dubbla
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
+53
View File
@@ -0,0 +1,53 @@
# Flutter Frontend - Anvandarguide
Detta dokument ar for anvandare och operativa testare.
Har beskriver vi vad som fungerar i Flutter-klienten och hur den anvands i praktiken.
## Dokumentstatus (2026-05-19)
- Fokus: anvandarflode, inte implementation.
- Teknisk detaljniva finns i `teknisk_beskrivning_flutter.md`.
- Planering och backlog finns i `next_steps_flutter.md`.
## Vad appen ar
Flutter-webben ar en klient for Recipe App som kors i Docker och exponeras via Caddy.
Den anvands parallellt med Next-frontenden under migrering och verifiering.
## Senaste forbattringar
- Kvittoimportens granskningsflode ar klart och stabiliserat.
- Pagande kvittoimport sparas i klientens session och kan atertas efter refresh/navigation.
- Tolkning av antal/forpackning i kvittorader ar forbattrad, inklusive format som `2st`.
- AI-kategoriforslag och produktforslag visas separerat for tydligare val.
- Flyerimport har nu sessionpersistens med lattviktig cache (`sessionId`, filnamn, valda rader).
- Flyer-tabben hydrerar tillstand via backend-sessioner vid aterbesok och app-omstart.
## Aktuella anvandarfloden
- Inloggning med anvandarnamn och losenord.
- Recept: lista, skapa, redigera, radera.
- Inventarie och baslager: skapa, redigera, konsumera.
- Matplan: veckovy, portionsjustering, inkopslista.
- Import: receptimport och kvittoimport med granskningssteg.
## Kanda begransningar
- Vissa adminfunktioner och avancerad AI-integration ar planerade men ej fullt migrerade.
- Bildimport forutsatter att containrar ar uppdaterade med senaste kod.
## Felsokning (anvandarniva)
1. Om sidan visar gammalt beteende efter deploy: hard uppdatering eller inkognito.
2. Om inloggning misslyckas: verifiera anvandarnamn/losenord (inte e-post).
3. Om importfloden fastnar: rapportera webblasarkonsolens fel till utvecklingsteamet.
## Relaterade dokument
- `next_steps_flutter.md` - roadmap och prioriteringar.
- `teknisk_beskrivning_flutter.md` - teknisk referens for drift/utveckling.
- `../README.md` - overgripande produktinformation.
## Notering
Aktiv och detaljerad status for Flutter-sparet finns i rotens dokumentation och i teknisk Flutter-dokumentation i samma katalog.
@@ -0,0 +1,60 @@
# Nasta steg: Flutter-migrering
Detta dokument ar Flutter-teamets roadmap och prioriteringslista.
All historik och implementationdetaljer finns i `teknisk_beskrivning_flutter.md`.
## Dokumentstatus (2026-05-19)
- Fokus: aktiv planering framat.
- Endast en roadmap for Flutter for att undvika dubbletter.
## Klart senaste sessionerna
- Fas 6b: granskningsflode for kvittoimport (edit, destination, merge, spara).
- Fas 6c: separering av AI-kategorichip och produktforslagschip.
- Fas 6d: klientpersistens for pagande kvittoimport + forbattrad antal/forpackningsinferens.
- Flyerimport: sessionpersistens i klient och backend-hydrering via sessions-endpoints.
## Pagande arbete
- Robust bildimport och diagnostik i drift.
- Aliasstrategi i kvittoimport (hybrid user-scope + global fallback via admin).
- Utokad adminfunktionalitet i Flutter-sparet.
- E2E-verifiering av flyerimport: tab-byte, refresh och app-omstart i staging.
## Prioriterade nasta steg
1. **Kvitto-import UX förbättring (Split-view långsiktigt)**
- MVP (kort sikt): Lägg till "Se kvitto"-modal som visar full OCR-text från parsade rader
- Knapp i radlist-header, öppnar dialog med ScrollableText
- Enkelt UI, höga UX-vinster
- Implementering: ~2h
- Långsiktigt (Phase 2): Split-view med scroll-synkronisering
- Desktop: Horisontell split (kvitto-text vänster, radlista höger)
- Tablet/Mobil: Tab-based fallback (radlista standard, "Se kvitto"-tab för kontext)
- Scroll-sync mellan text och rader (om rad 3 är synlig, visa motsvarande text)
- AI-guiding labels ("Denna rad matchade mejeri automatiskt")
- Implementering: ~8h
2. Verifiera bildimport och felhantering end-to-end i testmiljo.
3. Implementera alias-inlarning vid manuell korrigering i importflodet.
4. Forbattra UI/UX i granskningsfloden for kvittoimport.
5. Fortsatt migrering av kvarvarande adminfloden.
6. Lokalisera kvarvarande delar i import- och inventarievyer.
## Viktiga beslut
- Flutter ar separat klient mot befintliga API-kontrakt.
- Ingen klientspecifik speciallogik for datamatchning; backend ar sanningskalla.
- Next-frontenden kor parallellt tills verifierad parity ar uppnadd.
## Relaterade dokument
- `README.md` - anvandarperspektiv.
- `teknisk_beskrivning_flutter.md` - teknisk referens.
- `../NEXT_STEPS.md` - overgripande roadmap for hela produkten.
## Notering
Denna fil ar arkiv/planunderlag for Flutter-sparet. Primar status och prioritering finns i rotens `NEXT_STEPS.md`.
@@ -0,0 +1,128 @@
# Teknisk Beskrivning - Flutter Frontend
Detta dokument ar teknisk referens for systemadministratorer och programmerare.
Mallet ar en sammanhallen teknisk sanning utan duplicerade roadmap- eller anvandarsektioner.
## Dokumentstatus (2026-05-03)
- Fokus: arkitektur, drift och kanda gotchas.
- User guide finns i `README.md`.
- Planering finns i `next_steps_flutter.md`.
## Viktigt om miljo
Anvand inte absoluta Windows-sokvagar i kod eller scripts.
Bygg och drift sker pa Linux/Ubuntu i containeriserad miljo.
## Senaste tekniska tillagg
- Sessionpersistens i klienten for pagande kvittoimport.
- Forbattrad inferens for antal/forpackning (inklusive uttryck som `2st`).
- Tydlig separation i UI mellan AI-kategoriforslag och produktforslag.
- Ingen server-side persistens introducerad for kvittosession.
- Receptskapande: review-steg med redigerbara falt for quantity/unit/note per ingrediens.
- Receptskapande: tydlig varning i UI nar ingen produktmatchning hittas for inkluderad ingrediens.
- Receptskapande: stod for alternativa ingredienser i parse-svar (ex. "ris eller couscous") och visning i granskningsvyn.
- Global kopierbar feltext: copybar error-snackbar + SelectionArea pa appniva for battre felsokning/stod.
## Arkitektur
### Lager
- Presentation: `flutter/lib/features/*/presentation`
- State/Application: Riverpod providers/notifiers i `flutter/lib/features/*/data`
- Data/API: `flutter/lib/core/api`
- Platform abstraction: token storage i `flutter/lib/core/platform`
### Routing
- GoRouter i `flutter/lib/core/router/app_router.dart`
- ShellRoute for huvudnavigation
- Fullscreen-routes for skapa/redigera/detalj
### Auth
- Login via `POST /api/auth/login`
- Token-falt: `accessToken`
- Auth-gate i router med redirect logik
- `guardedApiCall()` hanterar logout vid 401
## Sakerhetsstatus (2026-05-07)
Denna sektion sammanfattar sakerhetsfunktioner som ar implementerade i Flutter-klienten och hur de fungerar tekniskt.
### Implementerat i Flutter
- Auth-gating i routing:
- Router-lagret stoppar navigation till skyddade vyer utan token/session.
- Om backend returnerar 401 i ett skyddat API-flode hanterar `guardedApiCall()` detta och triggar logout/omdirigering.
- Token-lagring via plattformsabstraktion:
- `ITokenStorage` definierar kontraktet (`getToken/saveToken/deleteToken`).
- Web-implementation (`WebTokenStorage`) lagrar token i `SharedPreferences` (web: localStorage).
- Arkitekturen gor att mobil implementation kan bytas till `flutter_secure_storage` utan att ovrig appkod andras.
- Minimerad klientside-auktorisering:
- Flutter forlitar sig pa backend som auktoritetskalla for access-kontroll.
- Klienten skickar bearer-token men gor inte egen resurstagarskapslogik som kan divergera fran serverregler.
- Kontraktsstyrd API-hantering:
- API-lagret accepterar 2xx pa importanrop och centraliserar felhantering.
- Minskar risken for ad-hoc felhantering i UI och inkonsekvent beteende vid auth-fel.
### Viktig begransning (web)
- Flutter Web kan inte anvanda `httpOnly` cookies enbart pa klientsidan.
- Nuvarande lagring i localStorage ar en praktisk kompromiss for web och innebar att XSS-hardening pa frontend och strict backend-headers/CSP ar fortsatt viktiga.
## API- och kontraktsprinciper
- Flutter foljer backend-kontrakt, ingen lokal speciallogik for matchning.
- 2xx-svar accepteras generellt i importanrop (inte hardkodning till enbart 200).
- Kvittoimport och receptimport ar separata floden med olika svarstyper.
## Kvittoimport - tekniska noter
- Granskningsflode med radredigering och destination (inventarie/baslager).
- Produktval stoder tradbaserat kategori -> produkt-flode.
- AI-kategorihint ar endast ett forslag; slutanvandaren validerar i granskningssteget.
## Drift och deploy
### Bygg och start (Flutter)
```bash
docker compose -f compose.yml -f compose.flutter.yml build recipe-flutter
docker compose -f compose.yml -f compose.flutter.yml up -d --no-deps recipe-flutter
```
### Viktiga verifieringar
- Compose merge: `docker compose -f compose.yml -f compose.flutter.yml config`
- Reachability via proxy/Caddy
- Runtime-loggar for importfloden vid felsokning
## Kanda fallgropar
- PDF-mime kan komma som `application/octet-stream` i Flutter Web.
- Orphan-varning ar normal nar huvudstack och Flutter-stack kors separat.
- Cache/service worker kan visa gammal frontend efter deploy.
- Vid massersattningar i Dart: verifiera parentesbalans i `showSnackBar(...)`-anrop innan build, annars faller web-kompilering med syntaxfel.
## Relaterade dokument
- `README.md` - anvandarguide.
- `next_steps_flutter.md` - aktiv planering.
- `../TEKNISK_BESKRIVNING.md` - backend/systemovergripande teknik.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
@@ -1,7 +1,66 @@
This file has been removed as all relevant information has been migrated.
# Prisma P3009 recovery (MySQL, migrationer)
**Problem:**
Prisma migrationer kan fastna i failed state (P3009) om en migration körts med fel SQL-citering (t.ex. "User" istället för `User` i MySQL) eller om deploy avbryts mitt i en migrering.
**Symptom:**
```
migrate found failed migrations in the target database, new migrations will not be applied. The `20260506144000_add_ai_engine_enabled` migration ... failed
```
**Lösning/playbook:**
1. Rätta migrationsfilen så att den använder backticks (`) för tabell- och kolumnnamn (MySQL-stil).
2. Kör:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --rolled-back 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
3. Om deploy klagar på duplicate column (dvs kolumnen finns redan):
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate resolve --applied 20260506144000_add_ai_engine_enabled --schema prisma/schema.prisma"
docker exec recipe-api sh -lc "cd /app && npx prisma migrate deploy --schema prisma/schema.prisma"
```
4. Verifiera status:
```bash
docker exec recipe-api sh -lc "cd /app && npx prisma migrate status --schema prisma/schema.prisma"
```
**Lessons learned:**
- Kontrollera alltid SQL-citering i migrationsfiler (MySQL kräver backticks, inte dubbla citattecken).
- Vid P3009: använd `migrate resolve` för att markera migrationen som rolled-back eller applied beroende på DB-läge.
- Kör alltid migrationskommandon i rätt container/miljö för att undvika env- och version-mismatch.
# Session 2026-05-06: Migreringar för user-scoped AI och premium
Denna session:
- Lade till `aiEngineEnabled` på User i Prisma-schema och migrerade databasen (manuell SQL vid behov).
- Implementerade endpoint och logik för admin att toggla AI per användare.
- Säkerställde att alla AI-funktioner är user-scoped och premiumstyrda.
- Lessons learned: Vid DB-connectivity-problem krävs ibland manuell migration och resolve (se driftsekvens i TEKNISK_BESKRIVNING.md).
Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för drift- och migreringsdetaljer.
# Migrering: Import-funktion → microservice-importer # Migrering: Import-funktion → microservice-importer
## Status: ✅ GENOMFÖRD 2026-04-30 ## Status: ✅ GENOMFÖRD 2026-04-30
## Dokumentstatus (2026-05-03)
### Målgrupp
Detta dokument är för systemadministratörer och utvecklare som ansvarar för integrationen mellan recipe-app och microservice-importer.
### Tillägg efter genomförd migrering
- Kvittoparsningens regelbaserade tolkning har förbättrats för multipack och enheter.
- Brödrelaterade kategoriregler och contradiction guards har utökats för högre träffsäkerhet.
- Klientens kvittosession i Flutter är nu persistent utan att ändra backendkontrakt eller införa serverlagring av sessionen.
- Kategoriträdet i seed-data har utökats med `Korvbröd` under `Fastfoodbröd`.
- **PDF-fix (2026-05-03):** `pdf-parse` använder `require()` istället för ESM-import; `pdfjs-dist/legacy/build/pdf.js` är fallback vid parsningsfel — löser `DOMMatrix is not defined` i Node.js Alpine-miljö.
- **Felkods-forwarding (2026-05-03):** `receipt-import.service.ts` returnerar nu `ServiceUnavailableException` (503) vid 503/429 från importer-api istället för `BadRequestException` (400).
- **Reproducerbart bygge (2026-05-03):** `package-lock.json` spåras i git; Dockerfile för importer-api kör `npm ci`.
- **AI-optimering (2026-05-03):** `looksLikeReceiptProductLine()` filtrerar PDF-rader utan siffra innan Mistral-anrop — minskar onödiga API-anrop vid kvittoimport.
- **Scope:** quick-import, parse-markdown, receipt-import - **Scope:** quick-import, parse-markdown, receipt-import
- **Arkitektur:** Backend-till-backend — recipe-app NestJS-backend anropar microservice-importer internt via HTTP. Frontend ändras inte. - **Arkitektur:** Backend-till-backend — recipe-app NestJS-backend anropar microservice-importer internt via HTTP. Frontend ändras inte.
- **OCR:** Implementerat i microservice-importer (tesseract.js + Alpine apk-paket) - **OCR:** Implementerat i microservice-importer (tesseract.js + Alpine apk-paket)
@@ -201,3 +260,7 @@ backend-tjänstens env.
- **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt på Docker-nätverket - **Auth stannar i recipe-app backend** — microservice-importer exponeras bara internt på Docker-nätverket
- **Bildoptimering vid sparande** behålls i recipe-app (sker vid `RecipesService.create()`, inte vid import) - **Bildoptimering vid sparande** behålls i recipe-app (sker vid `RecipesService.create()`, inte vid import)
- `receipt-import` splittad: AI-del → microservice, produktmatchning + DB → recipe-app backend - `receipt-import` splittad: AI-del → microservice, produktmatchning + DB → recipe-app backend
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
+157
View File
@@ -0,0 +1,157 @@
# Plan för produktlansering
Detta dokument är en releasechecklista.
Det kompletterar `NEXT_STEPS.md` och ska inte duplicera backloggen.
## Dokumentstatus (2026-05-21)
- Målgrupp: produktägare, systemadministratörer, utvecklingsteam.
- Fokus: vad som måste vara verifierat innan release.
## 1. Säkerhet och dataskydd
- [ ] Känsliga uppgifter krypterade enligt beslutad modell.
- [ ] Rate limiting aktiv på relevanta API/AI-endpoints.
- [ ] Secret-hantering verifierad (inga hardkodade hemligheter).
- [ ] Roll- och accesskontroller testade i praktiken.
- [ ] Migrera autentisering från `localStorage` till `httpOnly`-cookies i Flutter Web.
- [ ] Implementera automatiserad datarensning för `AiTrace` (retention-policy).
- [ ] Utför penetrationstest för IDOR och XSS.
- [ ] Dokumentera GDPR-processer (t.ex. rätt att glömmas, dataportabilitet).
## 2. DevOps och stabilitet
- [ ] CI/CD för build, test och deploy på plats.
- [ ] Migreringar + seedning kör konsekvent vid release.
- [ ] Health checks och loggning verifierade.
- [ ] Backup/restore testad för datavolymer.
## 3. Kvalitet och test
- [ ] Kritiska flöden har testtäckning (auth, import, CRUD, AI).
- [ ] Minst en end-to-end verifiering i testmiljö per release.
- [ ] DTO-validering och felhantering kontrollerad.
- [ ] Skapa E2E-tester för flyer- och kvittoimport (t.ex. Cypress eller Playwright).
- [ ] Validera OCR-korrigeringar med ett större dataset.
## 4. Funktionell releaseklarhet
- [ ] Kvittoimport fungerar end-to-end med granskningsteg.
- [ ] User-scoped produktmodell verifierad med flera testanvändare.
- [ ] Kategoriträd seedat och validerat i aktuell miljö.
- [ ] Bildimport och fallbackflöde fungerar i driftmiljö.
- [ ] Genomför manuell testning av aliasflödet med riktiga kvitton.
- [ ] Test sessionhydrering i olika scenarier (t.ex. flikbyte, app-krasch).
## 5. Riskhantering
- [ ] AI-kostnad, timeout och fallback-beteende verifierat.
- [ ] Ingen osynk mellan migrationer och seedskript.
- [ ] Kända release-risker dokumenterade med ansvarig ägare.
- [ ] Implementera adaptiv retry-logik med exponentiell backoff för AI-anrop.
- [ ] Lägg till kostnadsgränser och varningar i `AiTraceService`.
- [ ] Dokumentera fallback-beteende (t.ex. cache, manuell granskning).
## 6. Prestanda och skalbarhet
- [ ] Optimera Prisma-frågor med `EXPLAIN ANALYZE` och indexering.
- [ ] Implementera virtuell scrollning i Flutter för stora listor.
- [ ] Dokumentera belastningsgränser för `importer-api` och planera för skalning.
- [ ] Lägg till Redis-cache för vanliga frågor (t.ex. produkt-sökning).
## 7. Dokumentation och användarstöd
- [ ] Skapa admin-handbok med vanliga arbetsflöden.
- [ ] Förbättra felmeddelanden i UI med tydliga instruktioner.
- [ ] Utöka `HelpTextsModule` med kontextuella guider för alla huvudfunktioner.
- [ ] Generera OpenAPI-specifikation för backend (t.ex. med `@nestjs/swagger`).
## Relaterade dokument
- `NEXT_STEPS.md` — övergripande prioriteringar.
- `TEKNISK_BESKRIVNING.md` — teknisk implementation.
- `flutter/next_steps_flutter.md` — Flutter-specifik leveransplan.
- `MVP_CHECKLISTA.md` — testchecklista för MVP.
## Kritiska utvecklingsområden
### 1. Säkerhet och dataskydd (Högsta prioritet)
**Motivering:**
- Känsliga uppgifter (t.ex. JWT i localStorage) och AI-trace-loggar innehåller maskerade men potentiellt känsliga uppgifter.
- Risk för XSS-attacker och IDOR om inte åtgärdat.
**Åtgärder:**
- Migrera autentisering till `httpOnly`-cookies.
- Implementera automatiserad datarensning för `AiTrace`.
- Utför penetrationstester.
---
### 2. Stabilitet i AI-integration (Hög prioritet)
**Motivering:**
- Timeout och retry-logik kan leda till 503-fel.
- Saknad kostnadskontroll för AI-anrop.
**Åtgärder:**
- Implementera adaptiv retry-logik.
- Lägg till kostnadsgränser i `AiTraceService`.
- Dokumentera fallback-beteende.
---
### 3. Sluttestning av importflöden (Hög prioritet)
**Motivering:**
- Alias-strategin och sessionhydrering är ej fullt testade.
- Saknas E2E-tester för hela importflödet.
**Åtgärder:**
- Genomför manuell testning med riktiga kvitton.
- Skapa E2E-tester.
---
### 4. Prestanda och skalbarhet (Medel prioritet)
**Motivering:**
- Långsamma Prisma-frågor och renderingsproblem i Flutter-UI.
- Saknad caching-strategi.
**Åtgärder:**
- Optimera databasfrågor.
- Implementera virtuell scrollning.
- Lägg till Redis-cache.
---
### 5. Dokumentation och användarstöd (Medel prioritet)
**Motivering:**
- Saknas admin-guider och tydliga felmeddelanden.
- Begränsat innehåll i `HelpTextsModule`.
**Åtgärder:**
- Skapa admin-handbok.
- Utöka hjälptexter.
- Generera OpenAPI-dokumentation.
---
## Prioriteringsordning
| **Område** | **Prioritet** | **Berörda filer/dokument** |
|---------------------------------|---------------|-----------------------------------------------------|
| 1. Säkerhet och dataskydd | ⭐⭐⭐⭐⭐ | `TEKNISK_BESKRIVNING.md`, `flutter/.../auth/` |
| 2. Stabilitet i AI-integration | ⭐⭐⭐⭐ | `flyerimporter.md`, `backend/src/ai/` |
| 3. Sluttestning av importflöden | ⭐⭐⭐⭐ | `MVP_CHECKLISTA.md`, `NEXT_STEPS.md` |
| 4. Prestanda och skalbarhet | ⭐⭐⭐ | `backend/src/flyer-import/`, `flutter/lib/admin/` |
| 5. Dokumentation och stöd | ⭐⭐ | `README.md`, `HelpTextsModule`, `backend/src/ai/` |
---
## Nästa steg
1. Låsa säkerhetsbristerna (särskilt autentisering och dataskydd).
2. Stabilisera AI-integration med bättre felhantering och kostnadskontroll.
3. Kör sluttestning av importflöden och rätta eventuella regressioner.
4. Optimera prestanda i databas och UI.
5. Förbättra dokumentationen för admin och slutanvändare.
Dessa åtgärder säkerställer en **stabil, säker och användarvänlig lansering**.
+104
View File
@@ -0,0 +1,104 @@
# Plan för systematisk backend-review och optimering
## Mål
1. Minska komplexitet och duplicering.
2. Förbättra prestanda och stabilitet.
3. Göra koden enklare att underhålla och vidareutveckla.
4. Införa kvalitetsgrindar så förbättringar håller över tid.
## Arbetssätt
1. Jobba i små, säkra iterationer per modul/domän.
2. Mät före och efter varje förändring.
3. Lås upp förbättringar med tester och CI-gates.
4. Prioritera förändringar med hög effekt och låg risk först.
## Fas 1: Baslinje och kartläggning (1 vecka)
1. Inventera backend per modul:
- Endpoints, tjänster, databasaccess, externa integrationer.
2. Sätt baslinjemätningar:
- Responstider per kritisk endpoint (p50/p95), felgrad, DB-latens.
- Nuvarande testtäckning per modul.
3. Skapa hotspot-lista:
- Långa metoder, hög cyclomatic complexity, duplicerad logik, N+1-frågor.
4. Leverabel:
- Prioriterad backlog med topp 10 förbättringsområden.
## Fas 2: Snabba vinster och kodhygien (1-2 veckor)
1. Standardisera felhantering:
- Enhetlig exception mapping och API-felmodell.
2. Rensa duplicerad kod:
- Flytta gemensam logik till tydliga utilities/domänservices.
3. Förbättra validering:
- Konsekvent DTO/valideringslager in och ut.
4. Inför striktare lint-regler:
- Max function length, complexity-tak, no-dead-code.
5. Leverabel:
- Minskad kodvolym i hotspots och jämnare kodstandard.
## Fas 3: Arkitektur-förenkling (2-3 veckor)
1. Tydlig separering av lager:
- Controller = transport.
- Service = affärslogik.
- Repository/data layer = persistens.
2. Minska beroendekoppling:
- Ta bort korsberoenden mellan moduler.
3. Inför tydliga domängränser:
- En modul ska kunna förstås utan att läsa flera andra.
4. Leverabel:
- Enklare call-flöden och färre starkt kopplade beroenden.
## Fas 4: Databas och prestanda (1-2 veckor)
1. Granska tunga queries:
- N+1, överhämtning, saknade index, ineffektiva joins.
2. Förbättra dataåtkomst:
- Standard för pagination/filtering.
- Selektiv hämtning av fält.
3. Caching där det är motiverat:
- Endast för dyra och frekventa läsningar.
4. Leverabel:
- Mätbar förbättring i p95 och minskad DB-belastning.
## Fas 5: Teststrategi och regressionsskydd (1-2 veckor, löpande)
1. Lägg tester där risk och affärsvärde är högst:
- Kritiska use cases först.
2. Balans i testpyramiden:
- Fler enhetstester för domänlogik.
- Fokuserade integrations- och API-tester för flöden.
3. Kontrakttester för externa integrationer.
4. Leverabel:
- Högre täckning i kritiska moduler och färre regressionsbuggar.
## Fas 6: Säkerhet och driftbarhet (parallellt)
1. Säkerhetsgranskning:
- Input-validering, auth/role-kontroller, secret-hantering.
- Inventory är nu user-scopad och IDOR-skyddad: Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester). Tester verifierar att åtkomst nekas vid försök till IDOR.
2. Driftbarhet:
- Strukturerad loggning, korrelations-id, tydligare metrics.
3. Resiliens:
- Timeout/retry/circuit-breaker för externa beroenden.
4. Leverabel:
- Färre driftincidenter och enklare felsökning.
## Kvalitetsgrindar i CI
1. Build och lint måste passera.
2. Tester måste passera.
3. Miniminivå för täckning i ändrade moduler.
4. Blockera PR vid ökad komplexitet över satt tröskel.
5. Enkel performance-smoke på kritiska endpoints.
## Prioriteringsmodell för varje förbättring
1. Effekt: prestanda, stabilitet, underhållbarhet.
2. Risk: regressionsrisk och driftsrisk.
3. Insats: utvecklingstid.
4. Välj först: hög effekt + låg/medel risk + låg/medel insats.
## Definition of Done
1. Kritiska endpoints har förbättrad p95.
2. Topp-hotspots är refaktorerade eller borttagna.
3. Kodduplicering reducerad i prioriterade moduler.
4. Testskydd finns för alla kritiska flöden.
5. CI-gates förhindrar att kvaliteten glider tillbaka.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
@@ -3,6 +3,7 @@ WORKDIR /app
COPY package.json ./ COPY package.json ./
RUN npm install RUN npm install
# 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
FROM node:22-alpine AS builder FROM node:22-alpine AS builder
WORKDIR /app WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
+5
View File
@@ -0,0 +1,5 @@
## Arkiverad frontend-dokumentation
- Den aktiva dokumentationen finns i rotens `README.md`, `TEKNISK_BESKRIVNING.md` och `NEXT_STEPS.md`.
- Senaste uppdatering (2026-05-19): flyerimport har session-endpoints i backend och klientpersistens/hydrering i Flutter.
- Denna fil behålls endast som arkivreferens för äldre frontend-spår.

Some files were not shown because too many files have changed in this diff Show More