All files / src/pages ChangeProductDetailsPage.tsx

40.98% Statements 25/61
18.18% Branches 4/22
29.41% Functions 5/17
42.85% Lines 24/56

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200                                                                          2x 7x 7x 7x 7x 7x 7x 7x 7x 7x           7x               7x 7x 7x 7x                   7x       7x 7x       7x 7x 7x                 7x                                           7x             7x                                                                                                                                                          
/**
 * @file ChangeProductDetailsPage.tsx
 * @description
 * Edit interface for updating product details (quantity and price).
 *
 * **Features:**
 * - Load product details from API
 * - Edit quantity and price fields
 * - Two-step confirmation (preview + confirmation)
 * - Auto-redirect to dashboard after successful update
 * - Language-aware help modal
 * - Role-based dashboard navigation
 *
 * **Workflow:**
 * 1. Load product by ID from URL params
 * 2. Display current quantity and price
 * 3. Allow editing both fields
 * 4. Confirm changes before submitting
 * 5. Redirect to dashboard after success
 *
 * @component
 */
 
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import ProductService from '../api/ProductService';
import { useTranslation } from 'react-i18next';
import HelpModal from '../components/HelpModal';
import Header from '../components/Header';
import Footer from '../components/Footer';
import '../styles/tailwindCustom.css';
 
/**
 * Edit product details page component
 * @component
 * @returns {JSX.Element} Product edit form
 */
const ChangeProductDetailsPage: React.FC = () => {
  const { t, i18n } = useTranslation(['translation', 'help']);
  const { productId } = useParams<{ productId: string }>();
  const navigate = useNavigate();
  const [product, setProduct] = useState<{ id: number; name: string; quantity: number; price: number } | null>(null);
  const [newQuantity, setNewQuantity] = useState<number | null>(null);
  const [newPrice, setNewPrice] = useState<number | null>(null);
  const [confirmation, setConfirmation] = useState(false);
  const [message, setMessage] = useState('');
  const [isHelpOpen, setIsHelpOpen] = useState(false);
 
  /**
   * Navigate to role-appropriate dashboard
   * Admin -> /admin, User -> /user, others -> /login
   */
  const navigateToDashboard = () => {
    const role = localStorage.getItem('role');
    if (role === 'ROLE_ADMIN') navigate('/admin');
    else if (role === 'ROLE_USER') navigate('/user');
    else navigate('/login');
  };
 
  // Load product details on component mount
  useEffect(() => {
    const fetchProductDetails = async () => {
      try {
        const productDetails = await ProductService.getProductById(Number(productId));
        setProduct(productDetails);
        setNewQuantity(productDetails.quantity);
        setNewPrice(productDetails.price);
      } catch (error) {
        console.error(t('changeProduct.error.fetch'), error);
        setMessage(t('changeProduct.errorMessage'));
      }
    };
 
    fetchProductDetails();
  }, [productId, t]);
 
  // Re-render help button on language changes
  useEffect(() => {
    const handleLanguageChange = () => {
      setIsHelpOpen((prev) => prev);
    };
 
    i18n.on('languageChanged', handleLanguageChange);
    return () => {
      i18n.off('languageChanged', handleLanguageChange);
    };
  }, [i18n]);
 
  /**
   * Submit product updates to API
   * Sends separate requests for quantity and price changes
   * Auto-redirects to dashboard after 1.5s on success
   */
  const handleSaveChanges = async () => {
    setMessage('');
    try {
      if (newQuantity !== null) {
        await ProductService.updateProductQuantity(Number(productId), newQuantity);
      }
      if (newPrice !== null) {
        await ProductService.updateProductPrice(Number(productId), newPrice);
      }
      setMessage(t('changeProduct.successMessage'));
 
      setTimeout(() => navigateToDashboard(), 1500);
    } catch (error) {
      console.error(t('changeProduct.error.update'), error);
      setMessage(t('changeProduct.errorMessage'));
    }
    setConfirmation(false);
  };
 
  /**
   * Reset form to original product values and cancel
   */
  const handleCancel = () => {
    setNewQuantity(product?.quantity || 0);
    setNewPrice(product?.price || 0);
    setConfirmation(false);
    setMessage(t('changeProduct.cancelMessage'));
  };
 
  Eif (!product) return <p>{t('loading')}</p>;
 
  return (
    <div className="flex flex-col min-h-screen bg-gray-100">
      <Header isLoggedIn={true} onLogout={() => navigate('/login')} />
 
      <div className="absolute top-4 left-1/2 transform -translate-x-1/2">
        <button
          onClick={() => setIsHelpOpen(true)}
          className="button-secondary"
          key={i18n.language}
        >
          {t('button', { ns: 'help' })}
        </button>
      </div>
 
      <HelpModal isOpen={isHelpOpen} onClose={() => setIsHelpOpen(false)} pageKey="changeProduct" />
 
      <main className="w-full max-w-2xl p-6 bg-white shadow-lg rounded mx-auto mt-6">
        <h2 className="text-2xl font-bold mb-4">{product.name}</h2>
 
        <div className="mb-4">
          <label className="block font-medium mb-2">{t('changeProduct.quantityLabel')}</label>
          <input
            type="number"
            value={newQuantity ?? product.quantity}
            onChange={(e) => setNewQuantity(Number(e.target.value))}
            className="input-field"
          />
        </div>
 
        <div className="mb-4">
          <label className="block font-medium mb-2">{t('changeProduct.priceLabel')}</label>
          <input
            type="number"
            step="0.01"
            value={newPrice ?? product.price}
            onChange={(e) => setNewPrice(parseFloat(e.target.value))}
            className="input-field"
          />
        </div>
 
        <div className="flex justify-between space-x-4">
          <button className="button-confirmation button-confirmation-no" onClick={handleCancel}>
            {t('changeProduct.cancelButton')}
          </button>
          <button className="button-confirmation button-confirmation-yes" onClick={() => setConfirmation(true)}>
            {t('changeProduct.saveButton')}
          </button>
        </div>
 
        {confirmation && (
          <div className="confirmation-box mt-4">
            <p>{t('changeProduct.confirmationMessage')}</p>
            <div className="flex justify-between space-x-4">
              <button
                className="button-confirmation button-confirmation-yes"
                onClick={handleSaveChanges}
              >
                {t('changeProduct.confirmYes')}
              </button>
              <button className="button-confirmation button-confirmation-no" onClick={handleCancel}>
                {t('changeProduct.confirmNo')}
              </button>
            </div>
          </div>
        )}
 
        {message && <p className="mt-4 text-blue-500 font-semibold">{message}</p>}
      </main>
 
      <Footer />
    </div>
  );
};
 
export default ChangeProductDetailsPage;