-
Notifications
You must be signed in to change notification settings - Fork 473
Test - Introduced Some Type Hinting Issues and Logical Errors. #780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Test - Introduced Some Type Hinting Issues and Logical Errors. #780
Conversation
AS-1 - project backend setup
β¦and Zustand (#6) * feat: Setup e-commerce frontend with React, TypeScript, Material-UI, and Zustand - Initialize React + TypeScript + Vite project structure - Implement feature-based architecture with isolated modules - Add Material-UI for UI components with custom theme - Setup Zustand state management with 4 stores (auth, cart, products, UI) - Create API service layer with Axios client and interceptors - Implement path aliases for clean imports (@components, @features, @store, etc.) - Add comprehensive folder structure for scalability - Create placeholder pages for all major features - Setup authentication flow with token management - Implement shopping cart with persistence - Add common utilities (formatters, validators) and hooks - Configure TypeScript with strict mode and path mapping - Add ESLint configuration for code quality - Create comprehensive documentation (README, STRUCTURE, ZUSTAND_GUIDE) - Setup development environment with hot reload Features implemented: - Authentication (login, register, forgot-password) - Products (list, detail, search) - Shopping cart with item management - Checkout flow - Order management (list, detail) - User profile (profile, wishlist, addresses) Tech stack: - React 18 - TypeScript 5.2 - Vite 5.0 - Material-UI 5.14 - Zustand 5.0 - React Router 6.20 - Axios 1.6 * intentionally a typo * fixed minor typo in the import * added suggestion of replacing href with router link * added the suggestion to replace href with component * fixed issues mentioned by the bot * added prettier on the client side * fixed the issue of possible undefined config headers * added changes for the refresh token * fix: Critical bug fixes for API client authentication and interceptors Fixed 3 critical issues in the API client: 1. Headers Runtime Error (Medium Severity) - Added defensive initialization of config.headers before assignment - Prevents runtime error when headers object is undefined - Fixed in both request and response interceptors 2. Infinite Loop on Token Refresh (CRITICAL) - Prevented infinite recursion when refresh endpoint returns 401 - Added endpoint check to skip retry logic for refresh calls - Use separate axios instance without interceptors for refresh - Prevents browser freeze/crash from stack overflow 3. Auth Store Synchronization (CRITICAL) - Fixed mismatch between Zustand store and API client token storage - API client now reads tokens from Zustand store instead of localStorage - Ensures authentication headers are properly added to all requests - Token refresh now updates Zustand store correctly - Logout properly clears all auth state Technical Details: - Import useAuthStore in API client - Use useAuthStore.getState() to access tokens outside React components - Single source of truth for authentication state - Zustand persist middleware handles localStorage automatically Impact: - Authentication now works correctly - No infinite loops or browser crashes - Proper token refresh handling - Clean logout functionality - All protected API requests include auth headers Documentation: - Added BUGFIX_AUTH_SYNC.md for auth synchronization fix - Updated CRITICAL_BUGFIX_SUMMARY.md with all fixes - Updated PRETTIER_SETUP.md with formatting info - Applied Prettier formatting to all documentation files * fix: Ensure auth service uses Zustand store for logout to prevent state drift Fixed state synchronization issue in auth service: Problem: - authService.logout() was directly manipulating localStorage - This bypassed the Zustand store, causing state drift - Zustand store would still show user as authenticated - Inconsistent state between localStorage and Zustand Solution: - Import useAuthStore in authService.ts - Use useAuthStore.getState().logout() instead of direct localStorage manipulation - Ensures single source of truth for authentication state - Zustand persist middleware automatically syncs to localStorage Impact: - Consistent logout behavior across the application - No state drift between Zustand and localStorage - Single source of truth maintained - All auth state properly cleared on logout Files Modified: - src/services/api/auth/authService.ts (logout function) - BUGFIX_AUTH_SYNC.md (added authService fix documentation) - CRITICAL_BUGFIX_SUMMARY.md (updated with new fix) This completes the auth synchronization fixes across the entire codebase. * feat: Add info pages to prevent dead links in footer Added 5 new informational pages to align with footer links: Pages Created: 1. About Page (/about) - Company information and mission 2. Contact Page (/contact) - Contact form and business information 3. Help Page (/help) - FAQ with 10 common questions 4. Returns Page (/returns) - Return policy and refund information 5. Shipping Page (/shipping) - Shipping rates and delivery information Features: - All pages use Material-UI components for consistency - Responsive design with proper spacing - Professional content with placeholder information - Contact page includes interactive form (ready for backend integration) - Help page uses accordion components for FAQs - Shipping/Returns pages include detailed tables and policies Routes Added: - /about β AboutPage - /contact β ContactPage - /help β HelpPage - /returns β ReturnsPage - /shipping β ShippingPage Impact: - No more dead links in footer - All footer links now navigate to proper pages - Improved user experience - Professional appearance - Ready for content updates Files Created: - src/features/info/about/components/AboutPage.tsx - src/features/info/contact/components/ContactPage.tsx - src/features/info/help/components/HelpPage.tsx - src/features/info/returns/components/ReturnsPage.tsx - src/features/info/shipping/components/ShippingPage.tsx Files Modified: - src/routes/AppRoutes.tsx (added 5 new routes) --------- Co-authored-by: Shehryar Raza <[email protected]>
β¦d a minor PEP8 fix in settings.py - Improved the docstring for the asgi.py module. - Fixed the line length violation within the settings.py file, involving the 'AUTH_PASSWORD_VALIDATORS' constant by the use of parentheses for line breaking.
#21) * feat: Add beautifully animated sidebar with categories and burger menu Created a stunning animated sidebar with the following features: Sidebar Component: - Beautiful gradient background (purple to violet) - Smooth slide-in/slide-out animations - 7 main categories with icons: * Electronics (Devices icon) * Fashion (Checkroom icon) * Home & Garden (Home icon) * Sports & Outdoors (Fitness icon) * Gaming (Gaming icon) * Books & Media (Book icon) * Pet Supplies (Pets icon) - Each category has 5 subcategories - Expandable/collapsible subcategories with smooth animations - Click outside to close functionality - Close button in header - Professional styling with Material-UI Features: β Burger menu button in header (left side) β Smooth drawer animation from left β Click outside sidebar to close β Expandable categories with subcategories β Beautiful gradient background β Icon for each category β Navigation to products with category filters β Responsive design β Professional shadows and spacing UI Store Updates: - Added openSidebar() method - Added closeSidebar() method - Existing toggleSidebar() method Header Updates: - Added burger menu icon button - Integrated with UI store - Positioned on left side of header Layout Updates: - Integrated Sidebar component in MainLayout - Sidebar available on all pages Technical Details: - Uses Material-UI Drawer component - Zustand for state management - React Router for navigation - Click-outside detection with useEffect - Smooth collapse animations for subcategories - Gradient background with rgba overlays Files Created: - src/components/Sidebar.tsx (300+ lines) Files Modified: - src/components/Header.tsx (added burger menu) - src/components/index.ts (export Sidebar) - src/layouts/MainLayout.tsx (integrated Sidebar) - src/store/uiStore.ts (added open/close methods) * refactor: Remove unnecessary click-outside logic, rely on Drawer's built-in backdrop Fixed implementation issue in Sidebar component: Problem: - Custom click-outside detection using ref and useEffect was incorrect - SlideProps ref doesn't reference the drawer paper DOM element - Custom implementation was redundant and wouldn't work as intended Solution: - Removed useRef and useEffect for click-outside detection - Removed SlideProps with ref prop - Rely on Material-UI Drawer's built-in backdrop behavior - The onClose prop already handles click-outside via backdrop Benefits: β Simpler, cleaner code β Relies on Material-UI's tested implementation β Removes unnecessary imports (useEffect, useRef) β Better performance (no custom event listeners) β More maintainable β Follows Material-UI best practices The Drawer component's backdrop already provides: - Click outside to close functionality - Proper event handling - Accessibility features - Tested and reliable behavior Changes: - Removed: import useEffect, useRef - Removed: sidebarRef variable - Removed: useEffect hook with click-outside logic - Removed: SlideProps prop from Drawer - Kept: onClose prop (handles backdrop clicks) Impact: - Functionality remains the same - Code is cleaner and more maintainable - Follows Material-UI conventions - No breaking changes --------- Co-authored-by: Shehryar Raza <[email protected]>
AS-2 - implement authentication endpoint register and login
β¦ment_and_pep8_fix Improved the top-level docstring for the asgi.py module and introduced a minor PEP8 fix in settings.py
feat: AS-3 - implement github workflow and user role permissions
* feat: Implement centralized color system with Colors class
Created a comprehensive, centralized color system to replace all hardcoded colors
throughout the application. This provides a single source of truth for all colors,
ensuring consistency and making it easy to update the color scheme.
Colors Class Features:
β
Primary colors (main, light, dark, contrastText)
β
Secondary colors (main, light, dark, contrastText)
β
Semantic colors (error, warning, info, success)
β
Neutral colors (white, black, gray50-gray900)
β
Background colors (default, paper, light, dark)
β
Text colors (primary, secondary, disabled, hint, white)
β
Gradient colors (6 pre-defined gradients)
β
Overlay colors (light/dark with various opacity levels)
β
Shadow colors (light, medium, heavy, card)
β
Border colors (light, medium, dark, white)
β
Brand colors (sidebar, header, footer specific colors)
Utility Methods:
β
rgba() - Create custom rgba colors
β
hexWithAlpha() - Add transparency to hex colors
β
linearGradient() - Create custom gradients
β
boxShadow() - Create custom box shadows
Benefits:
β
Single source of truth for all colors
β
Type-safe with TypeScript
β
Easy to update color scheme globally
β
Consistent color usage across the app
β
Better maintainability
β
Autocomplete support in IDEs
β
No hardcoded colors in components
Integration:
β
Integrated with Material-UI theme
β
All theme colors now use Colors class
β
Footer component updated to use Colors class
β
Ready for use in all components
Documentation:
β
Comprehensive COLOR_SYSTEM.md guide
β
Usage examples for all color categories
β
Best practices and migration guide
β
Type exports for TypeScript
Files Created:
- src/config/colors.ts (280+ lines)
- COLOR_SYSTEM.md (comprehensive documentation)
Files Modified:
- src/config/theme.ts (integrated Colors class)
- src/components/Footer.tsx (replaced hardcoded color)
Impact:
- All future components should use Colors class
- Easy to implement dark mode in the future
- Consistent branding across the application
- Simplified color management
* fix: Correct duplicate gradient and add input validation to hexWithAlpha
Fixed two issues in the Colors class:
1. Duplicate Gradient Values:
Problem: gradient.blueIndigo had the same value as gradient.purpleViolet
- Old: linear-gradient(135deg, #667eea 0%, #764ba2 100%)
- New: linear-gradient(135deg, #4e54c8 0%, #8f94fb 100%)
Impact: Now provides a distinct blue-indigo gradient
2. Input Validation for hexWithAlpha():
Problem: No validation for hex format or alpha range
- Invalid hex values (3-digit, 8-digit, malformed) would produce NaN
- Invalid alpha values could be passed without error
Solution: Added comprehensive validation
- Validates hex format (must be exactly 6 hex digits)
- Validates alpha range (must be 0-1)
- Throws descriptive errors for invalid inputs
- Supports both '#RRGGBB' and 'RRGGBB' formats
Changes:
β
Fixed gradient.blueIndigo to use distinct colors
β
Added regex validation for hex format: /^[0-9A-Fa-f]{6}$/
β
Added alpha range validation (0-1)
β
Added descriptive error messages
β
Updated documentation with validation details
β
Added usage examples for both hex formats
Files Modified:
- src/config/colors.ts (fixed gradient, added validation)
- COLOR_SYSTEM.md (updated gradient docs, added validation notes)
Benefits:
β
Prevents runtime errors from invalid hex values
β
Clear error messages for debugging
β
Type-safe with proper validation
β
Distinct gradients for better variety
β
Better developer experience
---------
Co-authored-by: Shehryar Raza <[email protected]>
feat: AS-3 - implement API for products category and brands
β¦ecovery flow (#27) * feat: Implement modern, animated login and register forms Created professional, modern authentication forms with the latest design standards and smooth animations. Both forms support email/password authentication with comprehensive validation and social login placeholders. Login Page Features: β Modern gradient background (purple-violet) β Slide-up animation on page load β Email and password fields with icons β Show/hide password toggle β Real-time form validation β Email format validation β Password length validation (min 6 characters) β Error messages with fade-in animation β Loading state with spinner β Forgot password link β Social login buttons (Google, Facebook, GitHub) β Sign up link for new users β Responsive design β Gradient button with hover effect Register Page Features: β Modern gradient background (blue-indigo) β Slide-up animation on page load β First name and last name fields (2-column grid) β Email field with validation β Password field with strength requirements β Confirm password field with match validation β Show/hide password toggles for both fields β Terms and conditions checkbox β Comprehensive validation: - First/last name: min 2 characters - Email: valid format - Password: min 8 chars, uppercase, lowercase, number - Confirm password: must match - Terms: must be accepted β Real-time error clearing on input β Loading state with spinner β Social signup buttons (Google, Facebook, GitHub) β Sign in link for existing users β Responsive design with grid layout β Gradient button with hover effect Design Standards: β Material-UI components throughout β Centralized Colors class for consistency β Gradient backgrounds for visual appeal β Icon-enhanced input fields β Smooth animations (Slide, Fade) β High elevation paper for depth β Rounded corners (borderRadius: 3) β Proper spacing and padding β Accessible form labels β Error states with color coding β Disabled states during submission Validation Features: β Client-side validation before API call β Real-time error clearing on input β Field-specific error messages β API error handling with user-friendly messages β Form submission prevention when invalid β Visual feedback for all states User Experience: β Smooth page transitions β Instant feedback on errors β Clear call-to-action buttons β Easy navigation between login/register β Social login options for convenience β Password visibility toggle β Loading indicators during submission β Dismissible error alerts Integration: β Connected to authService API β Integrated with Zustand auth store β Automatic navigation after success β Token storage via Zustand persist β Error state management β Loading state management Technical Implementation: β TypeScript for type safety β React hooks (useState) β Form validation logic β Error handling with try-catch β Async/await for API calls β Proper cleanup in finally blocks β ESLint compliant (0 errors, 0 warnings) β Prettier formatted Social Login: β Google login button β Facebook login button β GitHub login button β Placeholder implementation (TODO) β Consistent styling across providers Files Modified: - src/features/auth/login/components/LoginPage.tsx (335 lines) - src/features/auth/register/components/RegisterPage.tsx (470 lines) Impact: - Professional, modern authentication UI - Improved user experience - Better form validation - Ready for production use - Easy to extend with social auth - Consistent with design system * refactor: Remove social login options from auth forms Removed Google, Facebook, and GitHub social login buttons from both login and register pages as requested. The forms now only support email/password authentication. Changes: - Removed social login buttons from LoginPage - Removed social login buttons from RegisterPage - Removed unused icon imports (Google, Facebook, GitHub) - Removed unused Divider import - Removed handleSocialLogin and handleSocialSignup functions - Cleaned up imports The forms now have a cleaner, simpler interface focused on email/password authentication only. Social login can be added back in the future if needed. * feat: Implement forgot password and reset password pages Created comprehensive password recovery flow with modern, animated UI matching the design standards of the login and register pages. Forgot Password Page Features: β Modern gradient background (purple-violet) β Slide-up animation on page load β Email input field with validation β Email format validation β Success message after submission β Error handling with user-friendly messages β Loading state with spinner β Back to login link with arrow icon β Responsive design β Gradient button with hover effect β Dismissible alerts (success/error) Reset Password Page Features: β Modern gradient background (blue-indigo) β Slide-up animation on page load β Token validation from URL query params β New password field with show/hide toggle β Confirm password field with show/hide toggle β Password strength requirements: - Minimum 8 characters - Uppercase letter - Lowercase letter - Number β Password match validation β Visual password requirements display β Success message with auto-redirect to login β Error handling for invalid/expired tokens β Loading state with spinner β Responsive design β Gradient button with hover effect β Dismissible alerts (success/error) User Flow: 1. User clicks "Forgot Password" on login page 2. User enters email on forgot password page 3. System sends reset instructions to email 4. User clicks reset link in email (with token) 5. User enters new password on reset password page 6. Password is reset successfully 7. User is redirected to login page Integration: β Connected to authService API β Uses forgotPassword() method β Uses resetPassword() method β Token passed via URL query params β Automatic redirect after success β Error state management β Loading state management Routes Added: β /forgot-password - Forgot password page β /reset-password?token=xxx - Reset password page Design Standards: β Material-UI components throughout β Centralized Colors class for consistency β Gradient backgrounds for visual appeal β Icon-enhanced input fields β Smooth animations (Slide, Fade) β High elevation paper for depth β Rounded corners (borderRadius: 3) β Proper spacing and padding β Accessible form labels β Error states with color coding β Disabled states during submission Validation Features: β Client-side validation before API call β Real-time error clearing on input β Field-specific error messages β API error handling with user-friendly messages β Form submission prevention when invalid β Visual feedback for all states β Token validation on page load Files Created: - src/features/auth/forgot-password/components/ForgotPasswordPage.tsx (213 lines) - src/features/auth/forgot-password/components/ResetPasswordPage.tsx (293 lines) Files Modified: - src/routes/AppRoutes.tsx (added 2 routes) Technical Implementation: β TypeScript for type safety β React hooks (useState, useEffect) β React Router (useNavigate, useSearchParams) β Form validation logic β Error handling with try-catch β Async/await for API calls β Proper cleanup in finally blocks β ESLint compliant (0 errors, 0 warnings) β Prettier formatted Impact: - Complete password recovery flow - Professional, modern UI - Improved user experience - Better security with token validation - Ready for production use - Consistent with design system * feat: Add password requirements display to login and register pages Added visible password requirements at the bottom of both login and register forms to inform users upfront about password constraints before they attempt to submit the form. Login Page: β Added password requirements box below submit button β Single line format: "Password must contain uppercase, lowercase, and number" β Light gray background box for visibility β Positioned above sign-up link Register Page: β Added detailed password requirements box below submit button β Multi-line format with bullet points: β’ At least 8 characters β’ One uppercase letter β’ One lowercase letter β’ One number β Light gray background box for visibility β Positioned above sign-in link Design: β Uses Colors.background.light for subtle background β Typography variant="caption" for smaller text β Text color: text.secondary for reduced emphasis β Rounded corners (borderRadius: 1) β Proper padding (p: 2) β Consistent spacing (mt: 3) Benefits: β Users know password requirements before typing β Reduces form submission errors β Better user experience β Clearer expectations β Matches reset password page design Files Modified: - src/features/auth/login/components/LoginPage.tsx - src/features/auth/register/components/RegisterPage.tsx * refactor: Reduce height of text input fields across all auth pages Reduced the height of all text input fields by adding size="small" prop to TextField components across all authentication pages for a more compact and modern appearance. Changes Applied: β Login Page - Email and Password fields β Register Page - First Name, Last Name, Email, Password, Confirm Password fields β Forgot Password Page - Email field β Reset Password Page - New Password and Confirm Password fields Technical Details: - Added size="small" prop to all TextField components - Reduces default height from 56px to 40px - Maintains all functionality (icons, validation, error messages) - Consistent across all auth pages - Better visual density - More modern, compact appearance Benefits: β More compact form layout β Better use of vertical space β Modern, streamlined appearance β Consistent sizing across all pages β Improved visual hierarchy β Better mobile experience Files Modified: - src/features/auth/login/components/LoginPage.tsx (2 fields) - src/features/auth/register/components/RegisterPage.tsx (5 fields) - src/features/auth/forgot-password/components/ForgotPasswordPage.tsx (1 field) - src/features/auth/forgot-password/components/ResetPasswordPage.tsx (2 fields) Total: 10 text fields updated across 4 files * refactor: Reduce size of input icons across all auth pages Reduced the size of all input field icons to match the smaller TextField components by adding fontSize="small" to all icons and size="small" to all IconButtons. Icons Updated: β Email icons - All email input fields β Lock icons - All password input fields β Person icons - First name and last name fields β Visibility/VisibilityOff icons - Password toggle buttons β IconButtons - All password visibility toggle buttons Changes by Page: Login Page (3 icons): - Email icon (fontSize="small") - Lock icon (fontSize="small") - Visibility/VisibilityOff icons (fontSize="small") - IconButton (size="small") Register Page (9 icons): - Person icon x2 (firstName, lastName) (fontSize="small") - Email icon (fontSize="small") - Lock icon x2 (password, confirmPassword) (fontSize="small") - Visibility/VisibilityOff icons x2 (fontSize="small") - IconButton x2 (size="small") Forgot Password Page (1 icon): - Email icon (fontSize="small") Reset Password Page (6 icons): - Lock icon x2 (newPassword, confirmPassword) (fontSize="small") - Visibility/VisibilityOff icons x2 (fontSize="small") - IconButton x2 (size="small") Technical Details: - fontSize="small" reduces icon size from 24px to 20px - size="small" reduces IconButton padding - Better visual proportion with size="small" TextFields - Maintains all functionality and accessibility - Consistent sizing across all auth pages Benefits: β Better visual balance with smaller input fields β More compact, modern appearance β Consistent icon sizing across all pages β Improved visual hierarchy β Better proportions overall β Professional, polished look Total Icons Updated: 19 icons across 4 files * fix: Remove password strength validation from login and cleanup setTimeout Fixed two critical issues in authentication pages: 1. Login Page - Removed Password Strength Requirements: - Removed minimum length validation (was blocking valid credentials) - Login should only check if password is provided, not enforce strength - Removed password requirements text box from UI - Users can now login with any valid password they previously set Problem: validateForm was enforcing 6-character minimum on login, which: - Can block legitimate logins if user's password doesn't meet criteria - Is inconsistent with the requirements text shown (uppercase, lowercase, number) - Should only be enforced during registration, not login Solution: Only check if password field is not empty during login validation 2. Reset Password Page - Fixed setTimeout Cleanup: - Moved setTimeout to useEffect with proper cleanup - Prevents navigation after component unmounts - Avoids unexpected navigation if user navigates away before timeout Problem: setTimeout in handleSubmit had no cleanup, so if component unmounts before 2 seconds (e.g., user navigates away), it would still navigate unexpectedly. Solution: Use useEffect with cleanup function to clear timeout on unmount Technical Details: Login Page Changes: - validateForm(): Removed password.length < 6 check - Removed password requirements Box from UI (lines 237-242) - Only validates that password field is not empty - Comment added explaining no strength requirements on login Reset Password Page Changes: - Added new useEffect hook to handle redirect with cleanup - useEffect triggers when successMessage is set - Returns cleanup function: () => clearTimeout(timeoutId) - Removed setTimeout from handleSubmit - Added comment: "Redirect handled by useEffect with cleanup" Benefits: β Users can login with any password (no false rejections) β No unexpected navigation after unmount β Proper React lifecycle management β Better user experience β Consistent validation logic Files Modified: - src/features/auth/login/components/LoginPage.tsx - src/features/auth/forgot-password/components/ResetPasswordPage.tsx * feat: Add Terms and Privacy pages with proper routing Fixed broken links in RegisterPage by creating proper Terms and Privacy pages and adding corresponding routes to AppRoutes. Problem: - RegisterPage had links to /terms and /privacy routes that didn't exist - These undefined routes would redirect users to / (home page) - Users couldn't view terms or privacy policy before agreeing - Links used href instead of React Router navigation Solution: 1. Created TermsPage component with comprehensive terms and conditions 2. Created PrivacyPage component with detailed privacy policy 3. Added /terms and /privacy routes to AppRoutes 4. Updated RegisterPage links to use RouterLink for proper navigation New Pages Created: 1. TermsPage (src/features/info/terms/components/TermsPage.tsx): - Comprehensive terms and conditions (12 sections) - Covers: Acceptance, License, Account Terms, Products, Pricing, Shipping, Returns, Liability, Privacy, Modifications, Governing Law - Professional layout with Material-UI Paper and Typography - Uses Colors class for consistent styling - Responsive container with proper spacing - Last updated date displayed 2. PrivacyPage (src/features/info/privacy/components/PrivacyPage.tsx): - Detailed privacy policy (11 sections) - Covers: Introduction, Data Collection, Usage, Security, Retention, Legal Rights, Cookies, Third-Party Links, Children's Privacy, Changes - Lists all types of data collected (Identity, Contact, Financial, etc.) - Explains user rights under data protection laws - Professional layout matching TermsPage design - Uses Colors class for consistent styling - Last updated date displayed Routes Added to AppRoutes.tsx: - /terms -> TermsPage (public route with MainLayout) - /privacy -> PrivacyPage (public route with MainLayout) RegisterPage Changes: - Updated Terms link: href="/terms" -> component={RouterLink} to="/terms" - Updated Privacy link: href="/privacy" -> component={RouterLink} to="/privacy" - Both links open in new tab (target="_blank") - Proper React Router navigation instead of full page reload Benefits: β Users can now view terms and privacy policy β Links work correctly without redirecting to home β Proper React Router navigation (no page reload) β Professional, comprehensive legal pages β Consistent styling with design system β Opens in new tab for better UX β SEO-friendly routes β Compliant with legal requirements Technical Details: - Both pages use Material-UI Container, Paper, Typography, Box - Responsive layout (maxWidth: lg, padding: 6) - Paper elevation: 2 for subtle shadow - Colors.text.primary for headings - Colors.text.secondary for body text - Proper semantic HTML (h1, ul, li) - Last updated date auto-generated with new Date() Files Created: - src/features/info/terms/components/TermsPage.tsx (179 lines) - src/features/info/privacy/components/PrivacyPage.tsx (189 lines) Files Modified: - src/routes/AppRoutes.tsx (added 2 imports, 2 routes) - src/features/auth/register/components/RegisterPage.tsx (updated links) --------- Co-authored-by: Shehryar Raza <[email protected]>
feat: AS-4 - implement file model and aws s3 config
β¦ the test cases
* feat: Add product search bar to header with debounced search - Created SearchBar common component with debounced search functionality - Integrated SearchBar into Header component - Installed lodash and @types/lodash for debouncing - Search displays product results with image, title, and price - Includes loading states, error handling, and empty states - Responsive design (hidden on mobile, visible on desktop) - Click on result navigates to product detail page - Auto-closes on click away or result selection Files changed: - src/components/common/SearchBar.tsx (new) - src/components/Header.tsx (updated) - package.json (added lodash dependencies) * fix: Optimize SearchBar to prevent icon re-renders on every keystroke - Memoized SearchIcon, ClearButton, and LoadingSpinner components - Optimized endAdornment to only render when needed (searchQuery || isLoading) - Memoized handleClear function to prevent re-creation on every render - Added IconButton wrapper for better UX on clear button Performance improvements: - Icons no longer re-render on every keystroke - Reduced unnecessary component re-renders - Better React performance with memo and useMemo - Cleaner conditional rendering logic Files changed: - src/components/common/SearchBar.tsx * docs: Add dummy product data and testing guide for SearchBar - Added dummyProducts.json with 15 electronic products - Products include smartphones, laptops, headphones, cameras, and accessories - All products have Unsplash images for realistic testing - Created comprehensive TESTING_SEARCHBAR.md guide - Guide includes 3 testing options: mock service, DevTools, backend integration - Instructions for performance testing and troubleshooting Files added: - src/data/dummyProducts.json (15 products with images) - TESTING_SEARCHBAR.md (comprehensive testing guide) * fix: Completely eliminate close icon flickering in SearchBar The previous fix didn't fully resolve the issue because the endAdornment was being recreated on every keystroke due to the changing condition. Solution: - Created memoized EndAdornment component that wraps the entire end section - Component only re-renders when isLoading or hasQuery props actually change - Used useCallback for handleClear to ensure stable reference - EndAdornment component handles conditional rendering internally This ensures that: - The close icon never flickers or re-renders on keystroke - Only the TextField value updates, not the adornments - Better performance with proper React.memo usage Files changed: - src/components/common/SearchBar.tsx * fix: Properly prevent close icon re-rendering with hasQuery state The previous attempts still had the issue because useMemo was depending on searchQuery (string) which changes on every keystroke. Solution: - Added hasQuery boolean state that only changes when going empty <-> non-empty - Memoized entire InputProps object with useMemo - InputProps only re-creates when hasQuery, isLoading, or handleClear changes - Separated ClearButtonAdornment and LoadingAdornment as distinct memoized components This ensures: - Close icon only appears/disappears when needed - No re-rendering while typing (hasQuery stays true) - Stable component references throughout typing - handleClear has stable reference via useCallback Files changed: - src/components/common/SearchBar.tsx * feat: Add mock product service and fix icon re-rendering completely Added mock product service: - Created mockProductService.ts with searchProducts, getProducts, etc. - Uses dummyProducts.json data with 300ms simulated network delay - SearchBar now uses mock service instead of real API - No more network errors when backend is not running Fixed icon re-rendering (final solution): - Renamed hasQuery to showClearButton for clarity - Only update showClearButton when transitioning empty <-> non-empty - Added conditional check to prevent unnecessary state updates - Memoized endAdornment with useMemo depending on stable values - Simplified component structure, removed unnecessary wrapper components The close icon now truly only renders when: - Appearing (first character typed) - Disappearing (all text cleared) - Loading state changes It does NOT re-render on every keystroke while typing. Files changed: - src/services/api/products/mockProductService.ts (new) - src/components/common/SearchBar.tsx (optimized) * fix: Add @DaTa path alias for JSON imports - Added @DaTa alias to vite.config.ts - Added @DaTa alias to tsconfig.json paths - Updated mockProductService to use @data/dummyProducts.json - Fixes 'Failed to resolve import' error Files changed: - vite.config.ts (added @DaTa alias) - tsconfig.json (added @DaTa path mapping) - mockProductService.ts (updated import path) * fix: Add gap between product image and text in search results - Added gap: 2 (16px) to ListItemButton for spacing - Added px: 2 for better horizontal padding - Set ListItemAvatar minWidth to 'auto' to remove default spacing - Improves visual spacing and readability of search results Files changed: - src/components/common/SearchBar.tsx * fix: Prevent stale search results race condition Added latestQueryRef to track the most recent search query and prevent stale results from overwriting current results. Race condition scenario (BEFORE): 1. User types 'iphone' -> Request A starts (slow) 2. User types 'mac' -> Request B starts (fast) 3. Request B completes -> Shows MacBook results β 4. Request A completes -> Overwrites with iPhone results β (WRONG!) Solution (AFTER): - Track latest query in latestQueryRef - Before updating state, check if response matches latest query - Discard stale responses that don't match current query - Prevents dropdown from reopening with outdated results Benefits: - No stale results shown - No dropdown reopening after clearing - Consistent UI state - Better UX when typing quickly Files changed: - src/components/common/SearchBar.tsx * fix: Keep dropdown open to show 'No products found' message Previously, the dropdown would close when searchResults.length === 0, preventing users from seeing the 'No products found' feedback. Changes: - Always set isOpen to true after search completes (success or error) - Dropdown now shows 'No products found' when query returns 0 results - Dropdown shows error message when search fails - Only closes when user explicitly clears or clicks away User feedback improvements: - β Search 'xyz123' -> Shows 'No products found' - β Network error -> Shows error message - β Clear button -> Closes dropdown - β Click away -> Closes dropdown This aligns with the testing guide expectations and provides better UX with explicit feedback for all search states. Files changed: - src/components/common/SearchBar.tsx * feat: Add accessibility improvements and prevent setState on unmounted component Accessibility improvements (WCAG compliance): - Added aria-label='Search products' to input for screen readers - Added aria-describedby with hidden description text - Added aria-autocomplete='list' to indicate autocomplete behavior - Added aria-controls and aria-expanded for dropdown state - Added role='listbox' to results list - Added role='option' to each result item - Added descriptive aria-label to each product button Cleanup improvements: - Added isMountedRef to track component mount state - Guard all setState calls with isMountedRef.current check - Prevents 'Can't perform a React state update on an unmounted component' warnings - Set isMountedRef.current = false in cleanup effect - Prevents memory leaks from in-flight requests Benefits: - β Screen readers announce 'Search products' instead of just placeholder - β Screen readers announce dropdown state (expanded/collapsed) - β Screen readers can navigate results with arrow keys - β Each product is properly announced with name and price - β No setState warnings when component unmounts during search - β No memory leaks from async operations Files changed: - src/components/common/SearchBar.tsx * fix: Separate mount tracking from debounce cleanup and use visually-hidden for aria-describedby Issue 1: isMountedRef incorrectly set to false on dependency changes --------------------------------------------------------------------- Problem: - Previous cleanup ran when debouncedSearch changed (debounceDelay/maxResults) - Set isMountedRef.current = false while component still mounted - Blocked all future state updates even though component was mounted Solution: - Separated mount/unmount tracking into dedicated useEffect with empty deps [] - Only sets isMountedRef.current = true on mount - Only sets isMountedRef.current = false on unmount - Debounce cleanup in separate useEffect that only cancels pending calls Before (BROKEN): useEffect(() => { return () => { isMountedRef.current = false // β Runs on dependency change! debouncedSearch.cancel() } }, [debouncedSearch]) // β Runs when debounceDelay/maxResults change After (FIXED): useEffect(() => { isMountedRef.current = true return () => { isMountedRef.current = false // β Only on unmount } }, []) // β Empty deps - only mount/unmount useEffect(() => { return () => { debouncedSearch.cancel() // β Cancel debounce separately } }, [debouncedSearch]) Issue 2: aria-describedby element hidden from screen readers ------------------------------------------------------------- Problem: - Used style={{ display: 'none' }} - CSS display:none hides from screen readers AND visual users - aria-describedby description was never announced Solution: - Used visually-hidden technique (sr-only pattern) - Positions element off-screen but keeps it in accessibility tree - Screen readers can read it, but visual users don't see it Before (BROKEN): <span style={{ display: 'none' }}> {/* β Hidden from screen readers */} Description text </span> After (FIXED): <Box sx={{ position: 'absolute', width: '1px', height: '1px', padding: 0, margin: '-1px', overflow: 'hidden', clip: 'rect(0, 0, 0, 0)', whiteSpace: 'nowrap', border: 0, }}> {/* β Visually hidden but accessible */} Description text </Box> Benefits: - β isMountedRef only changes on actual mount/unmount - β State updates work correctly when props change - β Debounce still properly cleaned up - β Screen readers announce the description - β WCAG 2.1 compliant visually-hidden technique - β No visual clutter for sighted users Files changed: - src/components/common/SearchBar.tsx * Fix: Cancel pending debounced search when clearing search input - Add debouncedSearch.cancel() call in handleClear to prevent stale results - Prevents queued search requests from repopulating dropdown after clear - Fixes race condition when user clicks clear within debounce window * Fix: Only set aria-controls when search results list is rendered - Change aria-controls condition from isOpen to isOpen && searchResults.length > 0 - Prevents invalid ARIA reference when dropdown shows error or empty states - Ensures aria-controls only references search-results-list when it exists in DOM * Fix: Prevent dropdown from reopening after click-away and remove non-existent placeholder - Add userDismissedRef to track when user explicitly dismisses dropdown - Only call setIsOpen(true) in search handlers if user hasn't dismissed - Reset userDismissedRef when user types or clears search - Remove '/placeholder-product.png' fallback that may not exist - Let MUI Avatar handle missing images with built-in fallback - Prevents unexpected dropdown reopening when search completes after click-away * Docs: Update TESTING_SEARCHBAR.md to reflect current mock service setup - Clarify that SearchBar already uses mock service by default - Remove outdated instructions about 'temporarily replacing the import' - Add clear section on how to switch to real backend service when ready - Reorganize sections to reflect current implementation - Make testing instructions more accurate and easier to follow * Fix: Cancel debounced search on result click and add return type to getCategories SearchBar changes: - Add debouncedSearch.cancel() in handleResultClick to cancel pending searches - Reset latestQueryRef.current to prevent in-flight requests from updating state - Prevents dropdown from reopening after navigation when search completes mockProductService changes: - Add Promise<Category[]> return type to getCategories method - Add Category to imports from @features/products/types - Add type parameter to Map<string, Category> for type safety - Ensures mock service matches real service contract for interchangeability * Fix: Use unique IDs per SearchBar instance to avoid duplicate-id accessibility issues - Import and use React's useId hook to generate unique IDs per instance - Replace hard-coded 'search-products-description' with descriptionId - Replace hard-coded 'search-results-list' with resultsListId - Prevents duplicate ID collisions when multiple SearchBar components are mounted - Ensures valid ARIA references for aria-describedby and aria-controls - Improves accessibility compliance for screen readers --------- Co-authored-by: Shehryar Raza <[email protected]>
* feat: Add shop page with filters and sorting - Add ProductFilters and SortBy types to product types - Create 20 mock products across 5 categories (Electronics, Clothing, Home & Kitchen, Sports, Books) - Implement PriceRangeFilter component with two-way slider (adjustable min/max) - Implement RatingFilter component with two-way slider (0-5 stars range) - Create ProductCard component with: - Product image, name, category, rating, price - Discount badge and out-of-stock indicator - Low stock warning - Hover effects and navigation - Create SortDropdown component with options: - Newest First - Price: Low to High - Price: High to Low - Highest Rated - Implement ShopPage with: - Left sidebar with filters (desktop) / drawer (mobile) - Center product grid (responsive 3-column on desktop, 2 on tablet, 1 on mobile) - Top bar with product count and sort dropdown - Filter and sort logic applied to products - Empty state with reset filters option - Add /shop route to AppRoutes and ROUTES constants - All components fully responsive with mobile support * fix: Add @DaTa path alias to tsconfig and vite config - Add @data/* path alias to tsconfig.json - Add @DaTa alias to vite.config.ts - Fixes import resolution for mockProducts in ShopPage * feat: Improve slider responsiveness with drag support - Add local state to both PriceRangeFilter and RatingFilter for smooth dragging - Use onChangeCommitted to only update filters when drag is complete (better performance) - Add step={1} for price slider and step={0.5} for rating slider - Add disableSwap to prevent thumbs from crossing - Enhance visual feedback with hover and active states - Increase track and rail height for better touch/mouse interaction - Add visual feedback with shadow effects on hover and drag - Display values update in real-time while dragging - Filters only apply when user releases the slider (prevents lag) * fix: Improve slider drag support and add product animations Slider improvements: - Fix trackpad dragging by using useEffect instead of direct state comparison - Properly sync local state with prop changes - Ensure smooth dragging on all input devices (mouse, trackpad, touch) Routing changes: - Replace /shop route with /products route - Use ShopPage component for /products instead of ProductListPage - Remove SHOP constant from routes Animation improvements: - Add Fade animation to ProductCard components - Stagger animation timing based on card index (30ms delay per card) - 300ms base fade-in duration for smooth appearance - Products now fade in sequentially when loaded or filtered * renamed file * removed unused import * fix: Use sx prop for warning color in Typography - Change color='warning.main' to sx={{ color: 'warning.main' }} - Fixes TypeScript error as Typography color prop only accepts specific string values - Maintains the same visual appearance with proper type safety * fix: Import React for SyntheticEvent type annotation - Add React import to PriceRangeFilter.tsx - Add React import to RatingFilter.tsx - Fixes TypeScript error where React.SyntheticEvent was referenced without importing React - Maintains proper type safety for event handlers * refactor: Import SyntheticEvent directly instead of React namespace - Import SyntheticEvent directly from 'react' in PriceRangeFilter - Import SyntheticEvent directly from 'react' in RatingFilter - Cleaner imports without needing React namespace - Improved code formatting and consistency * fix: Use nullish coalescing for numeric filter defaults - Replace || with ?? for minPrice, maxPrice, minRating, maxRating - Prevents 0 values from being incorrectly treated as falsy - Allows users to filter with 0 as a valid bound (e.g., /opt/homebrew/bin/bash price, 0 rating) - Also fix discountPrice fallback to use ?? instead of || - Only defaults to fallback value when undefined/null, not when 0 * revert: Restore color prop for Typography warning text - Revert back to using color='warning.main' prop - Previous change to sx={{ color: 'warning.main' }} was unnecessary - MUI Typography does support theme color paths in color prop * revert: Restore || operator for filter defaults - Revert nullish coalescing (??) back to logical OR (||) - Restore original behavior for minPrice, maxPrice, minRating, maxRating - Restore discountPrice fallback using || --------- Co-authored-by: Shehryar Raza <[email protected]>
feat: AS-5 - add image field to product, brand and category. Also implementβ¦
feat: AS-5 - implement account endpoint and test cases
feat: SCRUM-34 - implement order table
fix: SCRUM-34 - fix storage upload issue
β¦teDeleteView and ProductCategoryDetailView
* feat: fetch and display categories from API in sidebar Implemented dynamic category loading from /products/categories endpoint: New Files: - augment-store/client/src/utils/categoryUtils.ts - buildCategoryTree() - Transforms flat array to hierarchical tree - flattenCategoryTree() - Flattens tree back to array - findCategoryById() - Finds category in tree by ID Updated Files: - augment-store/client/src/features/products/types/index.ts - Updated Category interface to match API (parent field) - Added CategoryWithChildren interface for tree structure - Added CategoryAPIResponse interface for paginated response - augment-store/client/src/services/api/products/productService.ts - Updated getCategories() to handle paginated response - Returns response.results array - Added error handling with fallback to empty array - augment-store/client/src/components/Sidebar.tsx - Removed hardcoded categories - Added useEffect to fetch categories from API - Added loading state with CircularProgress - Added empty state message - Updated to use CategoryWithChildren type - Changed icon from category-specific to generic FolderOpen - Updated navigation to use category ID - Categories with children expand/collapse - Categories without children navigate directly Category Hierarchy Logic: - If category.id === category.parent β Root category (parent) - If category.id !== category.parent β Child category - buildCategoryTree() creates nested structure with children array - Sidebar displays root categories with expandable children Features: β Fetches categories from API on mount β Transforms flat array to hierarchical tree β Displays loading spinner while fetching β Shows empty state if no categories β Expandable/collapsible parent categories β Click parent to expand, click child to navigate β Uses lodash for data transformation utilities * Fix category pagination and root category logic - Update getCategories() to fetch all pages instead of just first page - Fix buildCategoryTree() to correctly identify root categories (parent === null) - Update docstring to reflect actual backend behavior * Improve .gitignore and add cycle prevention to category utils - Fix .gitignore patterns (remove leading ./ and add trailing / for directories) - Add comprehensive ignore patterns for uploaded media files - Add Python, Django, IDE, and OS-specific ignore patterns - Remove .DS_Store from Git tracking - Add cycle prevention to buildCategoryTree (handle parent === id as root) - Add cycle detection to flattenCategoryTree and findCategoryById - Prevent infinite recursion from malformed category data * Add generate_dummy_product.py to .gitignore --------- Co-authored-by: Shehryar Raza <[email protected]>
feat: SCRUM-40 - implement script for generating dummy products
fix: Scrum 40 product retrieve fix
feat: SCRUM-40 - create an order item table
fix: SCRUM-30 - update base product queryset
* feat: API base setup with Django backend integration
- Integrate Django authentication APIs (login, register, token refresh)
- Add email verification flow after registration
- Configure CORS for frontend-backend communication
- Implement token-based authentication with JWT
- Add user profile API integration
- Setup API client with automatic token refresh interceptors
- Configure Zustand store for auth state management
- Add success/error banners for auth pages
- Update API endpoints to match Django URL patterns
- Transform data between camelCase (frontend) and snake_case (backend)
Auth Flow:
- Registration: User registers β Email verification page β Verify email β Login
- Login: Fetch tokens β Fetch user profile β Store in Zustand β Redirect
- Token Refresh: Auto-refresh expired tokens via interceptor
- Logout: Clear tokens and user data from store
Backend Changes:
- Add django-cors-headers for CORS support
- Configure CORS allowed origins for local development
- Update RegisterSerializer to return user data only (no tokens)
Frontend Changes:
- Create VerifyEmailPage component for email verification
- Update LoginPage with enhanced error handling
- Update RegisterPage to redirect to email verification
- Add LoginResponseAPI and RegisterResponseAPI types
- Update authService to handle Django response formats
- Update API client token refresh logic
- Update auth store authentication logic based on accessToken presence
* feat: implement login flow with route protection and guest access
- Add ProtectedRoute component to guard authenticated routes
- Add PublicRoute component to prevent logged-in users from accessing auth pages
- Update AppRoutes with route protection for auth and protected routes
- Add 'Continue as Guest' button on login page for unauthenticated browsing
- Login redirects to home page (/) after successful authentication
- Protected routes (/checkout, /orders, /profile, /wishlist) require authentication
- Auth routes (/login, /register) redirect to home if already logged in
- Guest users can browse products but must login for protected features
* fix: add support for 'details' error key in auth error handling
- Add handling for 'details' key to match Django NON_FIELD_ERRORS_KEY config
- Update LoginPage and RegisterPage error handling
- Maintain backward compatibility with non_field_errors and detail keys
- Handle both array and string formats for details field
* fix: prevent duplicate form submissions during redirect delay
- Remove finally block that resets isSubmitting state
- Only reset isSubmitting on error to re-enable form
- Keep form disabled during success redirect to prevent duplicate submissions
- Affects LoginPage and RegisterPage with 1.5s redirect delays
* fix: ensure client auth state clears even when logout API fails
- Wrap logout API call in try-catch-finally block
- Always clear Zustand auth state in finally block
- Prevents users from being stuck logged in if /auth/logout/ returns 404
- Update Header component to use authService.logout() instead of direct store access
- Ensures reliable logout even when backend endpoint is not implemented
* added change
* added protected gate
* fix: prevent premature route redirects before auth state hydration
- Add hasHydrated flag to authStore to track persist rehydration status
- Update ProtectedRoute to wait for hydration before checking auth state
- Update PublicRoute to wait for hydration before redirecting
- Prevents authenticated users from being redirected to /login on initial page load
- Fixes race condition where routing decisions were made before localStorage state loaded
- Uses Zustand persist onRehydrateStorage callback to set hydration flag
* feat: implement user profile page with API integration
- Add comprehensive ProfilePage component with view/edit modes
- Implement GET /accounts/profile/ API integration
- Implement PATCH /accounts/profile/ API integration for updates
- Add UserProfileAPI and UpdateProfileRequestAPI types matching backend
- Add API response/request format conversion (snake_case <-> camelCase)
- Support all profile fields: username, firstName, lastName, mobile, gender
- Add profile header with avatar showing user initials
- Add inline editing with Edit/Save/Cancel buttons
- Add loading states and error handling
- Add success message with auto-hide after 3 seconds
- Display read-only fields: email, role, account status, member since date
- Use MUI Grid layout for responsive form fields
- Apply consistent styling with Colors config
* fix: use PUT instead of PATCH for profile update API call
- Change updateProfile to use apiClient.put instead of apiClient.patch
- Matches backend API endpoint that accepts PUT method
* fix: use PATCH and only send changed fields in profile update
- Change back to PATCH method for profile updates
- Only send fields that have actually changed (not empty and different from current)
- Add validation to check if there are any changes before sending request
- Show 'No changes to save' error if user tries to save without modifications
- Prevents sending empty username field that causes backend errors
- Ensures PATCH semantics: only modified fields are sent
* refactor: consolidate UserProfile to use backend API format (snake_case)
- Remove duplicate UserProfileAPI and UpdateProfileRequestAPI interfaces
- Use single UserProfile interface matching backend format with snake_case
- Remove conversion helper functions (convertProfileFromAPI, convertProfileToAPI)
- Update ProfilePage to use snake_case field names (first_name, last_name, etc.)
- Simplify userService by removing format conversion layer
- Direct API response/request mapping without transformation
- Benefits: simpler code, fewer conversions, single source of truth
* feat: integrate Mantine form with validation for ProfilePage
- Install @mantine/core, @mantine/hooks, @mantine/form packages
- Add MantineProvider to App.tsx wrapping MUI ThemeProvider
- Replace manual form state management with Mantine's useForm hook
- Add comprehensive validation rules:
- Username: required, min 3 chars, max 150 chars
- First name: required, min 2 chars, max 150 chars
- Last name: required, min 2 chars, max 150 chars
- Mobile: optional, max 20 chars
- Keep all UI components as MUI (TextField, Button, Grid, etc.)
- Use Mantine form's getInputProps() for field binding
- Display validation errors inline with MUI TextField error/helperText
- Form validates on submit, prevents submission if invalid
- Validation only shows when in edit mode
- Remove manual formData state and handleInputChange
- Use form.onSubmit() for handleSave with automatic validation
* refactor: move 'changed fields' validation logic into useForm hook
- Convert validate from object to function for access to all form values
- Add validation to check if any field has actually changed from profile
- Prevent form submission if no changes detected
- Show error message 'No changes detected' on username field if nothing changed
- Remove redundant 'No changes to save' check from handleSave
- Validation now handles both field-level and form-level validation
- API will only be called if at least one field has been modified
- Cleaner handleSave function with validation logic centralized in useForm
* refactor: modularize ProfilePage validation logic
- Create profileValidation.ts utility module with:
- VALIDATION_MESSAGES: centralized error messages
- VALIDATION_CONSTRAINTS: validation rules (min/max lengths)
- validateUsername, validateFirstName, validateLastName, validateMobile: individual field validators
- hasProfileChanges: check if any field changed from original
- getChangedFields: extract only modified fields for API call
- validateProfileForm: main validation function combining all validators
- Create useProfileForm custom hook:
- Encapsulates Mantine form initialization
- Integrates validateProfileForm for validation
- Provides setProfileValues and resetToProfile helper methods
- Cleaner separation of form logic from component
- Update ProfilePage component:
- Replace inline validation with useProfileForm hook
- Use setProfileValues in fetchProfile
- Use resetToProfile in handleCancel
- Use getChangedFields in handleSave
- Reduced component from ~200 lines to ~100 lines
- Much cleaner and more maintainable code
Benefits:
- Reusable validation functions
- Testable validation logic in isolation
- Centralized validation messages and constraints
- Easier to maintain and extend
- Better separation of concerns
- Type-safe validation utilities
* refactor: implement Zod for profile validation
- Install zod package for schema-based validation
- Replace manual validation functions with Zod schema (profileUpdateSchema)
- Create zodResolver helper to convert Zod errors to Mantine form format
- Export ProfileUpdateFormValues type inferred from Zod schema
- Update validateProfileForm to use zodResolver
- Maintain custom business logic (change detection) alongside Zod validation
Benefits of Zod:
- Type-safe validation with automatic TypeScript inference
- Declarative schema definition (easier to read and maintain)
- Built-in error messages with custom overrides
- Composable schemas for complex validation
- Runtime type checking aligned with TypeScript types
- Better error handling and reporting
- Industry standard for schema validation
- Reduces boilerplate code significantly
Schema features:
- Username: required, trimmed, 3-150 chars
- First name: required, trimmed, 2-150 chars
- Last name: required, trimmed, 2-150 chars
- Mobile: optional, max 20 chars
- Gender: enum (Male, Female, Other), optional
- Custom validation: no changes detection
* refactor: remove @mantine/core and @mantine/hooks packages
- Uninstall @mantine/core and @mantine/hooks
- Keep only @mantine/form for form management
- Remove MantineProvider from App.tsx
- Remove @mantine/core/styles.css import
- Use MUI exclusively for UI components
- Use @mantine/form only for form state and validation logic
Benefits:
- Smaller bundle size (removed 22 packages)
- No UI library conflicts
- Cleaner dependency tree
- MUI for all UI components (consistent design)
- Mantine form for validation logic only
- Best of both worlds: MUI UI + Mantine form management
* fix: allow clearing optional fields and support 'Not specified' gender
Issue 1: getChangedFields used truthy checks preventing cleared values
- Changed from truthy checks (values.mobile &&) to !== undefined
- Now allows sending empty string to clear mobile field
- Consistent with hasProfileChanges which uses !== undefined
- Prevents skipping updates when intended value is empty
Issue 2: Gender schema didn't allow empty string for 'Not specified'
- Updated gender schema to accept empty string: .or(z.literal(''))
- Aligns with MenuItem value='' for 'Not specified' option
- Prevents validation error when user selects 'Not specified'
- Allows clearing gender field
Before:
- if (values.mobile && values.mobile !== ...) // Truthy check
- gender: z.enum(['Male', 'Female', 'Other']).optional()
After:
- if (values.mobile !== undefined && values.mobile !== ...) // Explicit check
- gender: z.enum(['Male', 'Female', 'Other']).optional().or(z.literal(''))
Benefits:
- Users can now clear optional fields (mobile, gender)
- 'Not specified' gender option works without validation errors
- Consistent validation logic across all fields
- Empty strings are properly sent to API for clearing values
* fix: add in-flight guard to prevent concurrent save submissions
Issue: Double-clicking Save button causes multiple PATCH requests
- Multiple API calls can create race conditions
- Duplicate updates sent to backend
- No visual feedback during save operation
- Cancel button remains clickable during save
Solution: Add isSaving state to track save operation
- Early return if already saving: if (isSaving) return
- Set isSaving=true at start of save operation
- Set isSaving=false in finally block (always executes)
- Disable both Save and Cancel buttons while saving
- Show loading spinner in Save button during save
- Change button text to 'Saving...' for visual feedback
Implementation:
1. Added isSaving state: useState(false)
2. Guard clause in handleSave: if (isSaving) return
3. Set isSaving in try/finally blocks
4. Disabled buttons: disabled={isSaving}
5. Loading indicator: startIcon={isSaving ? <CircularProgress /> : <Save />}
6. Dynamic text: {isSaving ? 'Saving...' : 'Save Changes'}
Benefits:
- β
Prevents duplicate API requests
- β
Prevents race conditions
- β
Clear visual feedback (spinner + text)
- β
Buttons disabled during save
- β
Better UX with loading state
- β
Guaranteed cleanup with finally block
* fix: align gender field types with backend contract
Issue: Type mismatch between UI, validation, and backend
- UI allowed empty string '' for 'Not specified' option
- Zod schema allowed empty string: .or(z.literal(''))
- TypeScript types only allowed: 'Male' | 'Female' | 'Other'
- Backend gender field is NOT nullable (no null=True)
- Backend has default value of 'Other'
- Backend ALWAYS returns one of: 'Male' | 'Female' | 'Other'
Problem:
- If user selects 'Not specified' (empty string), backend rejects it
- Backend never returns empty string, so 'Not specified' state is impossible
- Type inconsistency between frontend and backend contract
- Potential runtime errors when backend returns non-empty gender
Solution: Align frontend with backend contract
1. Removed 'Not specified' option from UI
2. Updated Zod schema to require gender (removed .or(z.literal('')))
3. Updated form initial value: gender: 'Other' (matches backend default)
4. Updated setProfileValues: gender: profileData.gender (no fallback to undefined)
5. Gender is now always one of: 'Male' | 'Female' | 'Other'
Changes:
- ProfilePage.tsx: Removed <MenuItem value=""> for 'Not specified'
- profileValidation.ts: Changed gender schema from .optional().or(z.literal('')) to required enum
- useProfileForm.ts: Changed initial gender from undefined to 'Other'
- useProfileForm.ts: Removed fallback in setProfileValues (backend always returns value)
Backend Contract (Django):
Benefits:
- β
Frontend types match backend contract
- β
No type inconsistencies
- β
No runtime errors from unexpected empty strings
- β
Validation aligns with backend constraints
- β
Default value matches backend default ('Other')
- β
Simpler UI - no ambiguous 'Not specified' state
* fix: prevent memory leak from success message timeout on unmount
Issue: Success message timeout not cleared on unmount
- setTimeout creates a timeout that triggers setSuccessMessage after 3 seconds
- If user navigates away within 3 seconds, timeout still fires
- Causes 'setState on unmounted component' warning
- Minor memory leak from uncancelled timeout
Problem:
1. No cleanup function to clear timeout on unmount
2. Using setTimeout instead of lodash delay
3. No reference to timeout ID for cleanup
Solution: Add cleanup effect and use lodash delay
1. Import useRef from React
2. Import delay from lodash/delay
3. Add successTimeoutRef to store timeout ID
4. Clear existing timeout before setting new one in handleSave
5. Use lodash delay instead of setTimeout
6. Add cleanup useEffect to clear timeout on unmount
Changes:
- Added import: useRef from 'react'
- Added import: delay from 'lodash/delay'
- Added ref: successTimeoutRef = useRef<number | null>(null)
- Added cleanup effect: clears timeout on unmount
- Updated handleSave: clears existing timeout before setting new one
- Replaced setTimeout with lodash delay
- Store timeout ID in successTimeoutRef.current
Benefits:
- β
No setState on unmounted component warnings
- β
No memory leaks from uncancelled timeouts
- β
Proper cleanup on component unmount
- β
Using lodash delay (consistent with project patterns)
- β
Prevents multiple timeouts if user saves multiple times quickly
Code Flow:
1. User saves profile β setSuccessMessage('...')
2. Store timeout ID: successTimeoutRef.current = delay(...)
3. If user saves again before 3s β clear old timeout, set new one
4. If user navigates away β cleanup effect clears timeout
5. No setState on unmounted component β
* feat: implement avatar upload feature with consolidated state
Features:
- Avatar upload with 3-step direct upload process (start, upload, finish)
- Avatar preview with user initials fallback
- File validation (type: JPEG/PNG/GIF/WebP, size: max 5MB)
- Remove avatar functionality
- Loading states with spinner overlay
- Error handling and display
- Accessibility support (ARIA labels, keyboard navigation)
Components:
- AvatarUpload: Reusable avatar upload component
- Click to upload functionality
- File type and size validation
- Preview with loading overlay
- Remove button
- Error display
Services:
- StorageService: Handles file uploads
- startUpload(): Create file record and get upload URL
- uploadLocal(): Upload file to local storage
- finishUpload(): Mark upload as complete
- uploadFile(): Complete 3-step upload process
- uploadAvatar(): Upload with validation
State Management:
- Consolidated avatar state object:
{
isUploading: boolean
error: string | null
newUrl: string | null
}
- Replaced 3 separate states with single object
- Cleaner state updates with object spread
UI Improvements:
- Added size="small" to all TextFields
- Reduced TextField height for better layout
- Consistent spacing and alignment
Integration:
- ProfilePage integrates AvatarUpload component
- handleAvatarSelect: Uploads avatar and updates profile
- handleAvatarRemove: Removes avatar from profile
- Success messages with auto-hide timeout
API Endpoints:
- POST /storage/direct/ - Start upload
- POST /storage/direct/local/{file_id}/ - Upload to local storage
- POST /storage/direct/finish/ - Finish upload
- PATCH /accounts/profile/ - Update profile with avatar URL
Types:
- FileUploadStartRequest/Response
- FileUploadFinishRequest/Response
- Storage types in features/storage/types
Files Added:
- features/storage/types/index.ts
- features/user/profile/components/AvatarUpload.tsx
- features/user/profile/AVATAR_UPLOAD.md
- services/api/storage/storageService.ts
Files Modified:
- config/api.ts: Added STORAGE endpoints
- features/user/profile/components/ProfilePage.tsx:
- Integrated AvatarUpload component
- Consolidated avatar state
- Added avatar upload handlers
- Reduced TextField height (size="small")
Known Issues:
- Storage endpoints require hasAdminOrMerchantRole permission
- Regular users cannot upload avatars without backend permission changes
- See AVATAR_UPLOAD.md for details and workarounds
Benefits:
- β
Clean, reusable avatar upload component
- β
Consolidated state management (1 object vs 3 states)
- β
Proper file validation
- β
Loading and error states
- β
Accessibility support
- β
Better UI with smaller TextFields
- β
Comprehensive documentation
* fix: resolve TextField height inconsistency and 403 permission error
UI Fixes:
- Fixed TextField height inconsistency between username and email fields
- Added space (' ') as helperText to all editable fields when not editing
- This ensures all TextFields have consistent height regardless of edit mode
- Applied to: username, first_name, last_name, mobile, gender, account_status
Backend Permission Fix:
- Changed storage endpoints from [IsAuthenticated, hasAdminOrMerchantRole] to [IsAuthenticated]
- Allows all authenticated users (including members) to upload files for avatars
- Previously only admin/merchant roles could upload, causing 403 errors for regular users
Files Modified:
- augment-store/client/src/features/user/profile/components/ProfilePage.tsx
- Changed helperText from '' to ' ' for consistent height
- Applied to all editable fields (username, first_name, last_name, mobile, gender)
- Added helperText=' ' to account_status field
- augment-store/server/storage/views.py
- StartDirectFileUpload: permission_classes = [IsAuthenticated]
- DirectLocalFileUpload: permission_classes = [IsAuthenticated]
- FinishDirectFileUploadFinish: permission_classes = [IsAuthenticated]
- augment-store/server/storage/tests.py
- Updated test_start_direct_upload_member_role_forbidden β test_start_direct_upload_member_role_allowed
- Updated test_direct_local_upload_member_role_forbidden β test_direct_local_upload_member_role_allowed
- Updated test_finish_direct_upload_member_role_forbidden β test_finish_direct_upload_member_role_allowed
- Changed expected status from 403 FORBIDDEN to 201/200 OK
- Updated comments to reflect new behavior
Issue Resolved:
β
TextField height now consistent across all fields
β
No more 403 errors when uploading avatars
β
All authenticated users can now upload avatars
β
Tests updated to reflect new permission model
Before:
- Username field: no helperText when not editing β shorter height
- Email field: always has helperText β taller height
- Result: Misaligned fields
After:
- All fields: always have helperText (space when not editing)
- Result: Consistent height and alignment
Before:
- POST /api/v1/storage/direct/ β 403 Forbidden (member users)
- Only admin/merchant could upload
After:
- POST /api/v1/storage/direct/ β 201 Created (all authenticated users)
- All users can upload avatars
* debug: add authentication check and logging to storage service
Added:
- Import useAuthStore to check authentication state
- Pre-flight authentication check before upload
- Console logging to debug auth state
- Clear error message if user is not authenticated
This will help diagnose the 401 error by:
1. Checking if user is authenticated before making request
2. Logging auth state to console for debugging
3. Providing clear error message if not logged in
Debug output includes:
- isAuthenticated status
- hasAccessToken boolean
- tokenPreview (first 20 chars for security)
If you see this error, it means:
- User is not logged in
- Access token is missing/expired
- Need to login again
* fix: resolve 'Cannot read properties of undefined (reading file)' error in avatar upload
Issue:
- Avatar upload was failing with error: "Cannot read properties of undefined (reading 'file')"
- Root cause: Double extraction of .data property from API responses
Problem:
- apiClient.post() already returns response.data (see client.ts line 92)
- storageService was doing: response.data.data (double extraction)
- This resulted in undefined values
Fix:
- β
Remove redundant .data extraction in startUpload()
- β
Remove redundant .data extraction in finishUpload()
- β
Add comprehensive logging to uploadFile() for debugging
- β
Add validation checks for response structure
- β
Add better error messages
Changes:
- augment-store/client/src/services/api/storage/storageService.ts
- Fixed startUpload(): return response instead of response.data
- Fixed finishUpload(): return response instead of response.data
- Added detailed console logging for each upload step
- Added validation for finishResponse.file
- Added validation for finishResponse.file.file
- Added try-catch with error logging in uploadFile()
Before:
```typescript
const response = await apiClient.post<T>('/endpoint', data)
return response.data // β response is already data!
```
After:
```typescript
const response = await apiClient.post<T>('/endpoint', data)
return response // β
apiClient.post returns response.data
```
Testing:
- Avatar upload should now work correctly
- Console will show detailed logs for each step
- Better error messages if upload fails
* refactor: simplify file upload to 2-step process with UUID generation
Changes Overview:
- Simplified upload from 3 steps to 2 steps
- Frontend generates random UUID for file_id
- Backend creates file record if it doesn't exist
- Finish endpoint returns file URL as string (not full object)
Frontend Changes (augment-store/client/src/services/api/storage/storageService.ts):
- β
Removed startUpload() method (no longer needed)
- β
Added generateUUID() method to create random UUIDs
- β
Renamed uploadLocal() to uploadToLocal() and made it private
- β
Updated finishUpload() to return string instead of object
- β
Simplified uploadFile() to 2 steps:
1. Generate UUID and upload to /storage/direct/local/{file_id}/
2. Call /storage/direct/finish/ to get file URL as string
- β
Removed dependency on FileUploadStartRequest/Response types
- β
Added authentication check in uploadFile()
- β
Improved logging for each step
Backend Changes (augment-store/server/storage/serializers.py):
- β
Updated DirectLocalFileUploadSerializer to create file record if it doesn't exist
- β
Changed from get_object_or_404() to try/except with File.objects.get()
- β
Auto-create File record with UUID if not found
- β
Extract file metadata from uploaded file object
- β
Updated FinishFileUploadSerializer.get_file() to return URL string
- β
Return file.file.url for local storage
- β
Return presigned URL for S3 storage
- β
Return None if file doesn't exist
New Upload Flow:
1. Frontend generates random UUID (e.g., "abc-123-def-456")
2. POST /storage/direct/local/{file_id}/ with file
- Backend creates File record if doesn't exist
- Backend saves uploaded file
3. POST /storage/direct/finish/ with file_id
- Backend marks upload as complete
- Backend returns file URL as string: { "file": "http://..." }
Benefits:
- β
Simpler flow (2 steps instead of 3)
- β
No need to call /storage/direct/ first
- β
Frontend controls file_id generation
- β
Backend auto-creates file record
- β
Cleaner response (string instead of nested object)
- β
Less API calls = faster uploads
Example Response:
Before: { "file": { "id": "...", "file": "http://...", ... }, "file_id": "..." }
After: { "file": "http://localhost:8000/media/uploads/avatar.jpg" }
* revert: use /storage/direct/ endpoint for S3 presigned URL flow
Reverted Changes:
- Reverted to proper 3-step S3 upload flow
- /storage/direct/local/{file_id}/ was only for testing
- Production uses S3 with presigned URLs
New Upload Flow (S3):
1. POST /storage/direct/ β Get presigned URL and file_id
2. PUT to S3 presigned URL β Upload file directly to S3
3. POST /storage/direct/finish/ β Get actual file URL as string
Frontend Changes (augment-store/client/src/services/api/storage/storageService.ts):
- β
Added startUpload() to get presigned URL from /storage/direct/
- β
Added uploadToS3() to upload file using presigned URL
- β
Uses axios.put() directly for S3 (no auth headers needed)
- β
finishUpload() returns file URL as string
- β
Removed UUID generation (backend creates file_id)
- β
Added StartUploadResponse interface
- β
3-step process with detailed logging
Backend Changes (augment-store/server/storage/serializers.py):
- β
Reverted DirectLocalFileUploadSerializer to use get_object_or_404
- β
Removed auto-creation logic (not needed for S3 flow)
- β
Kept FinishFileUploadSerializer returning URL as string
Upload Flow Details:
Step 1 - Start Upload:
POST /storage/direct/
Request: { "original_file_name": "avatar.jpg", "file_type": "image/jpeg" }
Response: {
"file": { "id": "abc-123", ... },
"presigned_data": { "url": "https://s3.amazonaws.com/..." }
}
Step 2 - Upload to S3:
PUT https://s3.amazonaws.com/bucket/path/file.jpg
Headers: { "Content-Type": "image/jpeg" }
Body: <file binary data>
Step 3 - Finish Upload:
POST /storage/direct/finish/
Request: { "file_id": "abc-123" }
Response: { "file": "https://s3.amazonaws.com/bucket/path/file.jpg" }
Benefits:
- β
Direct upload to S3 (faster, no backend bottleneck)
- β
Backend generates presigned URL (secure, time-limited)
- β
No file data passes through Django (saves bandwidth)
- β
Finish endpoint returns clean URL string
- β
Works with both S3 and local storage
Note:
- /storage/direct/local/{file_id}/ is still available for local testing
- Production should use S3 with presigned URLs
- Local storage can be used in development
* SCRUM-34 - fix storage upload issue
* fix: update storage service for S3 presigned POST upload
Updated to match new backend response format from /storage/direct/
Backend Response Format:
{
"file": {
"id": "919ddd4b-dac3-428f-bd59-e8acab8201d0",
"file": "https://s3.eu-west-3.amazonaws.com/...",
"original_file_name": "Screenshot.png",
"file_name": "3300ba91.png",
"file_type": "image/png",
"upload_finished_at": null,
"created_by": "c77458e4-...",
"created_at": "2025-10-29T17:09:29.382196Z",
"updated_at": "2025-10-29T17:09:29.383426Z"
},
"presigned_data": {
"url": "https://pasquo.s3.amazonaws.com/",
"fields": {
"acl": "public-read",
"Content-Type": "image/png",
"key": "augment-store/3300ba91.png",
"x-amz-algorithm": "AWS4-HMAC-SHA256",
"x-amz-credential": "...",
"x-amz-date": "20251029T170929Z",
"policy": "...",
"x-amz-signature": "..."
}
}
}
Changes (augment-store/client/src/services/api/storage/storageService.ts):
- β
Updated StartUploadResponse interface to match backend response
- β
Added file.file field (presigned URL from backend)
- β
Changed presigned_data.fields to object with S3 POST fields
- β
Updated uploadToS3() to use POST instead of PUT
- β
Build FormData with presigned fields + file
- β
Add presigned fields first, then file (order matters for S3)
- β
Use /storage/direct/finish/ endpoint (not /storage/direct/{id}/)
- β
Updated comments to reflect S3 presigned POST flow
Upload Flow:
1. POST /storage/direct/ with { original_file_name, file_type }
β Get file.id and presigned_data.fields
2. POST to S3 with FormData(presigned_fields + file)
β Upload file to S3 bucket
3. POST /storage/direct/finish/ with { file_id }
β Get final file URL as string
S3 Presigned POST:
- Uses POST (not PUT) to S3
- Requires FormData with specific fields
- Fields include: acl, Content-Type, key, policy, signature, etc.
- File must be appended last to FormData
- Order of fields matters for S3 signature validation
Profile Update:
- After upload completes, use returned file URL
- PATCH /accounts/profile/ with { image: fileUrl }
- fileUrl is the final S3 URL from finish endpoint
* docs: add detailed logging to clarify file URL flow
Added comprehensive logging to show:
- β
URL returned from /storage/direct/finish/ endpoint
- β
Confirmation that we use file.file URL (not presigned URL)
- β
Clear indication that final URL is saved to profile.image
The flow is:
1. POST /storage/direct/ β Get presigned data for S3 upload
2. POST to S3 β Upload file using presigned fields
3. POST /storage/direct/finish/ β Get final file.file URL
4. Return file.file URL (e.g., https://s3.eu-west-3.amazonaws.com/pasquo/augment-store/file.png)
5. Save this URL to profile.image via PATCH /accounts/profile/
Note: We do NOT use the presigned bucket URL (https://pasquo.s3.amazonaws.com/)
We use the final file URL from the finish endpoint response
* fix: correct upload flow - backend handles S3 upload, not frontend
Fixed Upload Flow:
1. POST /storage/direct/ β Create file record, get file.id
2. POST /storage/direct/local/{file_id}/ β Upload file to backend (backend uploads to S3)
3. POST /storage/direct/finish/ β Get final file URL from backend
Changes:
- β
Removed direct S3 upload from frontend
- β
Removed axios import (not needed)
- β
Removed uploadToS3() method
- β
Added uploadToBackend() method to send file to Django
- β
Backend handles S3 upload, not frontend
- β
No CORS issues - all requests go to backend
- β
Simplified presigned_data interface (not used for upload)
The backend receives the file and uploads it to S3.
Frontend just sends file to backend and gets back the final S3 URL.
* refactor: simplify upload to 2-step process (remove local upload endpoint)
Simplified Upload Flow:
1. POST /storage/direct/ β Create file record, get file.id and file.file
2. POST /storage/direct/finish/ β Confirm and get final file URL
Changes:
- β
Removed uploadToBackend() method
- β
Removed LOCAL_UPLOAD endpoint from API config (already removed by user)
- β
Simplified from 3 steps to 2 steps
- β
Backend handles file upload internally
- β
Frontend just creates record and confirms completion
The /storage/direct/ endpoint returns file.id and file.file.
We pass file.id to /storage/direct/finish/ to get the final URL.
No need for /storage/direct/local/{file_id}/ endpoint on frontend.
* added changes
* fix: send file ID to profile_image instead of URL to image
Changed avatar upload to use the new storage system properly:
**Problem:**
- Was sending file URL to `image` field (legacy ImageField)
- Should send file ID to `profile_image` field (ForeignKey to storage.File)
- Backend serializer expects file ID, not the whole file object
**Solution:**
1. **storageService.uploadFile()**: Return file ID instead of URL
- Changed return type to return `finishResponse.file.id`
- File ID is used as ForeignKey reference to storage.File
2. **ProfilePage.handleAvatarSelect()**: Send file ID to `profile_image`
- Changed from `{ image: avatarUrl }` to `{ profile_image: fileId }`
- Backend will link the File record to user's profile
3. **ProfilePage.handleAvatarRemove()**: Clear `profile_image` field
- Changed from `{ image: '' }` to `{ profile_image: '' }`
4. **UpdateProfileRequest**: Added `profile_image` field
- `image`: Legacy ImageField (direct file URL)
- `profile_image`: ForeignKey to storage.File (file ID)
**Backend Flow:**
1. POST /storage/direct/ β Create File record, get file.id
2. POST /storage/direct/finish/ β Finish upload, return file object
3. PATCH /accounts/profile/ β Send { profile_image: file.id }
4. Backend links File record to User.profile_image (ForeignKey)
**Why File ID, Not URL:**
- `profile_image` is a ForeignKey field, expects UUID (file ID)
- Backend serializer will expand this to full file object on GET
- Sending URL would cause validation error (not a valid UUID)
**Example:**
- Upload returns: `{ file: { id: 'abc-123', file: 'https://...' } }`
- We send: `{ profile_image: 'abc-123' }` β
- NOT: `{ image: 'https://...' }` β
* fix: send file URL (file.file) to profile_image, not file ID
Changed to send the file URL string instead of file ID:
**What Changed:**
- **storageService.uploadFile()**: Return `file.file` (URL) instead of `file.id`
- **ProfilePage**: Send file URL to `profile_image` field
**Before:**
```typescript
// Returned file ID
return finishResponse.file.id // 'abc-123-def-456'
// Sent to profile
{ profile_image: 'abc-123-def-456' }
```
**After:**
```typescript
// Return file URL
return finishResponse.file.file // 'https://s3.amazonaws.com/...'
// Send to profile
{ profile_image: 'https://s3.amazonaws.com/...' }
```
**Backend Expectation:**
- `profile_image` field expects the file URL string (from file.file attribute)
- Not the file ID (file.id)
- Not the whole file object
**Example Response:**
```json
{
"file": {
"id": "abc-123",
"file": "https://s3.amazonaws.com/bucket/path/image.jpg", β We send this
"original_file_name": "avatar.jpg",
...
}
}
```
* fix: revert to sending file ID (file.id) to profile_image
Reverted back to sending file ID instead of file URL:
**What Changed:**
- **storageService.uploadFile()**: Return `file.id` (UUID) instead of `file.file` (URL)
- **ProfilePage**: Send file ID to `profile_image` field
**Current Implementation:**
```typescript
// Return file ID
return finishResponse.file.id // 'abc-123-def-456'
// Send to profile
{ profile_image: 'abc-123-def-456' }
```
**Why File ID:**
- `profile_image` is a ForeignKey field in Django User model
- ForeignKey expects the related object's ID (UUID), not a URL
- Backend will use this ID to link to the storage.File record
- When fetching profile, backend serializer expands this to full file object
**Backend Flow:**
1. Upload file β Get file.id from storage service
2. Send file.id to profile_image field
3. Django saves ForeignKey relationship: User.profile_image_id = file.id
4. On GET /profile β Serializer expands profile_image to full File object
**Example:**
```json
// Upload response
{ "file": { "id": "abc-123", "file": "https://..." } }
// We send
{ "profile_image": "abc-123" } β File ID (UUID)
// Backend GET response
{
"profile_image": {
"id": "abc-123",
"file": "https://...",
...
}
}
```
* feat: add S3 direct upload step between /direct and /finish
Added the missing S3 upload step in the file upload flow:
**Problem:**
- Was only calling /storage/direct/ and /storage/direct/finish/
- Missing the actual file upload to S3 using presigned URL
- File never actually uploaded to S3 storage
**Solution:**
Added 3-step upload process:
1. **POST /storage/direct/** β Get file.id + presigned URL/fields
2. **POST to S3** β Upload file directly to S3 using presigned data β
NEW
3. **POST /storage/direct/finish/** β Confirm upload, get final file object
**New Method: `uploadToS3()`**
```typescript
private async uploadToS3(
file: File,
presignedUrl: string,
presignedFields: Record<string, string>
): Promise<void> {
const formData = new FormData()
// Add all presigned fields first
Object.keys(presignedFields).forEach((key) => {
formData.append(key, presignedFields[key])
})
// Add file last (important for S3)
formData.append('file', file)
// Upload directly to S3 (not through our API)
await axios.post(presignedUrl, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
```
**Updated Flow:**
```typescript
// Step 1: Get presigned URL
const startResponse = await this.startUpload(file.name, file.type)
const { id, presigned_data } = startResponse.file
// Step 2: Upload to S3 (NEW!)
await this.uploadToS3(file, presigned_data.url, presigned_data.fields)
// Step 3: Finish upload
const finishResponse = await this.finishUpload(id)
return finishResponse.file.id
```
**Why This Order:**
1. Presigned fields must be added to FormData before the file
2. S3 requires specific field order for presigned POST uploads
3. File must be appended last to FormData
**Dependencies:**
- Added `import axios from 'axios'` for direct S3 upload
- Using axios instead of apiClient to bypass auth interceptors for S3
* feat: display profile_image.file as avatar when available
Updated profile to use the new profile_image field from backend:
**Problem:**
- Backend now returns `profile_image` field (ForeignKey to storage.File)
- `profile_image` can be `null` or an object with `file` property
- Frontend was only checking legacy `image` field
**Solution:**
1. **Added StorageFile interface** to match backend File model:
```typescript
export interface StorageFile {
id: string
file: string // The actual file URL
original_file_name: string
file_name: string
file_type: string
file_size: number
uploaded_at: string
}
```
2. **Updated UserProfile interface**:
```typescript
export interface UserProfile {
// ... other fields
image: string // Legacy ImageField (direct file URL)
profile_image: StorageFile | null // ForeignKey (expanded object)
}
```
3. **Updated avatar display logic** (priority order):
```typescript
currentImage={
avatarState.newUrl || // 1. Newly uploaded (optimistic)
profile?.profile_image?.file || // 2. From storage.File (new)
profile?.image || // 3. Legacy image field
null // 4. No avatar
}
```
4. **Updated handleAvatarSelect** to extract URL from profile_image:
```typescript
const newAvatarUrl =
updatedProfile.profile_image?.file ||
updatedProfile.image ||
null
setAvatarState((prev) => ({ ...prev, newUrl: newAvatarUrl }))
```
**Backend Response Example:**
```json
{
"id": "user-123",
"email": "[email protected]",
"image": "", // Legacy field (empty)
"profile_image": { // New field (expanded ForeignKey)
"id": "file-abc-123",
"file": "https://s3.amazonaws.com/bucket/avatar.jpg",
"original_file_name": "avatar.jpg",
"file_type": "image/jpeg",
"file_size": 102400,
"uploaded_at": "2024-01-15T10:30:00Z"
}
}
```
**Fallback Chain:**
1. If `profile_image` exists β Use `profile_image.file` β
2. If `profile_image` is null β Fall back to legacy `image` field
3. If both are empty β Show default avatar
This ensures backward compatibility while supporting the new storage system!
* fix: only send changed fields when updating profile image
Updated avatar upload/remove to include pending form changes:
**Problem:**
- When user edits profile fields AND uploads/removes avatar, only `profile_image` was sent
- Pending form changes were lost/ignored
- User would have to save form separately after avatar change
**Solution:**
Updated `handleAvatarSelect` and `handleAvatarRemove` to:
1. Get any pending form changes using `getChangedFields()`
2. Combine form changes with avatar update
3. Send only changed fields + `profile_image`
**Before:**
```typescript
// Only sent profile_image, lost pending form changes
await userService.updateProfile({ profile_image: fileId })
```
**After:**
```typescript
// Get pending form changes
const formChanges = getChangedFields(form.values, profile)
// Combine with avatar update
const updateData = {
...formChanges,
profile_image: fileId,
}
// Send only changed fields
await userService.updateProfile(updateData)
```
**Example Scenarios:**
1. **User only uploads avatar:**
- formChanges = {} (no changes)
- updateData = { profile_image: 'file-id' }
- β
Only profile_image sent
2. **User edits name + uploads avatar:**
- formChanges = { first_name: 'John', last_name: 'Doe' }
- updateData = { first_name: 'John', last_name: 'Doe', profile_image: 'file-id' }
- β
All changes sent together
3. **User edits mobile + removes avatar:**
- formChanges = { mobile: '+1234567890' }
- updateData = { mobile: '+1234567890', profile_image: '' }
- β
All changes sent together
**Benefits:**
- β
Only changed fields are sent (efficient)
- β
Pending form changes are preserved
- β
User doesn't need to save twice
- β
Single API call for all changes
**Added Logging:**
```typescript
console.log('π€ Sending profile update with:', updateData)
```
This helps debug what fields are being sent to the backend.
* added clean gitignore section
* Add validation error feedback to AvatarUpload component
- Add onValidationError callback prop for parent notification
- Add internal validationError state for displaying validation errors
- Show specific error messages for invalid file type and size
- Display validation errors as warning alerts (separate from upload errors)
- Clear validation errors when removing image or selecting new file
- Reset file input on validation failure to allow re-selection
- Improve UX by providing clear feedback instead of silent failures
* Fix ForeignKey clearing to use null instead of empty string
- Update UpdateProfileRequest type to allow null for profile_image field
- Change handleAvatarRemove to send null instead of empty string
- Prevents DRF serializer validation errors when clearing ForeignKey
- Update comment to clarify null is used to clear the ForeignKey field
- Auto-format AVATAR_UPLOAD.md documentation
* Fix presigned_data type to match actual S3 response shape
- Change presigned_data.presigned_data (Record<string, unknown>) to presigned_data.fields (Record<string, string>)
- Matches actual S3 presigned POST response structure used in storageService
- Add comment explaining fields contain S3 presigned POST data (key, policy, signature, etc.)
- Update AVATAR_UPLOAD.md with detailed S3 vs Local storage upload flows
- Document that file must be appended last in FormData for S3 compatibility
- Clarify profile_image is a ForeignKey (file ID) and null clears the relation
* Fix memory leak by revoking blob URLs from createObjectURL
- Add useEffect cleanup to revoke blob URL on component unmount
- Revoke previous preview URL before creating new one on file selection
- Revoke preview URL when removing image
- Prevents memory leaks from unreleased blob URLs
* Fix storage service types, docs, and security issues
- Update JSDoc to accurately describe 3-step S3 direct upload flow (not backend upload)
- Add distinct FinishUploadResponse type with non-null file URL
- Type StartUploadResponse.file.file as string | null (null until finished)
- Remove logging of presigned S3 URL to avoid exposing sensitive data
- Remove excessive logging of response objects
- Update step comments to clarify presigned POST flow
* Remove PII logging and fix S3 multipart upload headers
Security & Privacy:
- Remove logging of updateData payload to prevent PII exposure in browser logs
- User data (name, email, mobile, etc.) should not be logged to console
S3 Upload Fix:
- Remove static Content-Type: 'multipart/form-data' header from S3 upload
- Let browser automatically set Content-Type with proper boundary parameter
- Static header omits boundary and causes S3 to reject malformed requests
- Browser-generated header: 'multipart/form-data; boundary=----WebKitFormBoundary...'
This prevents upload failures in different browser environments.
* Remove .DS_Store from git and fix UserProfile.image type
.DS_Store cleanup:
- Remove augment-store/.DS_Store from git tracking (macOS artifact)
- Add **/.DS_Store pattern to .gitignore to catch all subdirectories
- Prevents future accidental commits of OS-specific files
Type safety fix:
- Change UserProfile.image from 'string' to 'string | null'
- Change UpdateProfileRequest.image from 'string' to 'string | null'
- Matches Django ImageField serializer behavior (returns null when no image)
- Prevents runtime errors when handling profiles without images
- Existing code already handles null correctly with optional chaining
* Clear preview URL when server image becomes available
Problem:
- previewUrl (local blob) takes precedence over currentImage (server URL)
- After successful upload, UI keeps showing stale blob URL
- Blob URL not revoked until component unmount
- Causes memory overhead and stale display
Solution:
- Add useEffect that watches currentImage changes
- When server image becomes available, immediately:
- Revoke the blob URL to free memory
- Clear previewUrl state to show server image
- Prevents stale preview and reduces memory overhead
Benefits:
- β
UI shows server image immediately after upload
- β
Blob URL released as soon as server image available
- β
No memory overhead from keeping unused blob URLs
- β
Better UX - users see the final uploaded image
* Add debug logging and fix preview clearing logic
Debug logging:
- Log new avatar URL from server response
- Log updated profile object
- Log AvatarUpload render state (currentImage, previewUrl, displayImage)
Fix preview clearing:
- Track previous currentImage with useRef to detect actual changes
- Only clear preview when currentImage actually changes (not on initial render)
- Prevents clearing preview when component re-renders with same image
This will help diagnose why uploaded image is not showing
* Refactor: Use shared storage types to avoid type drift
Problem:
- StartUploadResponse and FinishUploadResponse were duplicated in storageService
- Duplicate type definitions can drift over time and cause inconsistencies
- Violates DRY principle (Don't Repeat Yourself)
Solution:
- Import FileUploadStartResponse and FileUploadFinishResponse from @features/storage/types
- Remove local interface definitions
- Use shared types throughout the service
Benefits:
- β
Single source of truth for storage API types
- β
Prevents type drift between service and shared types
- β
Easier maintenance - update types in one place
- β
Better type consistency across the codebase
- β
Follows DRY principle
* updated PR description
---------
Co-authored-by: Shehryar Raza <[email protected]>
Co-authored-by: sagelite <[email protected]>
* feat: integrate products API with frontend pagination
Features:
- β
Fetch products from Django backend API (GET /api/v1/products/)
- β
Transform backend response format to frontend Product type
- β
Implement frontend pagination (12 products per page)
- β
Add loading states with CircularProgress spinner
- β
Add error handling with retry button
- β
Add MUI Pagination component with first/last buttons
- β
Scroll to top on page change
- β
Update HomePage to use real API for featured products
- β
Update ShopPage to use real API with filters and sorting
Backend API Integration:
- Created ProductAPI types matching Django serializer format
- Created transformProductFromAPI() to convert backend β frontend format
- Handle backend Decimal fields (price, rating) as strings
- Handle missing fields (discountPrice, reviewCount, createdAt)
- Fallback to placeholder image if no images available
Frontend Pagination:
- Page size: 12 products per page
- Standard numbered pagination (click numbers to navigate)
- Show first/last page buttons
- Smooth scroll to top on page change
- Display total pages and current page
Files Modified:
- augment-store/client/src/config/api.ts
- Fixed BASE_URL back to http://localhost:8000/api/v1
- augment-store/client/src/features/products/types/api.ts (NEW)
- ProductAPI, ProductBrandAPI, ProductCategoryAPI types
- transformProductFromAPI() transformation function
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to fetch from backend and paginate on frontend
- Updated getProductById() to transform backend response
- Updated searchProducts() to filter on frontend (backend has no search yet)
- Updated getCategories() to extract from products
- Updated getFeaturedProducts() to use real API
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added API state (products, isLoading, error, currentPage, totalPages)
- Added useEffect to fetch products on mount and page change
- Added loading spinner
- Added error state with retry button
- Added Pagination component
- Added handlePageChange with scroll to top
- Updated filteredAndSortedProducts to use API products
- augment-store/client/src/features/products/product-list/components/HomePage.tsx
- Updated to use productService instead of mockProductService
- Added loading state
- Added empty state message
Technical Details:
- Backend returns array of products (no pagination metadata)
- Frontend implements pagination by slicing the array
- When backend adds pagination, we can easily switch to backend pagination
- Filters and sorting still work on frontend (applied after fetching)
- Price range filter updates based on fetched products
Next Steps:
- Backend can add pagination support (page, limit query params)
- Backend can add search endpoint
- Backend can add featured products endpoint
- Backend can add discount_price field to Product model
- Backend can add review_count field to Product model
* fix: update products API to handle paginated backend response
Backend API Change:
- Backend now returns paginated response format:
{
"count": 123,
"next": "url",
"previous": "url",
"results": [...]
}
Changes:
- β
Added PaginatedProductsAPI type for backend response
- β
Updated getProducts() to use paginated response
- β
Pass page and page_size params to backend
- β
Extract products from response.results
- β
Use response.count for total count
- β
Calculate totalPages from count and limit
- β
Updated searchProducts() to use paginated response
- β
Updated getCategories() to use paginated response
- β
Updated ShopPage to track and display totalCount
- β
Display total count from backend instead of filtered count
Files Modified:
- augment-store/client/src/features/products/types/api.ts
- Added PaginatedProductsAPI interface
- Updated comments for images field
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to handle paginated response
- Pass page and page_size query params
- Extract data from response.results
- Use response.count for total
- Updated searchProducts() to fetch paginated data
- Updated getCategories() to fetch paginated data
- Added error handling for all methods
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added totalCount state
- Update totalCount from API response
- Display totalCount instead of filtered count
- Reset totalCount on error
Backend Pagination:
- Backend uses Django REST Framework pagination
- Query params: ?page=1&page_size=12
- Response includes count, next, previous, results
- Frontend now properly uses backend pagination
* Remove unused page_size parameter from product API calls
Problem:
- Backend has fixed PAGE_SIZE of 100 in REST_FRAMEWORK settings
- Frontend was sending page_size parameter that was being ignored
- This created confusion about pagination behavior
- getProducts was accepting limit parameter that had no effect
Solution:
- Remove page_size from API request params
- Document that backend has fixed page_size of 100
- Use constant backendPageSize = 100 in response
- Update searchProducts to fetch all pages (since backend returns max 100 per page)
- Update getFeaturedProducts to slice from first page results
Benefits:
- β
Code accurately reflects backend behavior
- β
No misleading parameters that don't work
- β
Clear documentation of pagination constraints
- β
searchProducts now correctly fetches all products across pages
- β
Prevents confusion about why limit parameter doesn't work
* Fix brief 'No products found' flash on initial load
Problem:
- On initial page load, 'No products found' message briefly appears
- This happens before the API call completes
- Poor UX - users see error state before loading completes
Root Cause:
- Component renders with empty products array initially
- isLoading starts as false, so conditional renders 'No products found'
- Then useEffect runs and sets isLoading to true
- Creates a flash of wrong content
Solution:
- Add hasLoadedOnce flag to track if initial load completed
- Show loading spinner if isLoading OR !hasLoadedOnce
- Set hasLoadedOnce to true after first API call (success or error)
- Prevents showing 'No products found' before first load completes
Benefits:
- β
No more flash of 'No products found' on initial load
- β
Loading spinner shows immediately on mount
- β
Better UX - users see loading state, not error state
- β
Only shows 'No products found' after actually loading
* Implement client-side pagination to show 12 products per page
Problem:
- Backend returns 100 items per page (fixed in settings)
- Frontend was displaying ALL 100 items on one page
- User reported seeing only 49 items (likely due to filters)
- No pagination was actually working - all filtered products shown at once
- PRODUCTS_PER_PAGE constant was defined but never used
Root Cause:
- Component was designed for client-side pagination but not implementing it
- filteredAndSortedProducts was rendered directly without slicing
- Pagination UI was using backend totalPages (based on 100 items/page)
- Mismatch between backend pagination (100) and frontend expectation (12)
Solution:
- Implement client-side pagination with 12 items per page
- Separate apiPage (backend) from clientPage (frontend display)
- Add paginatedProducts that slices filteredAndSortedProducts
- Calculate totalClientPages based on filtered products count
- Reset clientPage to 1 when filters or sort changes
- Render paginatedProducts instead of all filteredAndSortedProducts
Implementation:
- apiPage: Backend page number (100 items per page) - currently fixed at 1
- clientPage: Frontend page number (12 items per page) - user navigates
- paginatedProducts: Slice of filteredAndSortedProducts for current page
- totalClientPages: Total pages based on filtered products / 12
Benefits:
- β
Shows 12 products per page as intended
- β
Pagination works correctly for filtered products
- β
Better UX - manageable number of products per page
- β
Pagination resets when filters change
- β
Smooth scrolling to top on page change
- β
Can handle up to 100 products from backend (page 1)
* Add debug logging to diagnose product count mismatch
Problem:
- API returns 100 products but only 49 showing on frontend
- Need to identify where products are being lost
Debug Logging Added:
1. productService.getProducts():
- Log raw API response (count, results.length, next, previous)
- Log transformed products count and first product
2. ShopPage component:
- Log API response details (productsCount, total, page, limit)
- Log pagination info (totalProducts, filteredProducts, paginatedCount)
This will help identify:
- Is API actually returning 100 products?
- Are all products being transformed correctly?
- Are filters removing products?
- Is pagination slicing correctly?
Next Steps:
- Check browser console logs
- Verify API response has 100 items
- Check if transformation is dropping products
- Check if filters are removing products
* Remove filtering and sorting logic - display all products
Problem:
- Filtering and sorting are not being handled right now
- filteredAndSortedProducts was applying price and rating filters
- This was reducing the product count unnecessarily
Changes:
- Removed filter logic (price range, rating range)
- Removed sort logic (newest, price-asc, price-desc, rating-desc)
- Pagination now works directly on products array
- Reset client page when products.length changes (not filters/sort)
Result:
- All products from API are now displayed
- Pagination shows all 100 products across pages
- No products are filtered out
- Simpler logic until filtering/sorting is implemented
Note:
- Filter UI components still exist but don't affect display
- Sort dropdown still exists but doesn't affect display
- Can re-add filtering/sorting later when needed
* Increase pagination to 100 products per page to match backend
Problem:
- Frontend was showing 12 products per page
- Backend returns 100 products per page
- Mismatch between frontend and backend pagination
Solution:
- Changed PRODUCTS_PER_PAGE from 12 to 100
- Now frontend pagination matches backend pagination
- Each page shows all 100 products from backend
Result:
- Page 1: Shows all 100 products from backend page 1
- Page 2: Would show all 100 products from backend page 2 (if implemented)
- No client-side pagination slicing within a backend page
- Simpler 1:1 mapping between frontend and backend pages
Benefits:
- β
Consistent with backend page size
- β
All products from API visible on one page
- β
Simpler pagination logic
- β
Ready for multi-page backend pagination if needed
* Fix critical type mismatches in product API transformation
Problem 1 - Images Type Mismatch:
- Backend FileListSerializer returns { id, file }[] objects
- API type incorrectly defined as string[]
- Frontend Product type expects images: string[]
- Passing objects to CardMedia would break image display
Problem 2 - Category Parent Field Mismatch:
- Frontend Category type uses 'parent' field
- Transformation was mapping to 'parentId' instead
- This left category.parent undefined everywhere
- Breaks category hierarchy and navigation
Root Cause:
- API types didn't match actual Django backend response
- FileListSerializer returns { id: string, file: string | null }
- ProductBrandAPI and ProductCategoryAPI also use FileAPI for images
- Transformation didn't extract file URLs from objects
Solution:
1. Added FileAPI interface matching FileListSerializer
2. Updated ProductAPI.images to FileAPI[]
3. Updated ProductBrandAPI.image to FileAPI | null
4. Updated ProductCategoryAPI.image to FileAPI | null
5. Extract image URLs: images.map(obj => obj.file).filter(url => url !== null)
6. Map category.image?.file to get URL string
7. Use 'parent' instead of 'parentId' to match Category type
Benefits:
- β
Images display correctly in ProductCard
- β
Category parent field populated correctly
- β
Type safety - API types match backend exactly
- β
No runtime errors from type mismatches
- β
Proper null handling for missing images
Technical Details:
- FileAPI matches storage/serializers.py FileListSerializer
- ProductListSerializer uses FileListSerializer(many=True) for images
- ProductCategoryListSerializer uses FileListSerializer for image
- Frontend Category.parent is string | null | undefined
* reverted back to original behaviour for getProductById
* Fix placeholder image to use inline SVG data URL
Problem:
- Placeholder path '/placeholder-product.png' doesn't exist in project
- Products without images would show broken image icon
- Affects ProductCard, ImageGallery, and all components using product.images[0]
- No public folder in Vite setup to serve static placeholder
Solution:
- Use inline SVG data URL for placeholder image
- Creates a simple gray box with 'No Image' text
- Always works without external dependencies
- No broken images for products without photos
Technical Details:
- Data URL: 'data:image/svg+xml,...'
- SVG: 400x400 gray (#e0e0e0) rectangle
- Text: 'No Image' centered in gray (#999)
- URL-encoded to work in data URL format
- Constant PLACEHOLDER_IMAGE for reusability
Benefits:
- β
No broken images
- β
No external file dependencies
- β
Works in all environments
- β
Lightweight (inline SVG)
- β
Professional fallback appearance
- β
No network requests for placeholder
---------
Co-authored-by: Shehryar Raza <[email protected]>
β¦mage, category and brand
β¦rtDetailSerializer
fix: Scrum 30 product detail data
fix: Scrum 30 decimal as numeric
feat: Scrum 33 cart total subtotal
* feat: integrate products API with frontend pagination
Features:
- β
Fetch products from Django backend API (GET /api/v1/products/)
- β
Transform backend response format to frontend Product type
- β
Implement frontend pagination (12 products per page)
- β
Add loading states with CircularProgress spinner
- β
Add error handling with retry button
- β
Add MUI Pagination component with first/last buttons
- β
Scroll to top on page change
- β
Update HomePage to use real API for featured products
- β
Update ShopPage to use real API with filters and sorting
Backend API Integration:
- Created ProductAPI types matching Django serializer format
- Created transformProductFromAPI() to convert backend β frontend format
- Handle backend Decimal fields (price, rating) as strings
- Handle missing fields (discountPrice, reviewCount, createdAt)
- Fallback to placeholder image if no images available
Frontend Pagination:
- Page size: 12 products per page
- Standard numbered pagination (click numbers to navigate)
- Show first/last page buttons
- Smooth scroll to top on page change
- Display total pages and current page
Files Modified:
- augment-store/client/src/config/api.ts
- Fixed BASE_URL back to http://localhost:8000/api/v1
- augment-store/client/src/features/products/types/api.ts (NEW)
- ProductAPI, ProductBrandAPI, ProductCategoryAPI types
- transformProductFromAPI() transformation function
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to fetch from backend and paginate on frontend
- Updated getProductById() to transform backend response
- Updated searchProducts() to filter on frontend (backend has no search yet)
- Updated getCategories() to extract from products
- Updated getFeaturedProducts() to use real API
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added API state (products, isLoading, error, currentPage, totalPages)
- Added useEffect to fetch products on mount and page change
- Added loading spinner
- Added error state with retry button
- Added Pagination component
- Added handlePageChange with scroll to top
- Updated filteredAndSortedProducts to use API products
- augment-store/client/src/features/products/product-list/components/HomePage.tsx
- Updated to use productService instead of mockProductService
- Added loading state
- Added empty state message
Technical Details:
- Backend returns array of products (no pagination metadata)
- Frontend implements pagination by slicing the array
- When backend adds pagination, we can easily switch to backend pagination
- Filters and sorting still work on frontend (applied after fetching)
- Price range filter updates based on fetched products
Next Steps:
- Backend can add pagination support (page, limit query params)
- Backend can add search endpoint
- Backend can add featured products endpoint
- Backend can add discount_price field to Product model
- Backend can add review_count field to Product model
* fix: update products API to handle paginated backend response
Backend API Change:
- Backend now returns paginated response format:
{
"count": 123,
"next": "url",
"previous": "url",
"results": [...]
}
Changes:
- β
Added PaginatedProductsAPI type for backend response
- β
Updated getProducts() to use paginated response
- β
Pass page and page_size params to backend
- β
Extract products from response.results
- β
Use response.count for total count
- β
Calculate totalPages from count and limit
- β
Updated searchProducts() to use paginated response
- β
Updated getCategories() to use paginated response
- β
Updated ShopPage to track and display totalCount
- β
Display total count from backend instead of filtered count
Files Modified:
- augment-store/client/src/features/products/types/api.ts
- Added PaginatedProductsAPI interface
- Updated comments for images field
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to handle paginated response
- Pass page and page_size query params
- Extract data from response.results
- Use response.count for total
- Updated searchProducts() to fetch paginated data
- Updated getCategories() to fetch paginated data
- Added error handling for all methods
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added totalCount state
- Update totalCount from API response
- Display totalCount instead of filtered count
- Reset totalCount on error
Backend Pagination:
- Backend uses Django REST Framework pagination
- Query params: ?page=1&page_size=12
- Response includes count, next, previous, results
- Frontend now properly uses backend pagination
* Remove unused page_size parameter from product API calls
Problem:
- Backend has fixed PAGE_SIZE of 100 in REST_FRAMEWORK settings
- Frontend was sending page_size parameter that was being ignored
- This created confusion about pagination behavior
- getProducts was accepting limit parameter that had no effect
Solution:
- Remove page_size from API request params
- Document that backend has fixed page_size of 100
- Use constant backendPageSize = 100 in response
- Update searchProducts to fetch all pages (since backend returns max 100 per page)
- Update getFeaturedProducts to slice from first page results
Benefits:
- β
Code accurately reflects backend behavior
- β
No misleading parameters that don't work
- β
Clear documentation of pagination constraints
- β
searchProducts now correctly fetches all products across pages
- β
Prevents confusion about why limit parameter doesn't work
* Fix brief 'No products found' flash on initial load
Problem:
- On initial page load, 'No products found' message briefly appears
- This happens before the API call completes
- Poor UX - users see error state before loading completes
Root Cause:
- Component renders with empty products array initially
- isLoading starts as false, so conditional renders 'No products found'
- Then useEffect runs and sets isLoading to true
- Creates a flash of wrong content
Solution:
- Add hasLoadedOnce flag to track if initial load completed
- Show loading spinner if isLoading OR !hasLoadedOnce
- Set hasLoadedOnce to true after first API call (success or error)
- Prevents showing 'No products found' before first load completes
Benefits:
- β
No more flash of 'No products found' on initial load
- β
Loading spinner shows immediately on mount
- β
Better UX - users see loading state, not error state
- β
Only shows 'No products found' after actually loading
* Implement client-side pagination to show 12 products per page
Problem:
- Backend returns 100 items per page (fixed in settings)
- Frontend was displaying ALL 100 items on one page
- User reported seeing only 49 items (likely due to filters)
- No pagination was actually working - all filtered products shown at once
- PRODUCTS_PER_PAGE constant was defined but never used
Root Cause:
- Component was designed for client-side pagination but not implementing it
- filteredAndSortedProducts was rendered directly without slicing
- Pagination UI was using backend totalPages (based on 100 items/page)
- Mismatch between backend pagination (100) and frontend expectation (12)
Solution:
- Implement client-side pagination with 12 items per page
- Separate apiPage (backend) from clientPage (frontend display)
- Add paginatedProducts that slices filteredAndSortedProducts
- Calculate totalClientPages based on filtered products count
- Reset clientPage to 1 when filters or sort changes
- Render paginatedProducts instead of all filteredAndSortedProducts
Implementation:
- apiPage: Backend page number (100 items per page) - currently fixed at 1
- clientPage: Frontend page number (12 items per page) - user navigates
- paginatedProducts: Slice of filteredAndSortedProducts for current page
- totalClientPages: Total pages based on filtered products / 12
Benefits:
- β
Shows 12 products per page as intended
- β
Pagination works correctly for filtered products
- β
Better UX - manageable number of products per page
- β
Pagination resets when filters change
- β
Smooth scrolling to top on page change
- β
Can handle up to 100 products from backend (page 1)
* Add debug logging to diagnose product count mismatch
Problem:
- API returns 100 products but only 49 showing on frontend
- Need to identify where products are being lost
Debug Logging Added:
1. productService.getProducts():
- Log raw API response (count, results.length, next, previous)
- Log transformed products count and first product
2. ShopPage component:
- Log API response details (productsCount, total, page, limit)
- Log pagination info (totalProducts, filteredProducts, paginatedCount)
This will help identify:
- Is API actually returning 100 products?
- Are all products being transformed correctly?
- Are filters removing products?
- Is pagination slicing correctly?
Next Steps:
- Check browser console logs
- Verify API response has 100 items
- Check if transformation is dropping products
- Check if filters are removing products
* Remove filtering and sorting logic - display all products
Problem:
- Filtering and sorting are not being handled right now
- filteredAndSortedProducts was applying price and rating filters
- This was reducing the product count unnecessarily
Changes:
- Removed filter logic (price range, rating range)
- Removed sort logic (newest, price-asc, price-desc, rating-desc)
- Pagination now works directly on products array
- Reset client page when products.length changes (not filters/sort)
Result:
- All products from API are now displayed
- Pagination shows all 100 products across pages
- No products are filtered out
- Simpler logic until filtering/sorting is implemented
Note:
- Filter UI components still exist but don't affect display
- Sort dropdown still exists but doesn't affect display
- Can re-add filtering/sorting later when needed
* Increase pagination to 100 products per page to match backend
Problem:
- Frontend was showing 12 products per page
- Backend returns 100 products per page
- Mismatch between frontend and backend pagination
Solution:
- Changed PRODUCTS_PER_PAGE from 12 to 100
- Now frontend pagination matches backend pagination
- Each page shows all 100 products from backend
Result:
- Page 1: Shows all 100 products from backend page 1
- Page 2: Would show all 100 products from backend page 2 (if implemented)
- No client-side pagination slicing within a backend page
- Simpler 1:1 mapping between frontend and backend pages
Benefits:
- β
Consistent with backend page size
- β
All products from API visible on one page
- β
Simpler pagination logic
- β
Ready for multi-page backend pagination if needed
* Fix critical type mismatches in product API transformation
Problem 1 - Images Type Mismatch:
- Backend FileListSerializer returns { id, file }[] objects
- API type incorrectly defined as string[]
- Frontend Product type expects images: string[]
- Passing objects to CardMedia would break image display
Problem 2 - Category Parent Field Mismatch:
- Frontend Category type uses 'parent' field
- Transformation was mapping to 'parentId' instead
- This left category.parent undefined everywhere
- Breaks category hierarchy and navigation
Root Cause:
- API types didn't match actual Django backend response
- FileListSerializer returns { id: string, file: string | null }
- ProductBrandAPI and ProductCategoryAPI also use FileAPI for images
- Transformation didn't extract file URLs from objects
Solution:
1. Added FileAPI interface matching FileListSerializer
2. Updated ProductAPI.images to FileAPI[]
3. Updated ProductBrandAPI.image to FileAPI | null
4. Updated ProductCategoryAPI.image to FileAPI | null
5. Extract image URLs: images.map(obj => obj.file).filter(url => url !== null)
6. Map category.image?.file to get URL string
7. Use 'parent' instead of 'parentId' to match Category type
Benefits:
- β
Images display correctly in ProductCard
- β
Category parent field populated correctly
- β
Type safety - API types match backend exactly
- β
No runtime errors from type mismatches
- β
Proper null handling for missing images
Technical Details:
- FileAPI matches storage/serializers.py FileListSerializer
- ProductListSerializer uses FileListSerializer(many=True) for images
- ProductCategoryListSerializer uses FileListSerializer for image
- Frontend Category.parent is string | null | undefined
* reverted back to original behaviour for getProductById
* Fix placeholder image to use inline SVG data URL
Problem:
- Placeholder path '/placeholder-product.png' doesn't exist in project
- Products without images would show broken image icon
- Affects ProductCard, ImageGallery, and all components using product.images[0]
- No public folder in Vite setup to serve static placeholder
Solution:
- Use inline SVG data URL for placeholder image
- Creates a simple gray box with 'No Image' text
- Always works without external dependencies
- No broken images for products without photos
Technical Details:
- Data URL: 'data:image/svg+xml,...'
- SVG: 400x400 gray (#e0e0e0) rectangle
- Text: 'No Image' centered in gray (#999)
- URL-encoded to work in data URL format
- Constant PLACEHOLDER_IMAGE for reusability
Benefits:
- β
No broken images
- β
No external file dependencies
- β
Works in all environments
- β
Lightweight (inline SVG)
- β
Professional fallback appearance
- β
No network requests for placeholder
* feat: implement product details API integration
Backend changes:
- Update ProductDetailSerializer to include nested brand, category, and images
- Now returns full objects instead of just IDs for better frontend integration
Frontend changes:
- Add ProductDetailAPI interface to match backend response structure
- Add transformProductDetailFromAPI function to transform detail response
- Update productService.getProductById to use real API endpoint
- Update ProductDetailPage to use productService instead of mockProductService
- Remove unused Alert import from ProductDetailPage
The product detail endpoint now returns:
- Full product information with timestamps (created_at, updated_at)
- Nested brand object with name, description, and image
- Nested category object with name, description, parent, and image
- Array of image objects with file URLs
- All data properly transformed to frontend format
* reverted back the changes
* Fix product detail API to return nested objects for brand, category, and images
- Updated ProductDetailSerializer to use nested serializers (ProductBrandListSerializer, ProductCategoryListSerializer, FileListSerializer)
- Previously was returning raw UUID strings which caused frontend transformation to fail
- Now returns properly nested objects matching the frontend's expected API response format
- Fixes 'Product not found' error on product detail pages
* Simplify product detail API integration - use ProductDetail type directly without transformation
* Revert interface name back to ProductDetailAPI
* Add placeholder image fallback for products without images
* Use parsed ratingNumber for consistent formatting in rating display
* removed space
---------
Co-authored-by: Shehryar Raza <[email protected]>
* feat: integrate products API with frontend pagination
Features:
- β
Fetch products from Django backend API (GET /api/v1/products/)
- β
Transform backend response format to frontend Product type
- β
Implement frontend pagination (12 products per page)
- β
Add loading states with CircularProgress spinner
- β
Add error handling with retry button
- β
Add MUI Pagination component with first/last buttons
- β
Scroll to top on page change
- β
Update HomePage to use real API for featured products
- β
Update ShopPage to use real API with filters and sorting
Backend API Integration:
- Created ProductAPI types matching Django serializer format
- Created transformProductFromAPI() to convert backend β frontend format
- Handle backend Decimal fields (price, rating) as strings
- Handle missing fields (discountPrice, reviewCount, createdAt)
- Fallback to placeholder image if no images available
Frontend Pagination:
- Page size: 12 products per page
- Standard numbered pagination (click numbers to navigate)
- Show first/last page buttons
- Smooth scroll to top on page change
- Display total pages and current page
Files Modified:
- augment-store/client/src/config/api.ts
- Fixed BASE_URL back to http://localhost:8000/api/v1
- augment-store/client/src/features/products/types/api.ts (NEW)
- ProductAPI, ProductBrandAPI, ProductCategoryAPI types
- transformProductFromAPI() transformation function
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to fetch from backend and paginate on frontend
- Updated getProductById() to transform backend response
- Updated searchProducts() to filter on frontend (backend has no search yet)
- Updated getCategories() to extract from products
- Updated getFeaturedProducts() to use real API
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added API state (products, isLoading, error, currentPage, totalPages)
- Added useEffect to fetch products on mount and page change
- Added loading spinner
- Added error state with retry button
- Added Pagination component
- Added handlePageChange with scroll to top
- Updated filteredAndSortedProducts to use API products
- augment-store/client/src/features/products/product-list/components/HomePage.tsx
- Updated to use productService instead of mockProductService
- Added loading state
- Added empty state message
Technical Details:
- Backend returns array of products (no pagination metadata)
- Frontend implements pagination by slicing the array
- When backend adds pagination, we can easily switch to backend pagination
- Filters and sorting still work on frontend (applied after fetching)
- Price range filter updates based on fetched products
Next Steps:
- Backend can add pagination support (page, limit query params)
- Backend can add search endpoint
- Backend can add featured products endpoint
- Backend can add discount_price field to Product model
- Backend can add review_count field to Product model
* fix: update products API to handle paginated backend response
Backend API Change:
- Backend now returns paginated response format:
{
"count": 123,
"next": "url",
"previous": "url",
"results": [...]
}
Changes:
- β
Added PaginatedProductsAPI type for backend response
- β
Updated getProducts() to use paginated response
- β
Pass page and page_size params to backend
- β
Extract products from response.results
- β
Use response.count for total count
- β
Calculate totalPages from count and limit
- β
Updated searchProducts() to use paginated response
- β
Updated getCategories() to use paginated response
- β
Updated ShopPage to track and display totalCount
- β
Display total count from backend instead of filtered count
Files Modified:
- augment-store/client/src/features/products/types/api.ts
- Added PaginatedProductsAPI interface
- Updated comments for images field
- augment-store/client/src/services/api/products/productService.ts
- Updated getProducts() to handle paginated response
- Pass page and page_size query params
- Extract data from response.results
- Use response.count for total
- Updated searchProducts() to fetch paginated data
- Updated getCategories() to fetch paginated data
- Added error handling for all methods
- augment-store/client/src/features/products/product-list/components/ShopPage.tsx
- Added totalCount state
- Update totalCount from API response
- Display totalCount instead of filtered count
- Reset totalCount on error
Backend Pagination:
- Backend uses Django REST Framework pagination
- Query params: ?page=1&page_size=12
- Response includes count, next, previous, results
- Frontend now properly uses backend pagination
* Remove unused page_size parameter from product API calls
Problem:
- Backend has fixed PAGE_SIZE of 100 in REST_FRAMEWORK settings
- Frontend was sending page_size parameter that was being ignored
- This created confusion about pagination behavior
- getProducts was accepting limit parameter that had no effect
Solution:
- Remove page_size from API request params
- Document that backend has fixed page_size of 100
- Use constant backendPageSize = 100 in response
- Update searchProducts to fetch all pages (since backend returns max 100 per page)
- Update getFeaturedProducts to slice from first page results
Benefits:
- β
Code accurately reflects backend behavior
- β
No misleading parameters that don't work
- β
Clear documentation of pagination constraints
- β
searchProducts now correctly fetches all products across pages
- β
Prevents confusion about why limit parameter doesn't work
* Fix brief 'No products found' flash on initial load
Problem:
- On initial page load, 'No products found' message briefly appears
- This happens before the API call completes
- Poor UX - users see error state before loading completes
Root Cause:
- Component renders with empty products array initially
- isLoading starts as false, so conditional renders 'No products found'
- Then useEffect runs and sets isLoading to true
- Creates a flash of wrong content
Solution:
- Add hasLoadedOnce flag to track if initial load completed
- Show loading spinner if isLoading OR !hasLoadedOnce
- Set hasLoadedOnce to true after first API call (success or error)
- Prevents showing 'No products found' before first load completes
Benefits:
- β
No more flash of 'No products found' on initial load
- β
Loading spinner shows immediately on mount
- β
Better UX - users see loading state, not error state
- β
Only shows 'No products found' after actually loading
* Implement client-side pagination to show 12 products per page
Problem:
- Backend returns 100 items per page (fixed in settings)
- Frontend was displaying ALL 100 items on one page
- User reported seeing only 49 items (likely due to filters)
- No pagination was actually working - all filtered products shown at once
- PRODUCTS_PER_PAGE constant was defined but never used
Root Cause:
- Component was designed for client-side pagination but not implementing it
- filteredAndSortedProducts was rendered directly without slicing
- Pagination UI was using backend totalPages (based on 100 items/page)
- Mismatch between backend pagination (100) and frontend expectation (12)
Solution:
- Implement client-side pagination with 12 items per page
- Separate apiPage (backend) from clientPage (frontend display)
- Add paginatedProducts that slices filteredAndSortedProducts
- Calculate totalClientPages based on filtered products count
- Reset clientPage to 1 when filters or sort changes
- Render paginatedProducts instead of all filteredAndSortedProducts
Implementation:
- apiPage: Backend page number (100 items per page) - currently fixed at 1
- clientPage: Frontend page number (12 items per page) - user navigates
- paginatedProducts: Slice of filteredAndSortedProducts for current page
- totalClientPages: Total pages based on filtered products / 12
Benefits:
- β
Shows 12 products per page as intended
- β
Pagination works correctly for filtered products
- β
Better UX - manageable number of products per page
- β
Pagination resets when filters change
- β
Smooth scrolling to top on page change
- β
Can handle up to 100 products from backend (page 1)
* Add debug logging to diagnose product count mismatch
Problem:
- API returns 100 products but only 49 showing on frontend
- Need to identify where products are being lost
Debug Logging Added:
1. productService.getProducts():
- Log raw API response (count, results.length, next, previous)
- Log transformed products count and first product
2. ShopPage component:
- Log API response details (productsCount, total, page, limit)
- Log pagination info (totalProducts, filteredProducts, paginatedCount)
This will help identify:
- Is API actually returning 100 products?
- Are all products being transformed correctly?
- Are filters removing products?
- Is pagination slicing correctly?
Next Steps:
- Check browser console logs
- Verify API response has 100 items
- Check if transformation is dropping products
- Check if filters are removing products
* Remove filtering and sorting logic - display all products
Problem:
- Filtering and sorting are not being handled right now
- filteredAndSortedProducts was applying price and rating filters
- This was reducing the product count unnecessarily
Changes:
- Removed filter logic (price range, rating range)
- Removed sort logic (newest, price-asc, price-desc, rating-desc)
- Pagination now works directly on products array
- Reset client page when products.length changes (not filters/sort)
Result:
- All products from API are now displayed
- Pagination shows all 100 products across pages
- No products are filtered out
- Simpler logic until filtering/sorting is implemented
Note:
- Filter UI components still exist but don't affect display
- Sort dropdown still exists but doesn't affect display
- Can re-add filtering/sorting later when needed
* Increase pagination to 100 products per page to match backend
Problem:
- Frontend was showing 12 products per page
- Backend returns 100 products per page
- Mismatch between frontend and backend pagination
Solution:
- Changed PRODUCTS_PER_PAGE from 12 to 100
- Now frontend pagination matches backend pagination
- Each page shows all 100 products from backend
Result:
- Page 1: Shows all 100 products from backend page 1
- Page 2: Would show all 100 products from backend page 2 (if implemented)
- No client-side pagination slicing within a backend page
- Simpler 1:1 mapping between frontend and backend pages
Benefits:
- β
Consistent with backend page size
- β
All products from API visible on one page
- β
Simpler pagination logic
- β
Ready for multi-page backend pagination if needed
* Fix critical type mismatches in product API transformation
Problem 1 - Images Type Mismatch:
- Backend FileListSerializer returns { id, file }[] objects
- API type incorrectly defined as string[]
- Frontend Product type expects images: string[]
- Passing objects to CardMedia would break image display
Problem 2 - Category Parent Field Mismatch:
- Frontend Category type uses 'parent' field
- Transformation was mapping to 'parentId' instead
- This left category.parent undefined everywhere
- Breaks category hierarchy and navigation
Root Cause:
- API types didn't match actual Django backend response
- FileListSerializer returns { id: string, file: string | null }
- ProductBrandAPI and ProductCategoryAPI also use FileAPI for images
- Transformation didn't extract file URLs from objects
Solution:
1. Added FileAPI interface matching FileListSerializer
2. Updated ProductAPI.images to FileAPI[]
3. Updated ProductBrandAPI.image to FileAPI | null
4. Updated ProductCategoryAPI.image to FileAPI | null
5. Extract image URLs: images.map(obj => obj.file).filter(url => url !== null)
6. Map category.image?.file to get URL string
7. Use 'parent' instead of 'parentId' to match Category type
Benefits:
- β
Images display correctly in ProductCard
- β
Category parent field populated correctly
- β
Type safety - API types match backend exactly
- β
No runtime errors from type mismatches
- β
Proper null handling for missing images
Technical Details:
- FileAPI matches storage/serializers.py FileListSerializer
- ProductListSerializer uses FileListSerializer(many=True) for images
- ProductCategoryListSerializer uses FileListSerializer for image
- Frontend Category.parent is string | null | undefined
* reverted back to original behaviour for getProductById
* Fix placeholder image to use inline SVG data URL
Problem:
- Placeholder path '/placeholder-product.png' doesn't exist in project
- Products without images would show broken image icon
- Affects ProductCard, ImageGallery, and all components using product.images[0]
- No public folder in Vite setup to serve static placeholder
Solution:
- Use inline SVG data URL for placeholder image
- Creates a simple gray box with 'No Image' text
- Always works without external dependencies
- No broken images for products without photos
Technical Details:
- Data URL: 'data:image/svg+xml,...'
- SVG: 400x400 gray (#e0e0e0) rectangle
- Text: 'No Image' centered in gray (#999)
- URL-encoded to work in data URL format
- Constant PLACEHOLDER_IMAGE for reusability
Benefits:
- β
No broken images
- β
No external file dependencies
- β
Works in all environments
- β
Lightweight (inline SVG)
- β
Professional fallback appearance
- β
No network requests for placeholder
* feat: implement add to cart API integration
- Update cart API endpoint from /cart/add to /carts/add-item
- Change AddToCartRequest interface to use product_id (snake_case)
- Update cartService.addToCart to handle no-response scenario (200/201)
- Add addItemToCart action to cart store that calls API and refetches cart
- Update ProductDetailPage to use new API integration with loading states
- Add visual feedback (loading spinner, disabled buttons) during add to cart operation
- Automatically sync cart state after adding items
The backend returns 200/201 with no response body, so the frontend
calls the API and then refetches the cart to get the updated state.
* feat: implement product details API integration
Backend changes:
- Update ProductDetailSerializer to include nested brand, category, and images
- Now returns full objects instead of just IDs for better frontend integration
Frontend changes:
- Add ProductDetailAPI interface to match backend response structure
- Add transformProductDetailFromAPI function to transform detail response
- Update productService.getProductById to use real API endpoint
- Update ProductDetailPage to use productService instead of mockProductService
- Remove unused Alert import from ProductDetailPage
The product detail endpoint now returns:
- Full product information with timestamps (created_at, updated_at)
- Nested brand object with name, description, and image
- Nested category object with name, description, parent, and image
- Array of image objects with file URLs
- All data properly transformed to frontend format
* reverted back the changes
* Fix product detail API to return nested objects for brand, category, and images
- Updated ProductDetailSerializer to use nested serializers (ProductBrandListSerializer, ProductCategoryListSerializer, FileListSerializer)
- Previously was returning raw UUID strings which caused frontend transformation to fail
- Now returns properly nested objects matching the frontend's expected API response format
- Fixes 'Product not found' error on product detail pages
* fixed the endpoint by adding a trailing slash
* removed transform function
* revert back to original code for the update and remove cart item endpoints
---------
Co-authored-by: Shehryar Raza <[email protected]>
|
@michaelos443 is attempting to deploy a commit to the dunsin's projects Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR Compliance Checks
Thank you for your Pull Request! We have run several checks on this pull request in order to make sure it's suitable for merging into this project. The results are listed in the following section.
Issue Reference In order to be considered for merging, the pull request description must refer to a specific issue number. This is described in our Contributing Guide. This check is looking for a phrase similar to: "Fixes #XYZ" or "Resolves #XYZ" where XYZ is the issue number that this PR is meant to address.
Conventional Commit PR Title
In order to be considered for merging, the pull request title must match the specification in conventional commits. You can edit the title in order for this check to pass.
Most often, our PR titles are something like one of these:
- docs: correct typo in README
- feat: implement dark mode"
- fix: correct remove button behavior
Linting Errors
- Found type "null", must be one of "feat","fix","docs","style","refactor","perf","test","build","ci","chore","revert"
- No subject found
Fixes Issue
My PR closes #issue_number_here
π¨βπ» Changes proposed(What did you do ?)
βοΈ Check List (Check all the applicable boxes)
Note to reviewers
π· Screenshots