Dynamic price filter no overlapping deals

This is the updated code for

import React, { useState, useCallback, useEffect, useMemo } from 'react';
// Make sure these paths are correct for your project setup
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { AlertCircle } from "lucide-react";
import { motion, AnimatePresence } from 'framer-motion';
import { Checkbox } from "@/components/ui/checkbox";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";

// --- Constants ---
const MESSAGE_TIMEOUT_MS = 3000;
const PERCENTAGE_FACTOR = 100;

// --- Types ---
interface Deal {
    id: string;
    max_price: number;
}

interface DealCheckerDemoProps {
    initialOverDeviation?: number;
    initialUnderDeviation?: number;
    initialDeals?: Deal[];
    allowAddDeals?: boolean;
    allowCheckDeals?: boolean;
    defaultMinPricePercentDecimal?: number;
    customValidation?: (price: number) => string | null;
    maxPriceLabel?: string;
    minPriceLabel?: string;
    allowMinPriceConfiguration?: boolean;
    overDeviationLabel?: string;
    underDeviationLabel?: string;
    allowOverUnderDeviationConfiguration?: boolean;
    initialCheckNewDealBoundaries?: boolean;
}

enum BlockReason {
    PRICE_IN_EXISTING_RANGE = 'PRICE_IN_EXISTING_RANGE',
    NEW_RANGE_OVERLAPS_EXISTING_PRICE = 'NEW_RANGE_OVERLAPS_EXISTING_PRICE',
    CALCULATION_ERROR = 'CALCULATION_ERROR',
}

interface CheckResult {
    canOpen: boolean;
    blockingDeal?: Deal;
    overlappedDeal?: Deal;
    lowerBound?: number; // Range of the *blocking* deal
    upperBound?: number; // Range of the *blocking* deal
    newDealLowerBound?: number; // Range calculated *from* checkPrice
    newDealUpperBound?: number; // Range calculated *from* checkPrice
    blockReason?: BlockReason;
}

interface CalculationConfig {
    overDeviationPercent: number;
    underDeviationPercent: number;
    minPricePercent: number;
    useMinPrice: boolean;
}

interface MessageState {
    text: string | null;
    type: 'success' | 'error' | null;
}

// --- Utility Functions ---
const parsePositiveNumber = (value: string): number => {
    const num = parseFloat(value);
    return !isNaN(num) && num > 0 ? num : NaN;
};
const parsePercentageString = (value: string): number => {
    const num = parseFloat(value);
    return !isNaN(num) && num >= 0 ? num : NaN;
};
const decimalToPercentageString = (decimal: number): string => {
    return (Math.round(decimal * PERCENTAGE_FACTOR * 100) / 100).toString();
};
const toChangeFactor = (percentage: number): number => {
    if (isNaN(percentage)) { console.warn("NaN in toChangeFactor"); return 1; }
    const factor = 1 + (percentage / PERCENTAGE_FACTOR);
    // Prevent division by zero or near-zero issues later
    return factor <= 0 ? 0.000001 : factor; // Return a very small positive number instead of 0 or negative
};
const calculateDealBoundaries = (refPrice: number, config: CalculationConfig): { lowerBound: number; upperBound: number } => {
    const minPriceRefFactor = config.useMinPrice ? toChangeFactor(-config.minPricePercent) : 1;
    const minPriceRef = refPrice * minPriceRefFactor;
    const lowerBound = minPriceRef * toChangeFactor(-config.underDeviationPercent);
    const upperBound = refPrice * toChangeFactor(config.overDeviationPercent);
    return { lowerBound, upperBound };
};
const checkIfDealCanBeOpened = ( checkPrice: number, deals: Deal[], config: CalculationConfig, checkNewDealBoundariesOption: boolean ): CheckResult => {
    if (isNaN(checkPrice) || checkPrice <= 0) return { canOpen: false, blockReason: BlockReason.CALCULATION_ERROR };
    let newDealLowerBound: number | undefined, newDealUpperBound: number | undefined;
    if (checkNewDealBoundariesOption) {
        const bounds = calculateDealBoundaries(checkPrice, config);
        newDealLowerBound = bounds.lowerBound; newDealUpperBound = bounds.upperBound;
        if (isNaN(newDealLowerBound) || isNaN(newDealUpperBound)) { console.warn(`Invalid bounds for new deal`); return { canOpen: false, blockReason: BlockReason.CALCULATION_ERROR }; }
    }
    for (const existingDeal of deals) {
        if (isNaN(existingDeal.max_price) || existingDeal.max_price <= 0) { console.warn(`Skipping invalid existing deal ${existingDeal.id}`); continue; }
        const { lowerBound: existingLb, upperBound: existingUb } = calculateDealBoundaries(existingDeal.max_price, config);
        if (isNaN(existingLb) || isNaN(existingUb)) { console.warn(`Invalid bounds for existing deal ${existingDeal.id}`); continue; }
        if (existingLb < checkPrice && checkPrice < existingUb) {
            return { canOpen: false, blockingDeal: existingDeal, lowerBound: existingLb, upperBound: existingUb, blockReason: BlockReason.PRICE_IN_EXISTING_RANGE };
        }
        if (checkNewDealBoundariesOption && newDealLowerBound !== undefined && newDealUpperBound !== undefined) {
             if (newDealLowerBound < existingDeal.max_price && existingDeal.max_price < newDealUpperBound) {
                return { canOpen: false, overlappedDeal: existingDeal, newDealLowerBound: newDealLowerBound, newDealUpperBound: newDealUpperBound, blockReason: BlockReason.NEW_RANGE_OVERLAPS_EXISTING_PRICE };
            }
        }
    }
    return { canOpen: true };
};

// --- Sub-Components (Assumed Unchanged - Full code included below) ---
const DealList: React.FC<{ deals: Deal[]; onDelete: (id: string) => void; maxPriceLabel: string; }> = React.memo(({ deals, onDelete, maxPriceLabel }) => ( <Card className="bg-gray-900/90 backdrop-blur-md border border-gray-800"><CardHeader><CardTitle className="text-xl sm:text-2xl font-semibold text-gray-200">Existing Deals</CardTitle><CardDescription className="text-gray-400">Deals currently being tracked.</CardDescription></CardHeader><CardContent>{deals.length === 0 ? <p className="text-gray-400">No deals added yet.</p> : <div className="space-y-2"><AnimatePresence initial={false}>{deals.map((deal) => (<motion.div key={deal.id} layout initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} exit={{ opacity: 0, x: 50, transition: { duration: 0.2 } }} className="flex items-center justify-between bg-gray-800/80 border border-gray-700 rounded-md p-2 sm:p-3"><span className="text-gray-300 truncate pr-2" title={deal.max_price.toString()}>{maxPriceLabel}: {deal.max_price.toFixed(2)}</span><Button variant="destructive" size="sm" onClick={() => onDelete(deal.id)} className="bg-red-500/20 text-red-400 hover:bg-red-500/30 hover:text-red-300 flex-shrink-0" aria-label={`Delete deal with price ${deal.max_price.toFixed(2)}`}>Delete</Button></motion.div>))}</AnimatePresence></div>}</CardContent></Card> ));
DealList.displayName = "DealList";
const DealForm: React.FC<{ priceInput: string; onPriceInputChange: (value: string) => void; checkNewDeal: boolean; onCheckNewDealChange: (checked: boolean) => void; onCheck: () => void; onAdd: () => void; canAddDeal: boolean; allowCheckDeals: boolean; allowAddDeals: boolean; maxPriceLabel: string; cardTitle: string; cardDescription: string; }> = React.memo(({ priceInput, onPriceInputChange, checkNewDeal, onCheckNewDealChange, onCheck, onAdd, canAddDeal, allowCheckDeals, allowAddDeals, maxPriceLabel, cardTitle, cardDescription }) => ( <Card className="bg-gray-900/90 backdrop-blur-md border border-gray-800"><CardHeader><CardTitle className="text-xl sm:text-2xl font-semibold text-gray-200">{cardTitle}</CardTitle><CardDescription className="text-gray-400">{cardDescription}</CardDescription></CardHeader><CardContent className="space-y-4"><Input type="number" placeholder={maxPriceLabel} value={priceInput} onChange={(e) => onPriceInputChange(e.target.value)} className="bg-gray-800/80 border-gray-700 text-white placeholder:text-gray-400 w-full" aria-label={maxPriceLabel} step="any" min="0" /><div className="flex items-center space-x-2"><Checkbox id="check-new-deal-boundaries" checked={checkNewDeal} onCheckedChange={onCheckNewDealChange} className="border-gray-700 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-500" /><Label htmlFor="check-new-deal-boundaries" className="text-sm font-medium text-gray-300 select-none cursor-pointer">Check New Deal Boundaries (Two-Way Check)</Label></div><div className="flex flex-col sm:flex-row gap-2">{allowCheckDeals && <Button onClick={onCheck} className="bg-green-500/20 text-green-400 hover:bg-green-500/30 hover:text-green-300 flex-1">Check Availability</Button>}{allowAddDeals && <Button onClick={onAdd} className="bg-blue-500/20 text-blue-400 hover:bg-blue-500/30 hover:text-blue-300 flex-1" disabled={!canAddDeal} aria-disabled={!canAddDeal}>Add Deal</Button>}</div></CardContent></Card> ));
DealForm.displayName = "DealForm";
const ConfigurationPanel: React.FC<{ minPricePercentInput: string; overDeviationInput: string; underDeviationInput: string; isMinPriceEnabled: boolean; isOverDeviationEnabled: boolean; isUnderDeviationEnabled: boolean; onInputChange: (field: 'minPricePercent' | 'overDeviation' | 'underDeviation', value: string) => void; onToggleChange: (field: 'minPrice' | 'overDeviation' | 'underDeviation', checked: boolean) => void; allowMinPriceConfiguration: boolean; allowOverUnderDeviationConfiguration: boolean; minPriceLabel: string; overDeviationLabel: string; underDeviationLabel: string; }> = React.memo(({ minPricePercentInput, overDeviationInput, underDeviationInput, isMinPriceEnabled, isOverDeviationEnabled, isUnderDeviationEnabled, onInputChange, onToggleChange, allowMinPriceConfiguration, allowOverUnderDeviationConfiguration, minPriceLabel, overDeviationLabel, underDeviationLabel }) => { const renderConfigInput = ( fieldKey: keyof typeof configEnabled, inputKey: keyof typeof configInputs, labelText: string, inputValue: string, isEnabled: boolean, allowToggle: boolean ) => { const inputId = `${fieldKey}-input`; const checkboxId = `${fieldKey}-checkbox`; const checkboxLabelId = `${fieldKey}-label`; return ( <div className="flex flex-col sm:flex-row sm:items-center gap-3 py-3 border-b border-gray-800 last:border-b-0"> {allowToggle && (<div className="flex items-center gap-2 flex-shrink-0 w-full sm:w-auto"><Checkbox id={checkboxId} checked={isEnabled} onCheckedChange={(checked) => onToggleChange(fieldKey, Boolean(checked))} className="border-gray-700 data-[state=checked]:bg-blue-600 data-[state=checked]:border-blue-500" aria-labelledby={checkboxLabelId} /><Label id={checkboxLabelId} htmlFor={checkboxId} className="text-sm text-gray-300 cursor-pointer select-none font-medium">{labelText}</Label></div>)} <div className={cn("w-full", allowToggle && "sm:ml-auto")}> <Input id={inputId} type="number" placeholder="Enter %" value={inputValue} onChange={(e) => onInputChange(inputKey, e.target.value)} className={cn("w-full sm:w-32 bg-gray-800/80 border-gray-700 text-white placeholder:text-gray-400", !isEnabled && "opacity-50 cursor-not-allowed")} disabled={!isEnabled} aria-label={`${labelText} Percentage Input`} step="any" min="0" /> </div> </div> ); }; return ( <Card className="bg-gray-900/90 backdrop-blur-md border border-gray-800"><CardHeader><CardTitle className="text-xl sm:text-2xl font-semibold text-gray-200">Configuration</CardTitle><CardDescription className="text-gray-400">Enable and set deviation percentages.</CardDescription></CardHeader><CardContent className="space-y-0">{renderConfigInput('minPrice', 'minPricePercent', minPriceLabel, minPricePercentInput, isMinPriceEnabled, allowMinPriceConfiguration)}{renderConfigInput('overDeviation', 'overDeviation', overDeviationLabel, overDeviationInput, isOverDeviationEnabled, allowOverUnderDeviationConfiguration)}{renderConfigInput('underDeviation', 'underDeviation', underDeviationLabel, underDeviationInput, isUnderDeviationEnabled, allowOverUnderDeviationConfiguration)}</CardContent></Card> ); });
ConfigurationPanel.displayName = "ConfigurationPanel";
const InfoPanel: React.FC<{ maxPriceLabel: string; minPriceLabel: string; minPricePercentDisplay: string; allowMinPriceConfiguration: boolean; overDeviationLabel: string; overDeviationDisplay: string; underDeviationLabel: string; underDeviationDisplay: string; allowOverUnderDeviationConfiguration: boolean; checkNewDealBoundaries: boolean; }> = React.memo(({ maxPriceLabel, minPriceLabel, minPricePercentDisplay, allowMinPriceConfiguration, overDeviationLabel, overDeviationDisplay, underDeviationLabel, underDeviationDisplay, allowOverUnderDeviationConfiguration, checkNewDealBoundaries }) => ( <Card className="bg-gray-900/90 backdrop-blur-md border border-gray-800"><CardHeader><CardTitle className="text-xl sm:text-2xl font-semibold text-gray-200">Demo Information</CardTitle><CardDescription className="text-gray-400">Key configurations and usage instructions.</CardDescription></CardHeader><CardContent className='text-gray-400 text-sm space-y-3'><p>This demo simulates a deal checker. It prevents opening new deals if the input price falls within a calculated deviation range of an existing deal's reference price ({maxPriceLabel}).</p><p>Test it by entering a price, adjusting configurations (in %), checking validity, and then adding the deal if possible.</p><div><p className="font-semibold text-gray-300 mb-1">Current Configurations (as %):</p><ul className='list-disc list-inside space-y-1 pl-2'><li><b>{minPriceLabel}:</b> {minPricePercentDisplay}% {allowMinPriceConfiguration ? "(Configurable)" : "(Fixed)"}</li><li><b>{overDeviationLabel}:</b> {overDeviationDisplay}% {allowOverUnderDeviationConfiguration ? "(Configurable)" : "(Fixed)"}</li><li><b>{underDeviationLabel}:</b> {underDeviationDisplay}% {allowOverUnderDeviationConfiguration ? "(Configurable)" : "(Fixed)"}</li><li className={cn(checkNewDealBoundaries ? "text-green-400" : "text-gray-500")}><b>Two-Way Boundary Check:</b> {checkNewDealBoundaries ? "Enabled" : "Disabled"}</li></ul></div>{checkNewDealBoundaries && (<p className="text-xs text-blue-400">Two-Way Check is ON: A new deal is also blocked if its calculated range would overlap the price of an existing deal.</p>)}{!checkNewDealBoundaries && (<p className="text-xs text-gray-500">Two-Way Check is OFF: Only checks if the new price falls within an existing deal's range.</p>)}<p className="pt-1 text-xs text-gray-500">Note: Internal calculations derive factors (like 1.05 for +5% or 0.97 for -3%) from percentage values using `toChangeFactor`.</p></CardContent></Card> ));
InfoPanel.displayName = "InfoPanel";
const NotificationAlert: React.FC<{ message: MessageState | null }> = React.memo(({ message }) => ( <AnimatePresence>{message?.text && (<motion.div initial={{ opacity: 0, y: -20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: 20, transition: { duration: 0.3 } }} className="mb-6" role="alert" aria-live="polite"><Alert variant={message.type === 'error' ? "destructive" : "default"}>{message.type === 'error' && <AlertCircle className="h-4 w-4" />}<AlertTitle>{message.type === 'error' ? "Error" : "Success"}</AlertTitle><AlertDescription>{message.text}</AlertDescription></Alert></motion.div>)}</AnimatePresence> ));
NotificationAlert.displayName = "NotificationAlert";

// --- Main Component (`DealCheckerDemo`) ---
const DealCheckerDemo: React.FC<DealCheckerDemoProps> = ({
    initialOverDeviation = 0.02, initialUnderDeviation = 0.03, initialDeals = [],
    allowAddDeals = true, allowCheckDeals = true, defaultMinPricePercentDecimal = 0.05,
    customValidation = () => null, maxPriceLabel = "Max Price (Entry/Avg)",
    minPriceLabel = "Min Price Deviation", allowMinPriceConfiguration = true,
    overDeviationLabel = "Over Deviation", underDeviationLabel = "Under Deviation",
    allowOverUnderDeviationConfiguration = true,
    initialCheckNewDealBoundaries = false,
}) => {
    // --- State ---
    const [deals, setDeals] = useState<Deal[]>(initialDeals);
    const [priceInput, setPriceInput] = useState<string>('');
    const [canAddDeal, setCanAddDeal] = useState<boolean>(false);
    const [message, setMessage] = useState<MessageState | null>(null);
    const [configInputs, setConfigInputs] = useState({
        minPricePercent: decimalToPercentageString(defaultMinPricePercentDecimal),
        overDeviation: decimalToPercentageString(initialOverDeviation),
        underDeviation: decimalToPercentageString(initialUnderDeviation),
    });
    const [configEnabled, setConfigEnabled] = useState<{ minPrice: boolean; overDeviation: boolean; underDeviation: boolean; }>({
        minPrice: true, overDeviation: true, underDeviation: true,
    });
    const [checkNewDealBorders, setCheckNewDealBorders] = useState<boolean>(initialCheckNewDealBoundaries);

    // --- Memoized Values ---
    const { cardTitle, cardDescription } = useMemo(() => {
        let title = "Deal Checker"; let description = "Check if a new deal can be opened.";
        if (allowAddDeals && allowCheckDeals) { title = "Check & Add Deal"; description = "Enter a price, check validity, then add the deal."; }
        else if (allowAddDeals) { title = "Add New Deal"; description = "Enter price and add a new deal directly (validation recommended)."; }
        else if (allowCheckDeals) { title = "Check Deal Validity"; description = "Enter a price to check if a new deal can be opened."; }
        return { cardTitle: title, cardDescription: description };
    }, [allowAddDeals, allowCheckDeals]);

    // --- Callbacks ---
    const clearMessage = useCallback(() => setMessage(null), []);
    const showTemporaryMessage = useCallback((text: string, type: 'success' | 'error') => { setMessage({ text, type }); setTimeout(clearMessage, MESSAGE_TIMEOUT_MS); }, [clearMessage]);
    const handleConfigInputChange = useCallback((field: keyof typeof configInputs, value: string) => { setConfigInputs(prev => ({ ...prev, [field]: value })); setCanAddDeal(false); }, []);
    const handleConfigToggleChange = useCallback((field: keyof typeof configEnabled, checked: boolean) => { setConfigEnabled(prev => ({ ...prev, [field]: checked })); setCanAddDeal(false); }, []);
    const handleCheckNewDealChange = useCallback((checked: boolean) => { setCheckNewDealBorders(checked); setCanAddDeal(false); }, []);

    // *** Updated handleCheckDeal with CORRECTED messaging ***
    const handleCheckDeal = useCallback(() => {
        setMessage(null);
        const checkPrice = parsePositiveNumber(priceInput);
        if (isNaN(checkPrice)) { setMessage({ text: 'Please enter a valid positive price to check.', type: 'error' }); setCanAddDeal(false); return; }
        const customValidationError = customValidation(checkPrice);
        if (customValidationError) { setMessage({ text: customValidationError, type: 'error' }); setCanAddDeal(false); return; }

        const parsedMinPricePercent = configEnabled.minPrice ? parsePercentageString(configInputs.minPricePercent) : 0;
        const parsedOverDeviationPercent = configEnabled.overDeviation ? parsePercentageString(configInputs.overDeviation) : 0;
        const parsedUnderDeviationPercent = configEnabled.underDeviation ? parsePercentageString(configInputs.underDeviation) : 0;
        if (isNaN(parsedMinPricePercent) || isNaN(parsedOverDeviationPercent) || isNaN(parsedUnderDeviationPercent)) { setMessage({ text: 'Invalid configuration percentage(s). Please enter valid non-negative numbers.', type: 'error' }); setCanAddDeal(false); return; }

        const finalConfig: CalculationConfig = {
            overDeviationPercent: parsedOverDeviationPercent, underDeviationPercent: parsedUnderDeviationPercent,
            minPricePercent: parsedMinPricePercent, useMinPrice: configEnabled.minPrice
        };

        const result = checkIfDealCanBeOpened(checkPrice, deals, finalConfig, checkNewDealBorders);

        if (result.canOpen) {
            setMessage({ text: `A new deal can be opened at ${checkPrice.toFixed(2)}`, type: 'success' });
        } else {
            let blockMessage = `Cannot open deal at ${checkPrice.toFixed(2)}.`;
            switch (result.blockReason) {
                case BlockReason.PRICE_IN_EXISTING_RANGE:
                    if (result.blockingDeal && result.lowerBound !== undefined && result.upperBound !== undefined) {
                        blockMessage += ` Price is within the forbidden range (${result.lowerBound.toFixed(2)} - ${result.upperBound.toFixed(2)}) of Deal (Ref Price: ${result.blockingDeal.max_price.toFixed(2)}). Consider prices outside this range.`;
                    } else { blockMessage += ` Price is too close to an existing deal.`; }
                    break;

                case BlockReason.NEW_RANGE_OVERLAPS_EXISTING_PRICE:
                    if (result.overlappedDeal && result.newDealLowerBound !== undefined && result.newDealUpperBound !== undefined) {
                        const overlappedPrice = result.overlappedDeal.max_price;
                        const overlappedPriceStr = overlappedPrice.toFixed(2);

                        if (overlappedPrice > checkPrice) {
                            // Existing price is ABOVE checkPrice. Conflict is: overlappedPrice < newDealUpperBound.
                            // User needs to go lower. Boundary = overlappedPrice / (1 + over%)
                            const upperBoundaryFactor = toChangeFactor(finalConfig.overDeviationPercent);
                            // Ensure factor is positive to avoid division issues
                            const boundary = upperBoundaryFactor > 0 ? overlappedPrice / upperBoundaryFactor : 0;
                            blockMessage += ` Existing Deal at ${overlappedPriceStr} is too close ABOVE. It falls within the calculated upper range (${result.newDealUpperBound.toFixed(2)}) based on ${checkPrice.toFixed(2)}. Try prices below ${boundary.toFixed(2)}.`;
                        } else {
                            // Existing price is BELOW checkPrice. Conflict is: newDealLowerBound < overlappedPrice.
                            // User needs to go higher. Boundary = overlappedPrice / [(1 - min%) * (1 - under%)]
                            const lowerBoundaryFactor = (configEnabled.minPrice ? toChangeFactor(-finalConfig.minPricePercent) : 1) * toChangeFactor(-finalConfig.underDeviationPercent);
                             // Ensure factor is positive
                            const boundary = lowerBoundaryFactor > 0 ? overlappedPrice / lowerBoundaryFactor : Infinity; // If factor is 0, effectively no upper limit needed
                            blockMessage += ` Existing Deal at ${overlappedPriceStr} is too close BELOW. It falls within the calculated lower range (${result.newDealLowerBound.toFixed(2)}) based on ${checkPrice.toFixed(2)}. Try prices above ${boundary.toFixed(2)}.`;
                        }
                    } else { blockMessage += ` Potential new deal range overlaps an existing deal.`; }
                    break;

                case BlockReason.CALCULATION_ERROR:
                     blockMessage += ` Calculation error occurred during check (invalid price or configuration).`; break;
                default:
                    blockMessage += ` Conflicts with existing deals or configuration.`; break;
            }
            setMessage({ text: blockMessage, type: 'error' });
        }
        setCanAddDeal(result.canOpen);

    }, [priceInput, customValidation, configInputs, configEnabled, deals, checkNewDealBorders]); // Keep dependencies

    const handleAddDeal = useCallback(() => { if (!canAddDeal) { showTemporaryMessage('Price must be checked and available before adding.', 'error'); return; } const entryPrice = parsePositiveNumber(priceInput); if (isNaN(entryPrice)) { showTemporaryMessage('Invalid price. Cannot add deal.', 'error'); return; } const newDeal: Deal = { id: crypto.randomUUID(), max_price: entryPrice }; setDeals(prevDeals => [...prevDeals, newDeal]); setPriceInput(''); showTemporaryMessage('Deal added successfully.', 'success'); setCanAddDeal(false); }, [canAddDeal, priceInput, showTemporaryMessage]);
    const handleDeleteDeal = useCallback((id: string) => { setDeals(prevDeals => prevDeals.filter(deal => deal.id !== id)); showTemporaryMessage('Deal deleted.', 'success'); setCanAddDeal(false); }, [showTemporaryMessage]);

    // --- useEffect ---
    useEffect(() => { setConfigInputs(prev => ({ ...prev, overDeviation: decimalToPercentageString(initialOverDeviation), underDeviation: decimalToPercentageString(initialUnderDeviation), minPricePercent: decimalToPercentageString(defaultMinPricePercentDecimal) })); setCanAddDeal(false); }, [initialOverDeviation, initialUnderDeviation, defaultMinPricePercentDecimal]);

    // --- Prepare display strings ---
    const formatPercentageForDisplay = (value: string) => { const num = parseFloat(value); return isNaN(num) ? 'Invalid' : num.toFixed(2); };
    const minPriceDisplay = configEnabled.minPrice ? formatPercentageForDisplay(configInputs.minPricePercent) : "Disabled";
    const overDeviationDisplay = configEnabled.overDeviation ? formatPercentageForDisplay(configInputs.overDeviation) : "Disabled";
    const underDeviationDisplay = configEnabled.underDeviation ? formatPercentageForDisplay(configInputs.underDeviation) : "Disabled";

    // --- Render ---
    return (
        <div className="min-h-screen bg-gradient-to-br from-gray-900 to-gray-800 p-4 sm:p-8 text-gray-200 font-sans">
            <div className="max-w-3xl mx-auto space-y-6">
                <h1 className="text-3xl sm:text-4xl md:text-5xl font-bold text-white text-center mb-6">Deal Checker Demo</h1>
                <NotificationAlert message={message} />
                <DealList deals={deals} onDelete={handleDeleteDeal} maxPriceLabel={maxPriceLabel} />
                {(allowMinPriceConfiguration || allowOverUnderDeviationConfiguration) && ( <ConfigurationPanel minPricePercentInput={configInputs.minPricePercent} overDeviationInput={configInputs.overDeviation} underDeviationInput={configInputs.underDeviation} isMinPriceEnabled={configEnabled.minPrice} isOverDeviationEnabled={configEnabled.overDeviation} isUnderDeviationEnabled={configEnabled.underDeviation} onInputChange={handleConfigInputChange} onToggleChange={handleConfigToggleChange} allowMinPriceConfiguration={allowMinPriceConfiguration} allowOverUnderDeviationConfiguration={allowOverUnderDeviationConfiguration} minPriceLabel={minPriceLabel} overDeviationLabel={overDeviationLabel} underDeviationLabel={underDeviationLabel} /> )}
                {(allowAddDeals || allowCheckDeals) && ( <DealForm priceInput={priceInput} onPriceInputChange={setPriceInput} checkNewDeal={checkNewDealBorders} onCheckNewDealChange={handleCheckNewDealChange} onCheck={handleCheckDeal} onAdd={handleAddDeal} canAddDeal={canAddDeal} allowCheckDeals={allowCheckDeals} allowAddDeals={allowAddDeals} maxPriceLabel={maxPriceLabel} cardTitle={cardTitle} cardDescription={cardDescription} /> )}
                <InfoPanel maxPriceLabel={maxPriceLabel} minPriceLabel={minPriceLabel} minPricePercentDisplay={minPriceDisplay} allowMinPriceConfiguration={allowMinPriceConfiguration} overDeviationLabel={overDeviationLabel} overDeviationDisplay={overDeviationDisplay} underDeviationLabel={underDeviationLabel} underDeviationDisplay={underDeviationDisplay} allowOverUnderDeviationConfiguration={allowOverUnderDeviationConfiguration} checkNewDealBoundaries={checkNewDealBorders} />
            </div>
        </div>
    );
};

// --- Export ---
export default DealCheckerDemo;