feat: dashboard navigation, photo processing refactor, and UI fixes#182
feat: dashboard navigation, photo processing refactor, and UI fixes#182copyandpastecoder merged 18 commits intoMasterfrom
Conversation
…eb images; restart pods; ensure migrations applied; deploy to https://dev.myuglyrocks.com
Apply the same fix to cycle-stats-chart.tsx that was applied to dashboard statistics charts. All CSS variables in this project use oklch() format, so hsl(var(--...)) creates invalid CSS.
Bumps the npm-minor group with 8 updates in the /src/web directory: | Package | From | To | | --- | --- | --- | | [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) | `5.90.12` | `5.90.14` | | [lucide-react](https://github.com/lucide-icons/lucide/tree/HEAD/packages/lucide-react) | `0.561.0` | `0.562.0` | | [next](https://github.com/vercel/next.js) | `16.0.10` | `16.1.1` | | [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.68.0` | `7.69.0` | | [zod](https://github.com/colinhacks/zod) | `4.2.0` | `4.2.1` | | [@next/bundle-analyzer](https://github.com/vercel/next.js/tree/HEAD/packages/next-bundle-analyzer) | `16.0.10` | `16.1.1` | | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.0.2` | `25.0.3` | | [eslint-config-next](https://github.com/vercel/next.js/tree/HEAD/packages/eslint-config-next) | `16.0.10` | `16.1.1` | Updates `@tanstack/react-query` from 5.90.12 to 5.90.14 - [Release notes](https://github.com/TanStack/query/releases) - [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md) - [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.90.14/packages/react-query) Updates `lucide-react` from 0.561.0 to 0.562.0 - [Release notes](https://github.com/lucide-icons/lucide/releases) - [Commits](https://github.com/lucide-icons/lucide/commits/0.562.0/packages/lucide-react) Updates `next` from 16.0.10 to 16.1.1 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](vercel/next.js@v16.0.10...v16.1.1) Updates `react-hook-form` from 7.68.0 to 7.69.0 - [Release notes](https://github.com/react-hook-form/react-hook-form/releases) - [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md) - [Commits](react-hook-form/react-hook-form@v7.68.0...v7.69.0) Updates `zod` from 4.2.0 to 4.2.1 - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](colinhacks/zod@v4.2.0...v4.2.1) Updates `@next/bundle-analyzer` from 16.0.10 to 16.1.1 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v16.1.1/packages/next-bundle-analyzer) Updates `@types/node` from 25.0.2 to 25.0.3 - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Updates `eslint-config-next` from 16.0.10 to 16.1.1 - [Release notes](https://github.com/vercel/next.js/releases) - [Changelog](https://github.com/vercel/next.js/blob/canary/release.js) - [Commits](https://github.com/vercel/next.js/commits/v16.1.1/packages/eslint-config-next) --- updated-dependencies: - dependency-name: "@tanstack/react-query" dependency-version: 5.90.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm-minor - dependency-name: lucide-react dependency-version: 0.562.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-minor - dependency-name: next dependency-version: 16.1.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-minor - dependency-name: react-hook-form dependency-version: 7.69.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: npm-minor - dependency-name: zod dependency-version: 4.2.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: npm-minor - dependency-name: "@next/bundle-analyzer" dependency-version: 16.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-minor - dependency-name: "@types/node" dependency-version: 25.0.3 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: npm-minor - dependency-name: eslint-config-next dependency-version: 16.1.1 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: npm-minor ... Signed-off-by: dependabot[bot] <support@github.com>
Bumps AWSSDK.S3 from 4.0.14.3 to 4.0.16 Bumps Scalar.AspNetCore from 2.11.6 to 2.11.10 --- updated-dependencies: - dependency-name: AWSSDK.S3 dependency-version: 4.0.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet-minor - dependency-name: Scalar.AspNetCore dependency-version: 2.11.10 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dotnet-minor ... Signed-off-by: dependabot[bot] <support@github.com>
- Add max-h-[60vh] and overflow-y-auto to CommandList - Ensures materials dropdown is scrollable on mobile devices - Fixes issue where users couldn't scroll through materials list when adding materials to stage runs
- Add OriginalStorageKey, OriginalUrl, OriginalMimeType, OriginalFileSizeBytes fields to Photo and InventoryPhoto entities - Implement 3-phase photo processing: * Phase 1: Thumbnail (fast, user sees immediately) * Phase 2: Large WebP variant (user can view 1600x1600 while original processes) * Phase 3: Preserve original in native format (JPG/PNG/HEIC/etc, no conversion) - Update ImageProcessingService with PreserveOriginalAsync method - Update PhotoProcessingJob and InventoryPhotoProcessingJob with PreserveOriginalAsync - Update PhotosController to chain 3 background jobs for complete processing - Update delete methods to remove original files along with WebP variants - Add database migration for new original photo fields This allows users to: - View the large variant (1600x1600 WebP) immediately when clicking photos - Keep the true original upload for later use or deletion - Have full quality originals preserved without lossy WebP conversion
- Add nextStageWeightBefore and nextStageWaterAmount fields to Complete Stage modal - Fields only show when 'Repeat' or 'Advance' is selected - Values are transferred to the auto-created next stage - Reset fields when modal closes - Improves workflow by allowing users to set starting values for the next stage run
- Add onClick prop to StatCard component with cursor-pointer styling - Active Cycles card navigates to /cycles - Completed Cycles card navigates to /cycles?tab=completed - My Tumblers card navigates to /tumblers - Add URL parameter support to cycles page for initial tab state
…pi/MyUglyRocks.Api/dotnet-minor-a4739b4ff2 deps(dotnet): Bump the dotnet-minor group with 2 updates
…n/src/web/npm-minor-c39b9e994d deps(npm): bump the npm-minor group across 1 directory with 8 updates
…ches Merge/consolidate branches
- Update TargetFramework from net10.0 to net9.0 in all projects - Downgrade NuGet packages to .NET 9 compatible versions: - Microsoft.EntityFrameworkCore: 10.0.1 -> 9.0.0 - Npgsql.EntityFrameworkCore.PostgreSQL: 10.0.0 -> 9.0.2 - Microsoft.AspNetCore packages: 10.0.1 -> 9.0.0 - Serilog.AspNetCore: 10.0.0 -> 9.0.0 - Update Dockerfile to use .NET 9 SDK and runtime images Fixes NETSDK1045 error with SDK 9.0.201
Downgrade to .NET 9.0 for SDK compatibility
## Features - Add clickable navigation to dashboard stat cards - Active Cycles /cycles - Completed Cycles /cycles?tab=completed - My Tumblers /tumblers - Enhance water amount input with unit conversion (ml/fl oz) - Add image error handling with placeholder fallback for inventory cards ## Refactoring - Eliminate code duplication in photo processing jobs - Create generic BasePhotoProcessingJob<TPhoto> base class - Reduce PhotoProcessingJob and InventoryPhotoProcessingJob from 310 to 76 lines (-75%) - Total reduction: ~460 lines of duplicate code ## Bug Fixes - Fix cycles expand/collapse individual control after using Expand All - Add onExpandedChange callback to reset global state - Fix inventory card text overflow on desktop - Add overflow-hidden to text container - Add flex-shrink-0 to right side elements - Increase padding from p-3 to p-4 - Fix React hook error (#310) by moving useState to component level - Fix water amount type mismatch with proper numeric input ## Database - Add migration for photo original file fields - OriginalFileSizeBytes, OriginalMimeType, OriginalStorageKey, OriginalUrl - Apply to both photos and inventory_photos tables ## Documentation - Add XML documentation for photo file naming conventions - Export WATER_UNITS constant from stage-form See CHANGELOG-2025-12-31.md for detailed changes. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…to-refactor-ui-fixes feat: dashboard navigation, photo processing refactor, and UI fixes
There was a problem hiding this comment.
Pull request overview
This PR implements dashboard navigation enhancements, refactors photo processing jobs to eliminate code duplication, and fixes several UI issues related to inventory cards and cycle expansion controls. It also adds support for preserving original uploaded photos alongside WebP variants.
- Dashboard stat cards are now clickable and navigate to relevant pages
- Photo processing jobs refactored using a generic base class, reducing ~460 lines of duplicate code
- UI fixes for inventory card text overflow and image error handling
- Cycles expand/collapse control restored after using "Expand All"
Reviewed changes
Copilot reviewed 30 out of 32 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/web/src/components/ui/stat-card.tsx | Added onClick prop and cursor pointer styling for clickable stat cards |
| src/web/src/components/ui/cycle-stats-chart.tsx | Removed unnecessary hsl() wrappers from CSS variable references |
| src/web/src/components/stage/stage-materials-section.tsx | Added max-height and scrolling to material selection dropdown |
| src/web/src/components/stage-form/index.tsx | Exported WATER_UNITS constant for reuse |
| src/web/src/components/cycle-card/types.ts | Added onExpandedChange callback prop |
| src/web/src/components/cycle-card/cycle-card.tsx | Implemented callback to reset expand state on manual toggle |
| src/web/src/app/(protected)/inventory/page.tsx | Fixed text overflow, added image error handling, increased padding |
| src/web/src/app/(protected)/dashboard/page.tsx | Added navigation onClick handlers to stat cards |
| src/web/src/app/(protected)/cycles/page.tsx | Added URL parameter handling for tab selection and expand state reset |
| src/web/src/app/(protected)/cycles/[id]/page.tsx | Added water amount input with unit conversion for next stage |
| src/web/package.json | Updated frontend dependencies to latest versions |
| src/api/MyUglyRocks.Infrastructure/Services/ImageProcessingService.cs | Added PreserveOriginalAsync method for phase 3 processing |
| src/api/MyUglyRocks.Infrastructure/MyUglyRocks.Infrastructure.csproj | Downgraded from non-existent .NET 10.0 to .NET 9.0 |
| src/api/MyUglyRocks.Infrastructure/Migrations/*.cs | Added database migrations for original photo fields |
| src/api/MyUglyRocks.Infrastructure/Jobs/*.cs | Refactored photo processing jobs with generic base class |
| src/api/MyUglyRocks.Core/*.csproj | Corrected .NET version and dependencies |
| src/api/MyUglyRocks.Core/Entities/*.cs | Added original photo fields to Photo and InventoryPhoto entities |
| src/api/MyUglyRocks.Api/*.cs | Updated to three-phase photo processing and .NET 9.0 |
| src/api/MyUglyRocks.Abstractions/Interfaces/IImageProcessingService.cs | Added OriginalPreservationResult record and PreserveOriginalAsync method |
Files not reviewed (1)
- src/web/package-lock.json: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Add original photo fields to photos table | ||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalStorageKey", | ||
| table: "photos", | ||
| type: "text", | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalUrl", | ||
| table: "photos", | ||
| type: "text", | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalMimeType", | ||
| table: "photos", | ||
| type: "character varying(50)", | ||
| maxLength: 50, | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<long>( | ||
| name: "OriginalFileSizeBytes", | ||
| table: "photos", | ||
| type: "bigint", | ||
| nullable: true); | ||
|
|
||
| // Add original photo fields to inventory_photos table | ||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalStorageKey", | ||
| table: "inventory_photos", | ||
| type: "text", | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalUrl", | ||
| table: "inventory_photos", | ||
| type: "text", | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<string>( | ||
| name: "OriginalMimeType", | ||
| table: "inventory_photos", | ||
| type: "character varying(50)", | ||
| maxLength: 50, | ||
| nullable: true); | ||
|
|
||
| migrationBuilder.AddColumn<long>( | ||
| name: "OriginalFileSizeBytes", | ||
| table: "inventory_photos", | ||
| type: "bigint", | ||
| nullable: true); | ||
| } | ||
|
|
||
| /// <inheritdoc /> | ||
| protected override void Down(MigrationBuilder migrationBuilder) | ||
| { | ||
| // Drop original photo fields from photos table | ||
| migrationBuilder.DropColumn( | ||
| name: "OriginalStorageKey", | ||
| table: "photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalUrl", | ||
| table: "photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalMimeType", | ||
| table: "photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalFileSizeBytes", | ||
| table: "photos"); | ||
|
|
||
| // Drop original photo fields from inventory_photos table | ||
| migrationBuilder.DropColumn( | ||
| name: "OriginalStorageKey", | ||
| table: "inventory_photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalUrl", | ||
| table: "inventory_photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalMimeType", | ||
| table: "inventory_photos"); | ||
|
|
||
| migrationBuilder.DropColumn( | ||
| name: "OriginalFileSizeBytes", | ||
| table: "inventory_photos"); |
There was a problem hiding this comment.
The duplicate migration files (20251231000000_AddOriginalPhotoFields.cs and 20251231203756_AddPhotoOriginalFields.cs) appear to have the same purpose but different timestamps. The earlier migration (20251231000000) adds slightly different column types for OriginalMimeType (character varying(50) vs text). This could cause migration conflicts if both are applied. Consider removing the duplicate migration or consolidating them into a single migration.
| // Add original photo fields to photos table | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalStorageKey", | |
| table: "photos", | |
| type: "text", | |
| nullable: true); | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalUrl", | |
| table: "photos", | |
| type: "text", | |
| nullable: true); | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalMimeType", | |
| table: "photos", | |
| type: "character varying(50)", | |
| maxLength: 50, | |
| nullable: true); | |
| migrationBuilder.AddColumn<long>( | |
| name: "OriginalFileSizeBytes", | |
| table: "photos", | |
| type: "bigint", | |
| nullable: true); | |
| // Add original photo fields to inventory_photos table | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalStorageKey", | |
| table: "inventory_photos", | |
| type: "text", | |
| nullable: true); | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalUrl", | |
| table: "inventory_photos", | |
| type: "text", | |
| nullable: true); | |
| migrationBuilder.AddColumn<string>( | |
| name: "OriginalMimeType", | |
| table: "inventory_photos", | |
| type: "character varying(50)", | |
| maxLength: 50, | |
| nullable: true); | |
| migrationBuilder.AddColumn<long>( | |
| name: "OriginalFileSizeBytes", | |
| table: "inventory_photos", | |
| type: "bigint", | |
| nullable: true); | |
| } | |
| /// <inheritdoc /> | |
| protected override void Down(MigrationBuilder migrationBuilder) | |
| { | |
| // Drop original photo fields from photos table | |
| migrationBuilder.DropColumn( | |
| name: "OriginalStorageKey", | |
| table: "photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalUrl", | |
| table: "photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalMimeType", | |
| table: "photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalFileSizeBytes", | |
| table: "photos"); | |
| // Drop original photo fields from inventory_photos table | |
| migrationBuilder.DropColumn( | |
| name: "OriginalStorageKey", | |
| table: "inventory_photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalUrl", | |
| table: "inventory_photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalMimeType", | |
| table: "inventory_photos"); | |
| migrationBuilder.DropColumn( | |
| name: "OriginalFileSizeBytes", | |
| table: "inventory_photos"); | |
| // Intentionally left empty. | |
| // A later migration (20251231203756_AddPhotoOriginalFields) is the | |
| // authoritative source for adding the original photo fields. | |
| } | |
| /// <inheritdoc /> | |
| protected override void Down(MigrationBuilder migrationBuilder) | |
| { | |
| // Intentionally left empty. | |
| // The corresponding columns are managed by the later migration. |
No description provided.