+
+ 🔄 Переключить калькулятор:
-
+
{calculators.map((calc) => {
const isActive = pathname === `/${calc.id}`;
@@ -56,19 +56,24 @@ export default function CalculatorNav() {
);
})}
+
+ Нажмите на кнопку выше, чтобы переключиться между калькуляторами
+
);
}
diff --git a/frontend/components/CostBlock.tsx b/frontend/components/CostBlock.tsx
new file mode 100644
index 0000000..5cb4020
--- /dev/null
+++ b/frontend/components/CostBlock.tsx
@@ -0,0 +1,24 @@
+// components/CostBlock.tsx
+// Блок отображения промежуточной стоимости
+
+type CostBlockProps = {
+ title: string;
+ value: number;
+ highlight?: boolean;
+};
+
+export default function CostBlock({ title, value, highlight = false }: CostBlockProps) {
+ return (
+
+ {title}: {value.toFixed(1)} руб
+
+ );
+}
+
diff --git a/frontend/components/DynamicCalculator.tsx b/frontend/components/DynamicCalculator.tsx
index 85c2b9c..a1bfbe9 100644
--- a/frontend/components/DynamicCalculator.tsx
+++ b/frontend/components/DynamicCalculator.tsx
@@ -9,6 +9,7 @@ import { getCalculator } from '@/lib/calculators';
import { submitCalculator } from '@/lib/api';
import FormField from './FormField';
import CalculatorNav from './CalculatorNav';
+import CostBlock from './CostBlock';
import type { Calculator } from '@/types/calculator';
interface DynamicCalculatorProps {
@@ -175,7 +176,21 @@ export default function DynamicCalculator({ calculatorType }: DynamicCalculatorP
// Обработка отправки формы
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
- if (!chatId) {
+
+ // Получаем chat_id из разных источников
+ let currentChatId = chatId;
+ if (!currentChatId && typeof window !== 'undefined') {
+ const params = new URLSearchParams(window.location.search);
+ currentChatId = params.get('chat_id');
+ if (!currentChatId && typeof Storage !== 'undefined') {
+ currentChatId = sessionStorage.getItem('chat_id');
+ }
+ if (currentChatId) {
+ setChatId(currentChatId);
+ }
+ }
+
+ if (!currentChatId) {
alert('❗ Не найден chat_id. Откройте калькулятор через Telegram-бота.');
return;
}
@@ -218,7 +233,7 @@ export default function DynamicCalculator({ calculatorType }: DynamicCalculatorP
// Отправка на backend
const response = await submitCalculator(
calculatorType,
- chatId,
+ currentChatId!,
submitData,
photoFile
);
@@ -273,55 +288,141 @@ export default function DynamicCalculator({ calculatorType }: DynamicCalculatorP
Калькулятор: {calculator.name}
- {/* Основные поля (без группы) */}
- {groupedFields.general && (
-
- {groupedFields.general.map((field) => (
- updateField(field.name, value)}
+ {/* Специфичная структура для мыла с промежуточными стоимостями */}
+ {calculator.id === 'soap' && (
+ <>
+ {/* Название мыла */}
+ f.name === 'soapName')!}
+ value={formData.soapName || ''}
+ onChange={(value) => updateField('soapName', value)}
+ />
+
+ {/* Фото */}
+
+
+ {photoFile && (
+
})
+ )}
+
+ {/* Блок «Основа» */}
+
+ f.name === 'weight')!}
+ value={formData.weight || ''}
+ onChange={(value) => updateField('weight', value)}
+ />
+ f.name === 'basePrice')!}
+ value={formData.basePrice || ''}
+ onChange={(value) => updateField('basePrice', value)}
+ />
+
+
+
+ {/* Блок «Отдушка» */}
+
+ f.name === 'aromaPrice')!}
+ value={formData.aromaPrice || ''}
+ onChange={(value) => updateField('aromaPrice', value)}
+ />
+ f.name === 'aromaWeight')!}
+ value={formData.aromaWeight || ''}
+ onChange={(value) => updateField('aromaWeight', value)}
+ />
+
+
+
+ {/* Блок «Пигмент» */}
+
+ f.name === 'pigmentPrice')!}
+ value={formData.pigmentPrice || ''}
+ onChange={(value) => updateField('pigmentPrice', value)}
+ />
+ f.name === 'pigmentWeight')!}
+ value={formData.pigmentWeight || ''}
+ onChange={(value) => updateField('pigmentWeight', value)}
+ />
+
+
+
+ {/* Блок «Форма» */}
+
f.name === 'moldPrice')!}
+ value={formData.moldPrice || ''}
+ onChange={(value) => updateField('moldPrice', value)}
+ />
+
+
+ {/* Блок «Упаковка» */}
+
+ f.name === 'box')!}
+ value={formData.box || ''}
+ onChange={(value) => updateField('box', value)}
+ />
+ f.name === 'filler')!}
+ value={formData.filler || ''}
+ onChange={(value) => updateField('filler', value)}
+ />
+ f.name === 'ribbon')!}
+ value={formData.ribbon || ''}
+ onChange={(value) => updateField('ribbon', value)}
+ />
+ f.name === 'labelValue')!}
+ value={formData.labelValue || ''}
+ onChange={(value) => updateField('labelValue', value)}
+ />
+
+
+
+
+
+ {/* Блок «Наценка и цена 100 г» */}
+
+ f.name === 'markup')!}
+ value={formData.markup || ''}
+ onChange={(value) => updateField('markup', value)}
+ />
+
+
+
+ >
)}
- {/* Поля с фото */}
-
- {photoFile && (
-
- )}
-
- {/* Группированные поля (например, упаковка) */}
- {Object.entries(groupedFields)
- .filter(([group]) => group !== 'general')
- .map(([group, fields]) => (
-
-
- {group === 'packaging' ? 'Упаковка' : group}
-
-
- {fields.map((field) => (
+ {/* Универсальная структура для других калькуляторов */}
+ {calculator.id !== 'soap' && (
+ <>
+ {/* Основные поля (без группы) */}
+ {groupedFields.general && (
+
+ {groupedFields.general.map((field) => (
))}
-
- ))}
+ )}
- {/* Блоки с результатами расчёта */}
-
- {Object.entries(result)
- .filter(([key]) => key !== 'total')
- .map(([key, value]) => (
-
- {formatResultLabel(key)}: {value.toFixed(1)} ₽
-
- ))}
-
- Итого себестоимость: {result.total.toFixed(1)} ₽
-
-
- Итоговая цена с наценкой: {finalPrice.toFixed(1)} ₽
-
- {weight > 0 && (
-
- Цена за 100 г: {pricePer100g.toFixed(1)} ₽
+ {/* Поля с фото */}
+
+ {photoFile && (
+
})
+ )}
+
+ {/* Группированные поля */}
+ {Object.entries(groupedFields)
+ .filter(([group]) => group !== 'general')
+ .map(([group, fields]) => (
+
+
+ {group === 'packaging' ? 'Упаковка' : group}
+
+
+ {fields.map((field) => (
+ updateField(field.name, value)}
+ />
+ ))}
+
+
+ ))}
+
+ {/* Блоки с результатами расчёта */}
+
+ {Object.entries(result)
+ .filter(([key]) => key !== 'total')
+ .map(([key, value]) => (
+
+ ))}
+
+
+ {weight > 0 && (
+
+ )}
- )}
-
+ >
+ )}
{/* Кнопка отправки */}