Added comprehensive mobile card layout for Products/Index, completing Phase 2 responsive design.
**Products Mobile View Features:**
- Horizontal layout with 80x80px product image on left
- Product name, category badge, price, and status on right
- Full description (100 chars) below header
- 2-column grid for Stock and Weight info
- Conditional badges for multi-buys and variants
- Full-width "View Details & Edit" button
**Mobile UX Highlights:**
- Larger product images (80px vs 50px desktop thumbnail)
- Price prominently displayed in green (fs-5)
- Stock status color-coded (success/warning)
- Variations clearly shown with icon badges
- Touch-friendly full-width action button
**Technical Implementation:**
- d-flex for image + info horizontal layout
- flex-grow-1 for responsive info section
- row g-2 for 2-column grid with gutters
- Conditional rendering for variations badges
- ARIA labels for accessibility
**Phase 2 Now Complete:**
✅ Categories - Simple cards with description
✅ Users - Minimal cards with user info
✅ ShippingRates - 2x2 grid for rate details
✅ VariantCollections - Cards with JSON preview
✅ Products - Rich cards with images and variations
✅ Orders - Mobile cards (already implemented)
All main Index views now mobile-optimized!
🚀 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented responsive mobile card layouts for all main Index views, providing superior mobile UX while maintaining desktop table views.
**Responsive Design Pattern:**
- Desktop (≥992px): Table layout with all data columns
- Mobile (<992px): Card-based layout optimized for touch interaction
- Breakpoint: Bootstrap's lg breakpoint for optimal viewing experience
**Views Converted:**
1. **Categories/Index.cshtml:**
- Mobile cards with name, description, product count, status
- Full-width action buttons for easy touch interaction
- Clear visual hierarchy with icons and badges
2. **Users/Index.cshtml:**
- Simplified mobile cards showing username, created date, status
- Conditional delete button (protected admin account)
- Clean, minimal design for quick user management
3. **ShippingRates/Index.cshtml:**
- 2x2 grid layout for shipping rate data (country, price, weight, delivery)
- Visual separation with light background boxes
- All critical information displayed in scannable format
4. **VariantCollections/Index.cshtml:**
- Properties JSON displayed in scrollable code block
- Created/Updated dates in compact format
- Clear deactivation action for variant collections
**Mobile UX Enhancements:**
- ✅ 44px minimum touch targets (Bootstrap .btn default)
- ✅ Full-width buttons with .d-grid gap-2 for easy tapping
- ✅ Proper spacing with mb-3 between cards
- ✅ Clear visual hierarchy with card-title and badges
- ✅ Descriptive button text (not just icons) on mobile
- ✅ Responsive icons and status indicators
- ✅ Word-break handling for long JSON strings
**Technical Implementation:**
- Used Bootstrap's d-none d-lg-block for desktop tables
- Used d-lg-none for mobile card views
- No JavaScript required - pure CSS responsive design
- Maintains all functionality from desktop view
- Zero data loss in mobile transformation
**Accessibility Maintained:**
- All ARIA labels preserved from Phase 1
- Semantic HTML structure in both views
- Proper heading hierarchy maintained
- Keyboard navigation fully functional
🚀 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented comprehensive accessibility enhancements to meet WCAG 2.1 AA standards:
**Skip Navigation:**
- Added skip-to-content link for keyboard users
- Link appears on focus and jumps directly to main content area
**Screen Reader Support:**
- Created .sr-only and .sr-only-focusable utility classes
- Added aria-hidden="true" to all decorative icons
- Added descriptive aria-label attributes to all icon-only buttons
**Enhanced Focus Indicators:**
- Implemented 3px visible outlines on all interactive elements
- Added :focus-visible for keyboard-only focus indicators
- Special focus styling for primary actions (orange outline)
- Consistent 2px outline-offset for better visibility
**Table Accessibility:**
- Added scope="col" attributes to all table headers
- Properly grouped button actions with role="group" and aria-label
**Button Improvements:**
- All icon-only buttons now have descriptive ARIA labels
- Added responsive text labels (visible on sm+ screens, hidden on mobile)
- Improved button groups with proper ARIA roles
**Files Modified:**
- _Layout.cshtml: Skip link, accessible menu close button
- Categories/Index.cshtml: ARIA labels, table scopes
- Users/Index.cshtml: ARIA labels, table scopes
- Orders/Index.cshtml: Table scopes
- Products/Index.cshtml: Table scopes
- ShippingRates/Index.cshtml: ARIA labels, table scopes
- VariantCollections/Index.cshtml: ARIA labels, table scopes
- modern-admin.css: Accessibility utilities and enhanced focus styles
**WCAG 2.1 AA Criteria Addressed:**
- 2.4.1 Bypass Blocks (Level A)
- 2.4.7 Focus Visible (Level AA)
- 4.1.2 Name, Role, Value (Level A)
- 1.3.1 Info and Relationships (Level A)
🚀 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
- Changed JSON naming policy from CamelCase to SnakeCaseLower for SilverPay API compatibility
- Fixed field name from 'fiat_amount' to 'amount' in request body
- Used unique payment ID instead of order ID to avoid duplicate external_id conflicts
- Modified SilverPayApiResponse to handle string amounts from API
- Added [JsonIgnore] attributes to computed properties to prevent JSON serialization conflicts
- Fixed test compilation errors (mock service and enum casting issues)
- Updated SilverPay endpoint to http://10.0.0.52:8001/🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Critical fix for £0 order bug:
- When users select a variant and click 'Add to Basket', the confirmvar: callback triggers HandleConfirmVariant
- This method was passing variantId: null to AddItem(), causing cart items to have no variant and price £0
- Now looks up selected variant by name, extracts its ID, and passes it to cart
- Added logging to track which variant is being used
- Also includes CSV variant conversion utility and sample fixed import file
Previous build at 20:05 used cached layers from 18:06.
This empty commit will trigger pipeline #519 to rebuild fresh.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL BUG FIX: HandleAddToCart was only checking MultiBuys for price,
never ProductVariants. This caused all variant-based products to use the
base price (£0), resulting in £0 orders.
Changes:
- HandleAddToCart now checks variants FIRST for pricing
- Falls back to multi-buy, then base price (correct priority order)
- Uses proper Product-based AddItem() method to pass variant IDs
- Added logging to track which pricing method is used
- HandleQuickBuy already had correct variant detection (no changes)
Result: Orders now correctly calculate total using variant prices (e.g., £90, £160)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Log variant count and prices when fetching products to diagnose
why variants aren't being detected during add-to-cart flow.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added logging to diagnose why orders are created with £0 pricing:
- Log when product has variants and variant selection is shown
- Log WARNING when product has no variants and base price is used
- Helps identify if variants are missing or not being detected
Troubleshooting: Orders showing £0 despite variants having correct prices
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL BUG FIX: Orders were totaling £0 because imported products had:
- Base price set to £0
- Variants stored as ProductMultiBuys (not ProductVariants)
- Order creation couldn't find variant prices → used base price of £0
Changes:
- CSV import now detects format: semicolons (;) = variants, colons (:) = multi-buys
- Added ImportProductVariantsAsync() to handle pipe-delimited variant format
- Format: "value; price; stock|value; price; stock" (e.g., "10; 30.00; 100|25; 70.00; 50")
- Updated CSV export to prioritize ProductVariants over MultiBuys
- Updated CSV template with correct variant format examples
- Added comprehensive documentation in VARIANT_CSV_FORMAT.md
Migration Required:
- Existing products with incorrect MultiBuy data need re-import
- Convert format from "name:qty:price;name:qty:price" to "value; price; stock|value; price; stock"
- Ensure base price is 0 for variant-based products
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Previous Approach (WRONG):**
- Created a separate table section to display ProductVariants
- User wanted data in the EXISTING collapsible panels, not a new section
**Proper Fix:**
- ProductImportService creates records in ProductVariants table
- Edit page's collapsible panels read from VariantsJson field (different system)
- Solution: Convert ProductVariants → VariantsJson format on page load
**Changes:**
1. **ProductsController.cs (Lines 105-115):**
- Load ProductVariants from database
- If VariantsJson empty but ProductVariants exist, convert them
- Format: `[{Weight: "28g", Price: 700, StockQty: 100}, ...]`
- JavaScript reads Price and StockQty to populate collapsible panel fields
2. **Edit.cshtml:**
- Removed the extra table section
- Existing collapsible panels now display imported data automatically
**Result:**
When you open Edit page, expand "Product Variants" → "Price, Stock & Weight Details",
the fields will be PRE-FILLED with your imported values (700, 100, etc.)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem:**
- Variant price overrides and stock quantities were hidden in collapsible
panels in the Product Edit page
- The Edit page was showing the VariantCollections system (VariantsJson)
instead of the actual ProductVariant records created by text import
- User had to expand each variant panel to see price and stock values
**Solution:**
1. **ProductsController.cs (Lines 101-103):**
- Added call to GetProductVariantsAsync() to load actual variant records
- Added ViewData["ProductVariants"] to pass data to view
2. **Edit.cshtml (Lines 320-413):**
- Added new collapsible section "Product Variants"
- Displays variants in a table with directly visible columns:
* Name, Type, Price, Stock Level, Sort Order, Status
- No hidden panels - all information visible at a glance
- Added quick summary with total variants, stock, and price range
- Includes helpful links to ProductVariants management page
**Technical Details:**
- Price displays in green with £ symbol when override exists
- Stock shows color-coded badges (green=in stock, red=out of stock)
- Section only appears if variants exist (conditional rendering)
- Expanded by default (aria-expanded="true") for immediate visibility
**Impact:**
- User can now see all variant prices and stock quantities immediately
- No need to click/expand individual variant panels
- Better UX for products imported via text import format
- Maintains separation between VariantCollections system and ProductVariants
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added detailed logging to ParseAndImportVariantLine to output:
- Variant name/value
- Price override
- Stock quantity
- Variant type
This will help diagnose why variant price and stock aren't showing up
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add replaceAll checkbox to ImportText view with warning
- Pass replaceAll parameter to import service
- Allows complete product catalog replacement via text import
- Enhanced documentation for unit types and variant collections
- Fix optimistic concurrency errors in product import by using ExecuteSqlRaw instead of EF tracking
- Add DeleteAllOrdersAndSalesDataAsync() method to clear orders, payments, customers, and messages
- Add DeleteAllSalesData endpoint to ProductsController for admin access
- Proper deletion order to avoid foreign key violations
- Enhanced logging for troubleshooting data cleanup operations
Resolves: Import errors with 'database operation expected to affect 1 row(s)'
- Added Replace All checkbox to import UI for clean slate imports
- Implemented DeleteAllProductsAndCategoriesAsync for complete data wipe
- Added auto-creation of categories during CSV import
- Created products_import.csv with 13 products across 4 categories
- Added comprehensive IMPORT_INSTRUCTIONS.md documentation
Technical changes:
- ProductImportService: Added replaceAll parameter to all import methods
- ProductImportService: Categories now auto-created if missing from CSV
- ProductsController: Added replaceAll parameter to Import action
- Import.cshtml: Added Replace All checkbox with danger warnings
Categories: Flour, Cereal, Vitamins, Herbal
Products: 13 products with full variant pricing structures
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Created NotificationHub for instant browser notifications
- Updated CryptoPaymentService to broadcast via SignalR
- Added JavaScript client with toast notifications
- Works with custom SSL certificates (no FCM dependency)
- Automatic reconnection with exponential backoff
- Notification sound and visual indicators
- Bypasses all Web Push SSL certificate issues
TeleBot Configuration:
- Added TeleBot API URL and API key to docker-compose.yml
- Configured to connect to telebot-service:5000 internally
- Enables customer notifications via Telegram bot
Expired Payment Handling:
- Auto-cancel orders when payment status is Expired
- Only cancels orders in PendingPayment status
- Logs cancellation for audit trail
Customer View Improvements:
- Hide cancelled orders from customer order lists
- Filters applied to both GetOrdersByIdentityAsync and GetOrdersByCustomerIdAsync
- Prevents confusion from displaying cancelled/expired orders
This resolves:
- No notifications to customers (TeleBot not configured)
- No notifications to admin (TeleBot connection failed)
- Expired orders remaining visible to customers
- Orders not auto-cancelled when payment expires
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Webhook Improvements:
- Added Confirmations field to PaymentWebhookDto (default: 0)
- Updated webhook controller to pass confirmations to service layer
- Fixed notification logic to match order update conditions
Payment Confirmation Logic:
- Paid (2): Confirmed immediately regardless of confirmations
- Overpaid (3): Confirmed immediately regardless of confirmations
- Completed (7): Requires 3+ blockchain confirmations
- Notifications only sent when order is actually updated
This prevents premature notifications for unconfirmed 'Completed' status
while maintaining immediate processing for 'Paid' and 'Overpaid' statuses.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added quick action button on Packing tab to dispatch orders
- Created dispatch modal with tracking number input
- Modal includes tracking number, estimated days, and notes fields
- Button appears on both desktop table and mobile card views
- Fixes workflow gap where Packing orders had no quick action
- Orders now properly flow: Accepted → Packing → Dispatched
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added PaymentStatus.Overpaid to notification trigger conditions
- Overpaid payments now update order status to PaymentReceived
- Overpaid payments now send admin push notifications
- Overpaid payments now send TeleBot customer notifications
- Resolves issue where successful overpayments were silent
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed Issues:
1. Removed "maskable" purpose from icons (was causing padding warnings)
2. Added screenshots with form_factor for richer install UI
- Desktop: form_factor: "wide"
- Mobile: form_factor: "narrow"
Changes:
- Icons now use "purpose: any" only (no maskable)
- Added screenshots array with wide/narrow form factors
- This enables richer PWA install prompts on supported browsers
All PWA manifest warnings should now be resolved.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problems Fixed:
1. Blank white screen on initial load (loading screen had display:none)
2. Only showed once (sessionStorage.blazorLoaded prevented repeat shows)
3. Fast connections meant users never saw it
Solution:
1. Removed display:none from HTML - screen visible immediately
2. Removed sessionStorage check - shows on every page load
3. Screen visible by default, hides when Blazor.start() completes
Behavior Now:
- Loading screen appears instantly (no blank white screen)
- Shows on every page load (full page refresh)
- Hides when SignalR connection established
- Works correctly with slow/throttled connections
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem:
- Loading screen was hiding immediately without waiting for Blazor
- Page detection logic was too restrictive (only /blazor paths)
- Most admin pages don't have Blazor components, so screen hid instantly
Solution:
- Blazor Server is loaded on ALL admin pages via _Layout.cshtml
- Removed restrictive path checking (was checking for /blazor or components)
- Now always calls Blazor.start() and waits for SignalR connection
- Loading screen properly shows while SignalR establishes connection
Expected behavior:
- First load: Screen shows → Blazor connects → Screen fades out
- Console: "Starting Blazor Server..." → "Started successfully" → "Hiding"
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**Problem**:
- GetAvailableCurrenciesAsync() was routing internal API calls through Tor
- Caused SOCKS connection failures: "SOCKS server failed to connect"
- Internal Docker network calls don't need Tor privacy layer
**Root Cause**:
- Line 617-618 used OR logic: LittleShop:UseTor OR Privacy:EnableTor
- Production config: LittleShop__UseTor=false, Privacy__EnableTor=true
- OR condition meant Tor was enabled for all API calls
**Solution**:
- Only check LittleShop:UseTor (explicitly for API calls)
- Privacy:EnableTor now only affects external Telegram API calls
- Internal calls (http://littleshop:5000) bypass Tor completely
**Benefits**:
- No more SOCKS connection errors
- Faster internal API responses (no Tor overhead)
- Tor still protects external Telegram communications
**File**: TeleBot/TeleBot/Services/LittleShopService.cs:619
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Ephemeral sessions weren't persisted after checkout
- Sessions ephemeral by default, only persisted during CheckoutFlow
- After checkout completes, state changes to MainMenu
- SavedAddress was lost because session wasn't saved
Fix implemented:
- Persist ephemeral sessions when SavedAddress is not null
- Also persist when cart is non-empty (preserve cart state)
- User's saved shipping address now available on next checkout
- Improves UX - no need to re-enter address every time
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Root cause: Orders created with CustomerInfo had NULL IdentityReference
- CancelOrderAsync checked order.IdentityReference != identityReference
- NULL != "telegram:12345:username" → always returned false
- User saw "already processed" error even for pending orders
Fix implemented:
- Include Customer entity in CancelOrderAsync query
- Extract Telegram user ID from identity reference format
- Match against Customer.TelegramUserId for modern orders
- Fallback to IdentityReference matching for legacy orders
- Enhanced logging to debug ownership/status issues
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added delete order functionality with identity verification
- OrderDetailsMenu now shows conditional buttons based on order/payment state
- Retry payment if no payments exist
- View payment details if payment pending
- Delete order option for all pending orders
- Full client SDK implementation for CancelOrderAsync
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: When clicking Buy button, product details message appeared at bottom
but Telegram didn't auto-scroll to show it, leaving users confused.
Solution: Use replyToMessageId parameter to thread product details as a reply
to the user's button click message. This:
- Creates visual thread connection
- Encourages Telegram to show the response prominently
- Makes conversation flow more natural
Also wrapped activity tracking in try-catch to prevent blocking.
Technical Changes:
- ProductCarouselService: Added replyToMessageId optional parameter
- SendPhotoAsync/SendTextMessageAsync: Use replyToMessageId with allowSendingWithoutReply
- HandleProductDetail: Pass message.MessageId to carousel service
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Error: MenuBuilder.PaymentCurrencyMenu does not exist
Fix: Changed to MenuBuilder.PaymentMethodMenu (correct method name)
TeleBot now compiles successfully.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Activity tracker DNS errors (littleshop-admin:8080) were blocking
checkout flow, preventing shipping address prompt from showing.
Solution: Wrap activity tracking in try-catch with warning log.
Checkout flow continues even if activity tracking fails.
This ensures users can complete checkout even if analytics are unavailable.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
1. Remove /help from main menu
- Removed "❓ Help" button from main menu
- Help still available via /help command
2. Fix support refresh clearing messages
- Changed from editing message to sending new message
- Preserves message history when refreshing
- Shows "No new messages" if no updates instead of clearing
- Better error handling with user-friendly alerts
3. Add retry payment button for failed payments
- New "🔄 Retry Payment" button when payment creation fails
- Shows order ID (friendly reference) in error message
- PaymentFailedMenu: Retry, View Basket, Main Menu options
- HandleRetryPayment: Re-shows currency selection
- Preserves order and allows payment retry without recreating order
Technical Changes:
- MenuBuilder: Added PaymentFailedMenu method
- CallbackHandler: Added retry_payment case and HandleRetryPayment method
- HandlePayment: Updated error messages to use PaymentFailedMenu
- HandleRefreshConversation: Sends new message instead of editing
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>