// components/SoapCalculator.tsx
'use client';
import { useState, useEffect, ChangeEvent, FormEvent } from 'react';
import Image from 'next/image';
import { calculateTotal } from '@/lib/calc';
import { API_BASE_URL } from '@/lib/config';
type InputNumberProps = {
label: string;
value: string;
onChange: (v: string) => void;
};
const InputNumber = ({ label, value, onChange }: InputNumberProps) => {
const id = label.toLowerCase().replace(/\s+/g, '-');
return (
onChange(e.target.value)}
placeholder=" "
className={`
peer
h-10 w-full
bg-gray-700
border-2 border-gray-600
rounded-md
text-gray-200
placeholder-transparent
pl-3
focus:outline-none focus:border-sky-500
appearance-none
`}
/>
);
};
export default function SoapCalculator() {
const [soapName, setSoapName] = useState('');
const [weight, setWeight] = useState('');
const [basePrice, setBasePrice] = useState('');
const [aromaPrice, setAromaPrice] = useState('');
const [aromaWeight, setAromaWeight] = useState('');
const [pigmentPrice, setPigmentPrice] = useState('');
const [pigmentWeight, setPigmentWeight] = useState('');
const [moldPrice, setMoldPrice] = useState('');
const [box, setBox] = useState('');
const [filler, setFiller] = useState('');
const [ribbon, setRibbon] = useState('');
const [labelValue, setLabelValue] = useState('');
const [markup, setMarkup] = useState('');
const [photoFile, setPhotoFile] = useState(null);
const [chatId, setChatId] = useState(null);
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const id = params.get('chat_id');
if (id) {
setChatId(id);
}
}, []);
const toNum = (str: string) => {
const n = parseFloat(str.replace(',', '.'));
return isNaN(n) ? 0 : n;
};
const weightNum = toNum(weight);
const basePriceNum = toNum(basePrice);
const aromaPriceNum = toNum(aromaPrice);
const aromaWeightNum = toNum(aromaWeight);
const pigmentPriceNum = toNum(pigmentPrice);
const pigmentWeightNum = toNum(pigmentWeight);
const moldPriceNum = toNum(moldPrice);
const boxNum = toNum(box);
const fillerNum = toNum(filler);
const ribbonNum = toNum(ribbon);
const labelNum = toNum(labelValue);
const markupNum = toNum(markup);
const result = calculateTotal({
weight: weightNum,
basePrice: basePriceNum,
aromaPrice: aromaPriceNum,
aromaWeight: aromaWeightNum,
pigmentPrice: pigmentPriceNum,
pigmentWeight: pigmentWeightNum,
moldPrice: moldPriceNum,
packaging: {
box: boxNum,
filler: fillerNum,
ribbon: ribbonNum,
label: labelNum,
},
});
const finalPrice = result.total * (1 + markupNum / 100);
const pricePer100g = weightNum > 0 ? (finalPrice / weightNum) * 100 : 0;
const handlePhotoChange = (e: ChangeEvent) => {
if (e.target.files && e.target.files[0]) {
setPhotoFile(e.target.files[0]);
} else {
setPhotoFile(null);
}
};
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
if (!chatId) {
alert('❗ Не найден chat_id. Откройте калькулятор через Telegram-бота.');
return;
}
const formData = new FormData();
formData.append('chat_id', chatId);
formData.append('soapName', soapName || '');
formData.append('weight', weightNum.toString());
formData.append('basePrice', basePriceNum.toString());
formData.append('aromaPrice', aromaPriceNum.toString());
formData.append('aromaWeight', aromaWeightNum.toString());
formData.append('pigmentPrice', pigmentPriceNum.toString());
formData.append('pigmentWeight', pigmentWeightNum.toString());
formData.append('moldPrice', moldPriceNum.toString());
formData.append('box', boxNum.toString());
formData.append('filler', fillerNum.toString());
formData.append('ribbon', ribbonNum.toString());
formData.append('labelValue', labelNum.toString());
formData.append('markup', markupNum.toString());
formData.append('totalCost', result.total.toString());
formData.append('finalPrice', finalPrice.toString());
formData.append('pricePer100g', pricePer100g.toString());
if (photoFile) {
formData.append('photo', photoFile);
}
try {
const res = await fetch(`${API_BASE_URL}/api/submit`, {
method: 'POST',
body: formData,
});
if (res.ok) {
alert('✅ Расчёт успешно отправлен в Telegram!');
setSoapName('');
setWeight('');
setBasePrice('');
setAromaPrice('');
setAromaWeight('');
setPigmentPrice('');
setPigmentWeight('');
setMoldPrice('');
setBox('');
setFiller('');
setRibbon('');
setLabelValue('');
setMarkup('');
setPhotoFile(null);
window.scrollTo({ top: 0, behavior: 'smooth' });
} else {
const text = await res.text();
alert(`Ошибка при отправке: ${text}`);
}
} catch (err) {
console.error(err);
alert('Ошибка сети при отправке расчёта');
}
};
return (
);
}
type CostBlockProps = {
title: string;
value: number;
highlight?: boolean;
};
const CostBlock = ({ title, value, highlight = false }: CostBlockProps) => (
{title}: {value.toFixed(1)} руб
);