From 1fa97ca010aa0d61824b6976670e53182519fc2c Mon Sep 17 00:00:00 2001 From: houseassassin Date: Tue, 12 May 2026 11:17:09 +0300 Subject: [PATCH] feat: Telegram message templates in OrderModal Co-Authored-By: Claude Sonnet 4.6 --- src/admin/orders/OrderModal.jsx | 107 +++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/admin/orders/OrderModal.jsx b/src/admin/orders/OrderModal.jsx index ee56509..a0b3f39 100644 --- a/src/admin/orders/OrderModal.jsx +++ b/src/admin/orders/OrderModal.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react'; -import { X, Save, FileText, Camera, X as XIcon, Share2 } from 'lucide-react'; +import { X, Save, FileText, Camera, X as XIcon, Share2, MessageSquare } from 'lucide-react'; import { useOrderPhotos } from './useOrderPhotos'; import { pdf } from '@react-pdf/renderer'; import { supabase } from '../../lib/supabase'; @@ -9,6 +9,42 @@ import { useIsMobile } from '../../hooks/useIsMobile'; import { InvoicePDF } from './InvoicePDF'; const STATUSES = ['новая', 'замер', 'согласование', 'монтаж', 'закрыт']; + +const TEMPLATES = [ + { + id: 'confirm', + label: 'Подтверждение визита', + text: (f) => { + const date = f.scheduled_at + ? new Date(f.scheduled_at).toLocaleString('ru-RU', { day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' }) + : '[дата не указана]'; + return `Здравствуйте, ${f.name || '[имя]'}! Подтверждаем ваш визит ${date}${f.address ? ` по адресу: ${f.address}` : ''}. Ждём вас!`; + }, + }, + { + id: 'reminder', + label: 'Напоминание (за день)', + text: (f) => { + const date = f.scheduled_at + ? new Date(f.scheduled_at).toLocaleString('ru-RU', { day: 'numeric', month: 'long', hour: '2-digit', minute: '2-digit' }) + : '[дата не указана]'; + return `${f.name || 'Клиент'}, напоминаем о визите завтра в ${date.split(',')[1]?.trim() || '[время]'}${f.address ? `, адрес: ${f.address}` : ''}. До встречи!`; + }, + }, + { + id: 'received', + label: 'Заявка получена', + text: (f) => `Здравствуйте, ${f.name || '[имя]'}! Ваша заявка принята. Менеджер свяжется с вами в ближайшее время для уточнения деталей.`, + }, + { + id: 'done', + label: 'Работы завершены', + text: (f) => { + const cost = f.final_cost ? `Итоговая стоимость: ${Number(f.final_cost).toLocaleString('ru-RU')} ₽.` : ''; + return `${f.name || 'Клиент'}, работы по бронированию стёкол завершены. ${cost} Спасибо за доверие!`; + }, + }, +]; const OBJECT_TYPES = [ { value: 'industrial', label: 'Промышленный объект' }, { value: 'commercial', label: 'Коммерческая штаб-квартира' }, @@ -58,6 +94,8 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => { const [downloading, setDownloading] = useState(false); const [error, setError] = useState(''); const [copied, setCopied] = useState(false); + const [templatesOpen, setTemplatesOpen] = useState(false); + const [copiedTpl, setCopiedTpl] = useState(null); useEffect(() => { if (order?.id) { @@ -97,6 +135,14 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => { scheduled_at: f.scheduled_at === '' ? null : new Date(f.scheduled_at).toISOString(), }); + const handleCopyTemplate = (tpl) => { + const text = tpl.text(form); + navigator.clipboard.writeText(text).then(() => { + setCopiedTpl(tpl.id); + setTimeout(() => setCopiedTpl(null), 2000); + }); + }; + const handleShare = () => { const url = `${window.location.origin}/track/${order.public_token}`; navigator.clipboard.writeText(url).then(() => { @@ -325,6 +371,65 @@ const OrderModal = ({ order, onClose, onSaved, mode = 'edit' }) => {