Untitled

 avatar
unknown
plain_text
11 days ago
36 kB
2
Indexable
import React, {
  useState,
  useMemo,
  useCallback,
  useEffect,
  FC,
} from "react";

// --- Global Styles for Theming, Animations, and Responsive Design ---
const GlobalStyles: FC = () => (
  <style>
    {`
      :root {
        --primary-color: #4CAF50;
        --secondary-color: #FFC107;
        --light-bg: #f5f7fa;
        --light-surface: #ffffff;
        --light-text: #333333;
        --dark-bg: #2c3e50;
        --dark-surface: #34495e;
        --dark-text: #ecf0f1;
        --accent-bg: #1abc9c;
        --card-bg-light: #ffffff;
        --card-bg-dark: #34495e;
        --error-color: #e74c3c;
      }
      @keyframes fadeIn {
        from { opacity: 0; transform: translateY(20px); }
        to { opacity: 1; transform: translateY(0); }
      }
      body {
        font-family: 'Roboto', sans-serif;
        margin: 0;
      }
      /* Global button styling */
      .button {
        padding: 10px 16px;
        font-size: 0.95rem;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        transition: background-color 0.3s ease, transform 0.3s ease;
      }
      .button:hover,
      .button:focus {
        transform: scale(1.05);
        outline: 2px solid var(--primary-color);
      }
      /* Toast styles */
      .toast {
        position: fixed;
        bottom: 30px;
        left: 50%;
        transform: translateX(-50%);
        background: rgba(0,0,0,0.85);
        color: #fff;
        padding: 12px 24px;
        border-radius: 6px;
        animation: fadeIn 0.5s ease;
        z-index: 1000;
      }
      /* Keypad button hover */
      .keypad-button:hover {
        transform: scale(1.1);
      }
      /* Quote card hover */
      .quote-card:hover {
        transform: translateY(-4px);
      }
      /* Responsive adjustments */
      @media (max-width: 768px) {
        .main-wrapper {
          padding: 12px;
        }
      }
    `}
  </style>
);

// --- Type Definitions ---
interface FormData {
  goldRate: string;
  goldWeight: string;
  labourCost: string;
  labourPercentage: string;
  diamondPricePerCarat: string;
  diamondWeight: string;
  gstTax: string;
  miscCost: string;
  stone1Price: string;
  stone1Weight: string;
  stone2Price: string;
  stone2Weight: string;
  goldPercentage14K: string;
  goldPercentage18K: string;
  goldPercentage22K: string;
  // New fields for Diamond Options
  diamondColor: string;
  diamondClarity: string;
}

interface Breakdown {
  baseGoldCost: number;
  labourPercentageCost: number;
  fixedLabourCost: number;
  additionalFixedCosts: number;
  diamondCost: number;
  totalBeforeGST: number;
  finalTotal: number;
}

interface SavedCalculation {
  id: string;
  timestamp: number;
  formData: FormData;
}

const GOLD_RATE_DIVISOR = 10;

// --- Helper Functions ---
const toNum = (val: string): number => parseFloat(val) || 0;

export const calculateBreakdown = (
  formData: FormData,
  purityFactor: number
): Breakdown => {
  const {
    goldRate,
    goldWeight,
    labourCost,
    labourPercentage,
    diamondPricePerCarat,
    diamondWeight,
    gstTax,
    miscCost,
    stone1Price,
    stone1Weight,
    stone2Price,
    stone2Weight,
  } = formData;

  const numGoldRate = toNum(goldRate);
  const numGoldWeight = toNum(goldWeight);
  const numLabourCost = toNum(labourCost);
  const numLabourPercentage = toNum(labourPercentage);
  const numDiamondPricePerCarat = toNum(diamondPricePerCarat);
  const numDiamondWeight = toNum(diamondWeight);
  const numGSTTax = toNum(gstTax);
  const numMiscCost = toNum(miscCost);
  const numStone1Price = toNum(stone1Price);
  const numStone1Weight = toNum(stone1Weight);
  const numStone2Price = toNum(stone2Price);
  const numStone2Weight = toNum(stone2Weight);

  const stone1Cost = numStone1Price * numStone1Weight;
  const stone2Cost = numStone2Price * numStone2Weight;
  const additionalFixedCosts = stone1Cost + stone2Cost + numMiscCost;
  const totalDiamondCost = numDiamondPricePerCarat * numDiamondWeight;
  const perGramPrice = numGoldRate / GOLD_RATE_DIVISOR;
  const baseGoldCost = perGramPrice * purityFactor * numGoldWeight;
  const labourPercentageCost = baseGoldCost * (numLabourPercentage / 100);
  const fixedLabourCostTotal = numLabourCost * numGoldWeight;
  const totalBeforeGST =
    baseGoldCost +
    labourPercentageCost +
    fixedLabourCostTotal +
    additionalFixedCosts +
    totalDiamondCost;
  const finalTotal = totalBeforeGST * (1 + numGSTTax / 100);

  return {
    baseGoldCost,
    labourPercentageCost,
    fixedLabourCost: fixedLabourCostTotal,
    additionalFixedCosts,
    diamondCost: totalDiamondCost,
    totalBeforeGST,
    finalTotal,
  };
};

// --- Dynamic Styles Generator ---
const getStyles = (darkMode: boolean): { [key: string]: React.CSSProperties } => ({
  pageWrapper: {
    backgroundColor: darkMode ? "var(--dark-bg)" : "var(--light-bg)",
    minHeight: "100vh",
    padding: "20px",
    boxSizing: "border-box",
    overflowY: "auto",
    color: darkMode ? "var(--dark-text)" : "var(--light-text)",
    transition: "background-color 0.3s ease, color 0.3s ease",
  },
  mainWrapper: {
    width: "100%",
    maxWidth: "1200px",
    margin: "0 auto",
    display: "flex",
    flexDirection: "column",
    gap: "20px",
  },
  topSection: {
    display: "flex",
    flexWrap: "wrap",
    gap: "20px",
  },
  container: {
    flex: 3,
    backgroundColor: darkMode ? "var(--dark-surface)" : "var(--light-surface)",
    borderRadius: "8px",
    boxShadow: darkMode
      ? "0 3px 10px rgba(0,0,0,0.3)"
      : "0 3px 10px rgba(0,0,0,0.1)",
    padding: "20px",
    overflow: "hidden",
    transition: "all 0.3s ease",
  },
  sidebar: {
    flex: 1,
    display: "flex",
    flexDirection: "column",
    gap: "20px",
    alignItems: "center",
    justifyContent: "flex-start",
  },
  bottomSection: {
    backgroundColor: darkMode ? "var(--dark-surface)" : "var(--light-surface)",
    borderRadius: "8px",
    padding: "16px",
    boxShadow: darkMode
      ? "0 3px 10px rgba(0,0,0,0.3)"
      : "0 3px 10px rgba(0,0,0,0.1)",
    overflow: "hidden",
    transition: "all 0.3s ease",
  },
  title: {
    textAlign: "center",
    marginBottom: "20px",
    fontSize: "1.8rem",
    fontWeight: 700,
  },
  columns: {
    display: "flex",
    gap: "20px",
    flexWrap: "wrap",
  },
  card: {
    flex: 1,
    backgroundColor: darkMode ? "var(--card-bg-dark)" : "var(--card-bg-light)",
    borderRadius: "8px",
    padding: "16px",
    boxShadow: darkMode
      ? "0 2px 6px rgba(0,0,0,0.3)"
      : "0 2px 6px rgba(0,0,0,0.1)",
    transition: "all 0.3s ease",
  },
  cardTitle: {
    marginBottom: "12px",
    fontSize: "1.1rem",
    fontWeight: 600,
    borderBottom: darkMode ? "1px solid #555" : "1px solid #e0e0e0",
    paddingBottom: "6px",
  },
  grid: {
    display: "grid",
    gridTemplateColumns: "repeat(auto-fit, minmax(140px, 1fr))",
    gap: "16px",
  },
  label: {
    display: "block",
    marginBottom: "6px",
    fontSize: "0.8rem",
    fontWeight: 500,
  },
  input: {
    width: "100%",
    padding: "8px",
    border: "1px solid #ccc",
    borderRadius: "4px",
    fontSize: "0.85rem",
    backgroundColor: darkMode ? "#555" : "#fff",
    color: darkMode ? "#fff" : "#333",
    transition: "border-color 0.3s ease",
  },
  toggleButton: {
    backgroundColor: darkMode ? "var(--accent-bg)" : "var(--secondary-color)",
    color: "#fff",
    padding: "10px 16px",
    border: "none",
    borderRadius: "4px",
    cursor: "pointer",
    transition: "background-color 0.3s ease",
  },
  keypadContainer: {
    minWidth: "140px",
    backgroundColor: darkMode ? "var(--dark-surface)" : "var(--light-surface)",
    borderRadius: "8px",
    boxShadow: darkMode
      ? "0 3px 10px rgba(0,0,0,0.3)"
      : "0 3px 10px rgba(0,0,0,0.1)",
    padding: "16px",
    transition: "all 0.3s ease",
  },
  keypadRow: {
    display: "flex",
    justifyContent: "space-between",
    marginBottom: "10px",
  },
  keypadButton: {
    flex: 1,
    margin: "0 5px",
    padding: "10px",
    fontSize: "0.95rem",
    border: "1px solid #ccc",
    borderRadius: "4px",
    backgroundColor: darkMode ? "#555" : "#f3f4f6",
    cursor: "pointer",
    color: darkMode ? "#fff" : "#333",
    transition: "background-color 0.3s ease, transform 0.3s ease",
  },
  sectionHeading: {
    marginTop: "16px",
    marginBottom: "8px",
    fontSize: "1rem",
    fontWeight: 600,
  },
  tableWrapper: {
    overflowX: "auto",
    marginTop: "16px",
  },
  table: {
    width: "100%",
    borderCollapse: "collapse",
    fontSize: "0.85rem",
  },
  th: {
    textAlign: "left",
    borderBottom: "1px solid #ddd",
    padding: "8px",
    backgroundColor: darkMode ? "#555" : "#f3f4f6",
    fontWeight: 600,
  },
  td: {
    padding: "8px",
    borderBottom: "1px solid #eee",
  },
  note: {
    marginTop: "8px",
    fontStyle: "italic",
    textAlign: "center",
    fontSize: "0.75rem",
  },
  quoteCard: {
    backgroundColor: darkMode ? "var(--card-bg-dark)" : "var(--card-bg-light)",
    borderRadius: "8px",
    padding: "20px",
    width: "320px",
    boxShadow: darkMode
      ? "0 2px 6px rgba(0,0,0,0.3)"
      : "0 2px 6px rgba(0,0,0,0.1)",
    animation: "fadeIn 0.5s ease",
    transition: "transform 0.3s ease",
  },
});

// --- Input Field Component ---
interface InputFieldProps {
  label: string;
  name: keyof FormData;
  value: string;
  onChange: (name: keyof FormData, value: string) => void;
  onFocus: (name: keyof FormData) => void;
  styles: ReturnType<typeof getStyles>;
}

const InputField: FC<InputFieldProps> = React.memo(
  ({ label, name, value, onChange, onFocus, styles }) => {
    const inputId = `input-${name}`;
    return (
      <div>
        <label htmlFor={inputId} style={styles.label}>
          {label}
        </label>
        <input
          id={inputId}
          type="number"
          value={value}
          onChange={(e) => onChange(name, e.target.value)}
          onFocus={() => onFocus(name)}
          style={styles.input}
          aria-label={label}
        />
      </div>
    );
  }
);

// --- New Select Field Component ---
interface SelectFieldProps {
  label: string;
  name: keyof FormData;
  value: string;
  options: string[];
  onChange: (name: keyof FormData, value: string) => void;
  onFocus: (name: keyof FormData) => void;
  styles: ReturnType<typeof getStyles>;
}

const SelectField: FC<SelectFieldProps> = React.memo(
  ({ label, name, value, options, onChange, onFocus, styles }) => {
    const selectId = `select-${name}`;
    return (
      <div>
        <label htmlFor={selectId} style={styles.label}>
          {label}
        </label>
        <select
          id={selectId}
          value={value}
          onChange={(e) => onChange(name, e.target.value)}
          onFocus={() => onFocus(name)}
          style={styles.input}
          aria-label={label}
        >
          {options.map((opt) => (
            <option key={opt} value={opt}>
              {opt}
            </option>
          ))}
        </select>
      </div>
    );
  }
);

// --- Keypad Component ---
interface KeypadProps {
  onKeyPress: (key: string) => void;
  styles: ReturnType<typeof getStyles>;
}

const Keypad: FC<KeypadProps> = React.memo(({ onKeyPress, styles }) => {
  const keypadRows = [
    ["7", "8", "9"],
    ["4", "5", "6"],
    ["1", "2", "3"],
    ["0", ".", "⌫"],
  ];

  return (
    <div style={styles.keypadContainer} role="group" aria-label="Custom Keypad">
      <h3 style={{ textAlign: "center", marginBottom: "10px", fontSize: "0.95rem" }}>
        Keypad
      </h3>
      {keypadRows.map((row, rowIndex) => (
        <div key={rowIndex} style={styles.keypadRow}>
          {row.map((key) => (
            <button
              key={key}
              className="keypad-button button"
              style={styles.keypadButton}
              onClick={() => onKeyPress(key)}
              aria-label={`Key ${key}`}
            >
              {key}
            </button>
          ))}
        </div>
      ))}
    </div>
  );
});

// --- Cost Breakdown Table Component ---
interface CostBreakdownTableProps {
  breakdown14K: Breakdown;
  breakdown18K: Breakdown;
  breakdown22K: Breakdown;
  styles: ReturnType<typeof getStyles>;
  formData: FormData;
  onGoldPercentageChange: (
    key: "goldPercentage14K" | "goldPercentage18K" | "goldPercentage22K",
    value: string
  ) => void;
}

const CostBreakdownTable: FC<CostBreakdownTableProps> = React.memo(
  ({ breakdown14K, breakdown18K, breakdown22K, styles, formData, onGoldPercentageChange }) => (
    <div style={styles.tableWrapper}>
      <h3 style={{ textAlign: "center", marginBottom: "12px", fontSize: "1rem" }}>
        Cost Breakdown
      </h3>
      <table style={styles.table}>
        <thead>
          <tr>
            <th style={styles.th}>Karat</th>
            <th style={styles.th}>Gold %</th>
            <th style={styles.th}>Base Gold</th>
            <th style={styles.th}>Labour %</th>
            <th style={styles.th}>Fixed Labour</th>
            <th style={styles.th}>Add. Costs</th>
            <th style={styles.th}>Diamond</th>
            <th style={styles.th}>Total (GST)</th>
          </tr>
        </thead>
        <tbody>
          {[
            { karat: "14K", breakdown: breakdown14K, key: "goldPercentage14K" as const },
            { karat: "18K", breakdown: breakdown18K, key: "goldPercentage18K" as const },
            { karat: "22K", breakdown: breakdown22K, key: "goldPercentage22K" as const },
          ].map(({ karat, breakdown, key }) => (
            <tr key={karat}>
              <td style={styles.td}>{karat}</td>
              <td style={styles.td}>
                <input
                  type="number"
                  value={formData[key]}
                  onChange={(e) => onGoldPercentageChange(key, e.target.value)}
                  style={{ width: "50px", fontSize: "0.85rem" }}
                  aria-label={`Gold percentage for ${karat}`}
                />
              </td>
              <td style={styles.td}>{breakdown.baseGoldCost.toFixed(2)}</td>
              <td style={styles.td}>{breakdown.labourPercentageCost.toFixed(2)}</td>
              <td style={styles.td}>{breakdown.fixedLabourCost.toFixed(2)}</td>
              <td style={styles.td}>{breakdown.additionalFixedCosts.toFixed(2)}</td>
              <td style={styles.td}>{breakdown.diamondCost.toFixed(2)}</td>
              <td style={styles.td}>
                <strong>{breakdown.finalTotal.toFixed(2)}</strong>
              </td>
            </tr>
          ))}
        </tbody>
      </table>
      <p style={styles.note}>
        Note: Set Fixed Labour Cost to 0 if applying only percentage-based labour.
      </p>
    </div>
  )
);

// --- Quote Card Component ---
interface QuoteCardProps {
  karat: string;
  breakdown: Breakdown;
  formData: FormData;
  styles: ReturnType<typeof getStyles>;
  onToast: (msg: string) => void;
}

const QuoteCard: FC<QuoteCardProps> = React.memo(
  ({ karat, breakdown, formData, styles, onToast }) => {
    const quoteDate = new Date().toLocaleDateString();
    const quoteNumber = useMemo(
      () => Math.floor(100000 + Math.random() * 900000),
      []
    );

    const copyQuote = useCallback(() => {
      const text = `
Jewellery Quote - ${karat}
Date: ${quoteDate} | Quote #: ${quoteNumber}

Name: Jewellery
Karat: ${karat}
Gold Weight: ${formData.goldWeight} g
Diamond Weight: ${formData.diamondWeight} ct
Diamond Clarity: ${formData.diamondClarity}
Diamond Color: ${formData.diamondColor}
Total: ${breakdown.finalTotal.toFixed(2)}
      `;
      navigator.clipboard.writeText(text);
      onToast(`Quote for ${karat} copied to clipboard!`);
    }, [karat, quoteDate, quoteNumber, formData, breakdown, onToast]);

    return (
      <div className="quote-card" style={styles.quoteCard}>
        <div style={{ borderBottom: "1px solid", marginBottom: "12px", paddingBottom: "8px" }}>
          <h4 style={{ textAlign: "center", margin: 0 }}>
            Jewellery Quote - {karat}
          </h4>
          <p style={{ textAlign: "center", fontSize: "0.8rem", margin: 0 }}>
            Date: {quoteDate} | Quote #: {quoteNumber}
          </p>
        </div>
        <table style={{ width: "100%", fontSize: "0.85rem", marginBottom: "12px" }}>
          <tbody>
            <tr>
              <td><strong>Name:</strong></td>
              <td>Jewellery</td>
            </tr>
            <tr>
              <td><strong>Karat:</strong></td>
              <td>{karat}</td>
            </tr>
            <tr>
              <td><strong>Gold Weight:</strong></td>
              <td>{formData.goldWeight} g</td>
            </tr>
            <tr>
              <td><strong>Diamond Weight:</strong></td>
              <td>{formData.diamondWeight} ct</td>
            </tr>
            <tr>
              <td><strong>Diamond Clarity:</strong></td>
              <td>{formData.diamondClarity}</td>
            </tr>
            <tr>
              <td><strong>Diamond Color:</strong></td>
              <td>{formData.diamondColor}</td>
            </tr>
          </tbody>
        </table>
        <div style={{ borderTop: "1px solid", paddingTop: "8px", textAlign: "center" }}>
          <p style={{ fontWeight: "bold", fontSize: "1rem", margin: 0 }}>
            Total: {breakdown.finalTotal.toFixed(2)}
          </p>
        </div>
        <div style={{ textAlign: "center", marginTop: "12px" }}>
          <button
            className="button"
            onClick={copyQuote}
            style={{ backgroundColor: "var(--primary-color)", color: "#fff" }}
            aria-label="Copy Quote"
          >
            Copy Quote
          </button>
        </div>
      </div>
    );
  }
);

// --- Quote Section Component ---
interface QuoteSectionProps {
  breakdown14K: Breakdown;
  breakdown18K: Breakdown;
  breakdown22K: Breakdown;
  formData: FormData;
  styles: ReturnType<typeof getStyles>;
  onToast: (msg: string) => void;
}

const QuoteSection: FC<QuoteSectionProps> = ({
  breakdown14K,
  breakdown18K,
  breakdown22K,
  formData,
  styles,
  onToast,
}) => (
  <div style={{ marginTop: "20px" }}>
    <h3 style={{ textAlign: "center", marginBottom: "12px" }}>Quote Summary</h3>
    <div style={{ display: "flex", justifyContent: "center", gap: "20px", flexWrap: "wrap" }}>
      <QuoteCard karat="14K" breakdown={breakdown14K} formData={formData} styles={styles} onToast={onToast} />
      <QuoteCard karat="18K" breakdown={breakdown18K} formData={formData} styles={styles} onToast={onToast} />
      <QuoteCard karat="22K" breakdown={breakdown22K} formData={formData} styles={styles} onToast={onToast} />
    </div>
  </div>
);

// --- Toast Component ---
interface ToastProps {
  message: string;
}
const Toast: FC<ToastProps> = ({ message }) => (
  <div className="toast" role="alert">
    {message}
  </div>
);

const JewelleryPriceCalculator: FC = () => {
  const [darkMode, setDarkMode] = useState(false);
  const styles = useMemo(() => getStyles(darkMode), [darkMode]);
  const [formData, setFormData] = useState<FormData>({
    goldRate: "60000",
    goldWeight: "0",
    labourCost: "0",
    labourPercentage: "0",
    diamondPricePerCarat: "10000",
    diamondWeight: "0",
    gstTax: "0",
    miscCost: "0",
    stone1Price: "0",
    stone1Weight: "0",
    stone2Price: "0",
    stone2Weight: "0",
    goldPercentage14K: "60",
    goldPercentage18K: "75",
    goldPercentage22K: "92",
    // New fields default values
    diamondColor: "F-G",
    diamondClarity: "VVS1",
  });
  const [activeInput, setActiveInput] = useState<keyof FormData | null>(null);
  const [savedCalculations, setSavedCalculations] = useState<SavedCalculation[]>([]);
  const [selectedCalcIds, setSelectedCalcIds] = useState<string[]>([]);
  const [toastMessage, setToastMessage] = useState("");

  // --- Toast handler ---
  const showToast = useCallback((msg: string) => {
    setToastMessage(msg);
    setTimeout(() => setToastMessage(""), 3000);
  }, []);

  // --- Load saved calculations on mount ---
  useEffect(() => {
    const storedCalculations = localStorage.getItem("pastCalculations");
    if (storedCalculations) {
      setSavedCalculations(JSON.parse(storedCalculations));
    }
  }, []);

  const handleChange = useCallback((name: keyof FormData, value: string) => {
    setFormData((prev) => ({ ...prev, [name]: value }));
  }, []);

  const handleActiveInput = useCallback((name: keyof FormData) => {
    setActiveInput(name);
  }, []);

  const handleKeypadInput = useCallback(
    (key: string) => {
      if (!activeInput) return;
      let currentVal = formData[activeInput];
      if (key === ".") {
        if (currentVal.includes(".")) return;
        currentVal = currentVal || "0";
      } else if (currentVal === "0") {
        currentVal = "";
      }
      handleChange(activeInput, currentVal + key);
    },
    [activeInput, formData, handleChange]
  );

  const handleBackspace = useCallback(() => {
    if (!activeInput) return;
    const currentVal = formData[activeInput];
    const newValue = currentVal.slice(0, -1) || "0";
    handleChange(activeInput, newValue);
  }, [activeInput, formData, handleChange]);

  const handleKeypadKey = useCallback(
    (key: string) => (key === "⌫" ? handleBackspace() : handleKeypadInput(key)),
    [handleBackspace, handleKeypadInput]
  );

  const handleGoldPercentageChange = useCallback(
    (
      key: "goldPercentage14K" | "goldPercentage18K" | "goldPercentage22K",
      value: string
    ) => {
      setFormData((prev) => ({ ...prev, [key]: value }));
    },
    []
  );

  const breakdown14K = useMemo(
    () =>
      calculateBreakdown(formData, toNum(formData.goldPercentage14K) / 100),
    [formData]
  );
  const breakdown18K = useMemo(
    () =>
      calculateBreakdown(formData, toNum(formData.goldPercentage18K) / 100),
    [formData]
  );
  const breakdown22K = useMemo(
    () =>
      calculateBreakdown(formData, toNum(formData.goldPercentage22K) / 100),
    [formData]
  );

  const saveCalculation = useCallback(() => {
    const newCalc: SavedCalculation = {
      id: Date.now().toString(),
      timestamp: Date.now(),
      formData: { ...formData },
    };
    const updatedCalculations = [...savedCalculations, newCalc];
    setSavedCalculations(updatedCalculations);
    localStorage.setItem("pastCalculations", JSON.stringify(updatedCalculations));
  }, [formData, savedCalculations]);

  const loadCalculation = useCallback((calc: SavedCalculation) => {
    setFormData(calc.formData);
  }, []);

  const deleteCalculation = useCallback(
    (id: string) => {
      const updatedCalculations = savedCalculations.filter((calc) => calc.id !== id);
      setSavedCalculations(updatedCalculations);
      localStorage.setItem("pastCalculations", JSON.stringify(updatedCalculations));
      setSelectedCalcIds((prev) => prev.filter((calcId) => calcId !== id));
    },
    [savedCalculations]
  );

  const deleteSelectedCalculations = useCallback(() => {
    const updatedCalculations = savedCalculations.filter(
      (calc) => !selectedCalcIds.includes(calc.id)
    );
    setSavedCalculations(updatedCalculations);
    localStorage.setItem("pastCalculations", JSON.stringify(updatedCalculations));
    setSelectedCalcIds([]);
  }, [savedCalculations, selectedCalcIds]);

  const clearAllCalculations = useCallback(() => {
    setSavedCalculations([]);
    localStorage.removeItem("pastCalculations");
    setSelectedCalcIds([]);
  }, []);

  const toggleSelectCalculation = useCallback((id: string) => {
    setSelectedCalcIds((prev) =>
      prev.includes(id) ? prev.filter((calcId) => calcId !== id) : [...prev, id]
    );
  }, []);

  const toggleSelectAll = useCallback(() => {
    setSelectedCalcIds((prev) =>
      prev.length === savedCalculations.length ? [] : savedCalculations.map((calc) => calc.id)
    );
  }, [savedCalculations]);

  // Options for diamond color and clarity
  const diamondColorOptions = ["D", "E", "F", "G", "H", "D-E", "E-F", "F-G", "G-H", "H-I"];
  const diamondClarityOptions = [
    "FL",
    "IF",
    "VVS1",
    "VVS1-2",
    "VVS2",
    "VVS-VS",
    "VS1",
    "VS1-VS2",
    "VS2",
    "VS-SI",
    "SI1",
    "SI1-2",
    "SI2",
    "SI",
    "SI3"
  ];

  return (
    <>
      <GlobalStyles />
      <div style={styles.pageWrapper}>
        <div style={styles.mainWrapper} className="main-wrapper">
          {/* Top Section: Form and Keypad */}
          <header style={styles.topSection}>
            <div style={styles.container}>
              <h2 style={styles.title}>Jewellery Price Calculator</h2>
              <div style={styles.columns}>
                <div style={styles.card}>
                  <div style={styles.cardTitle}>Gold & Diamond Info</div>
                  <div style={styles.grid}>
                    <InputField
                      label="24K GOLD RATE (per 10g):"
                      name="goldRate"
                      value={formData.goldRate}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Gold Weight (g):"
                      name="goldWeight"
                      value={formData.goldWeight}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Fixed Labour (per g):"
                      name="labourCost"
                      value={formData.labourCost}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Labour %:"
                      name="labourPercentage"
                      value={formData.labourPercentage}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Diamond Price/Carat:"
                      name="diamondPricePerCarat"
                      value={formData.diamondPricePerCarat}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Diamond Weight (ct):"
                      name="diamondWeight"
                      value={formData.diamondWeight}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    {/* New Select Fields for Diamond Options */}
                    <SelectField
                      label="Diamond Color:"
                      name="diamondColor"
                      value={formData.diamondColor}
                      options={diamondColorOptions}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <SelectField
                      label="Diamond Clarity:"
                      name="diamondClarity"
                      value={formData.diamondClarity}
                      options={diamondClarityOptions}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                  </div>
                </div>
                <div style={styles.card}>
                  <div style={styles.cardTitle}>Additional Costs</div>
                  <div style={styles.grid}>
                    <InputField
                      label="GST Tax (%):"
                      name="gstTax"
                      value={formData.gstTax}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Misc Cost:"
                      name="miscCost"
                      value={formData.miscCost}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                  </div>
                  <div style={styles.sectionHeading}>Stone 1</div>
                  <div style={styles.grid}>
                    <InputField
                      label="Price/Carat:"
                      name="stone1Price"
                      value={formData.stone1Price}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Weight (ct):"
                      name="stone1Weight"
                      value={formData.stone1Weight}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                  </div>
                  <div style={styles.sectionHeading}>Stone 2</div>
                  <div style={styles.grid}>
                    <InputField
                      label="Price/Carat:"
                      name="stone2Price"
                      value={formData.stone2Price}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                    <InputField
                      label="Weight (ct):"
                      name="stone2Weight"
                      value={formData.stone2Weight}
                      onChange={handleChange}
                      onFocus={handleActiveInput}
                      styles={styles}
                    />
                  </div>
                </div>
              </div>
            </div>
            <aside style={styles.sidebar}>
              <button
                className="button"
                style={styles.toggleButton}
                onClick={() => setDarkMode((prev) => !prev)}
                aria-label="Toggle dark/light mode"
              >
                {darkMode ? "☀️ Light Mode" : "🌙 Dark Mode"}
              </button>
              <Keypad onKeyPress={handleKeypadKey} styles={styles} />
            </aside>
          </header>
          {/* Bottom Section: Cost Breakdown */}
          <section style={styles.bottomSection}>
            <CostBreakdownTable
              breakdown14K={breakdown14K}
              breakdown18K={breakdown18K}
              breakdown22K={breakdown22K}
              styles={styles}
              formData={formData}
              onGoldPercentageChange={handleGoldPercentageChange}
            />
          </section>
          {/* Quote Summary */}
          <section>
            <QuoteSection
              breakdown14K={breakdown14K}
              breakdown18K={breakdown18K}
              breakdown22K={breakdown22K}
              formData={formData}
              styles={styles}
              onToast={showToast}
            />
          </section>
          {/* Saved Calculations Section */}
          <section
            style={{
              marginTop: "20px",
              padding: "16px",
              backgroundColor: darkMode ? "var(--dark-surface)" : "var(--light-surface)",
              borderRadius: "8px",
            }}
          >
            <h3 style={{ textAlign: "center", marginBottom: "12px" }}>
              Past Calculations
            </h3>
            <div
              style={{
                marginBottom: "12px",
                display: "flex",
                gap: "12px",
                justifyContent: "center",
                flexWrap: "wrap",
              }}
            >
              <button onClick={saveCalculation} className="button">
                Save Current Calculation
              </button>
              <button onClick={clearAllCalculations} className="button">
                Clear All
              </button>
              <button onClick={toggleSelectAll} className="button">
                {selectedCalcIds.length === savedCalculations.length
                  ? "Deselect All"
                  : "Select All"}
              </button>
              <button
                onClick={deleteSelectedCalculations}
                className="button"
                disabled={selectedCalcIds.length === 0}
              >
                Delete Selected
              </button>
            </div>
            {savedCalculations.length > 0 ? (
              <ul style={{ listStyleType: "none", padding: 0 }}>
                {savedCalculations.map((calc) => (
                  <li
                    key={calc.id}
                    style={{
                      marginBottom: "8px",
                      borderBottom: "1px solid",
                      paddingBottom: "4px",
                      display: "flex",
                      alignItems: "center",
                      justifyContent: "space-between",
                    }}
                  >
                    <div style={{ display: "flex", alignItems: "center" }}>
                      <input
                        type="checkbox"
                        checked={selectedCalcIds.includes(calc.id)}
                        onChange={() => toggleSelectCalculation(calc.id)}
                        aria-label="Select calculation"
                      />
                      <span style={{ marginLeft: "8px" }}>
                        {new Date(calc.timestamp).toLocaleString()}
                      </span>
                    </div>
                    <div>
                      <button onClick={() => loadCalculation(calc)} className="button">
                        Load
                      </button>
                      <button
                        onClick={() => deleteCalculation(calc.id)}
                        className="button"
                        style={{ marginLeft: "10px" }}
                      >
                        Delete
                      </button>
                    </div>
                  </li>
                ))}
              </ul>
            ) : (
              <p style={{ textAlign: "center" }}>No saved calculations.</p>
            )}
          </section>
        </div>
        {toastMessage && <Toast message={toastMessage} />}
      </div>
    </>
  );
};

export default JewelleryPriceCalculator;
Editor is loading...
Leave a Comment