Backoffice stage 2: chats (support + concierge, files)
- ChatThread per client/provider and channel; messages get files, internal notes, source (bot/mini app) and delivery errors; reply templates. Migration moves old concierge messages into threads. - Routing: subscribers write to the concierge, others to support; /support and /concierge in the client bot; staff can take, transfer, close and move threads. - Telegram: photos/documents from clients and providers, replies with the channel header, files sent from storage, notifications only to the assignee or the channel's staff, "Reply" into a specific thread. - New admin: /chats with list, conversation, notes, files, templates, client panel; "Message" button on the client card; dashboard counts threads awaiting reply. - Client mini app: in-app chat screen sharing the same conversation. - Old admin keeps working on the same data (notes hidden there). - Tests for routing, notes, assign/close/move, lists, recipients; separate test DB via TEST_DB_NAME; dev:chats-demo script. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
d452824c62
commit
23ffb2bf1d
46 files changed
+4176
-144
No files matched your search
@@ -442,7 +442,9 @@
|
||||
"PROVIDER_NOT_LINKED": "The provider hasn’t opened the work bot via the invite yet — nowhere to send messages.",
|
||||
"CARD_EXPIRED": "The QR is out of date or invalid — ask the client to reopen the card.",
|
||||
"OFFER_NOT_AVAILABLE": "This offer isn’t available at the client’s level.",
|
||||
"OFFER_LIMIT_REACHED": "The limit for this offer is already used up."
|
||||
"OFFER_LIMIT_REACHED": "The limit for this offer is already used up.",
|
||||
"CONCIERGE_SUBSCRIBERS_ONLY": "The personal concierge is for Barev+ and Barev Elite members. For now, write to support — we will help too.",
|
||||
"ASSIGNEE_NO_ACCESS": "This team member has no access to this channel."
|
||||
},
|
||||
"client": {
|
||||
"nav": {
|
||||
@@ -654,6 +656,20 @@
|
||||
"used_ONCE": "already used",
|
||||
"used_WEEK": "again next week",
|
||||
"used_MONTH": "again next month"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Chat with Barev Club",
|
||||
"support": "Support",
|
||||
"concierge": "Personal concierge",
|
||||
"hint": "This is the same conversation as in the bot chat: replies arrive here and in Telegram.",
|
||||
"empty": "Tell us how we can help — we’ll reply here.",
|
||||
"placeholder": "Message…",
|
||||
"send": "Send",
|
||||
"attach": "Attach a photo or file",
|
||||
"conciergeOnly": "The personal concierge comes with Barev+ and Barev Elite",
|
||||
"team": "Barev Club",
|
||||
"fileTooBig": "The file is larger than 10 MB",
|
||||
"file": "File"
|
||||
}
|
||||
},
|
||||
"pro": {
|
||||
@@ -815,7 +831,12 @@
|
||||
"cmdHelp": "Help",
|
||||
"webRequestOrdered": "Request No. {{request}} received and booked as order No. {{order}} for {{date}}. Track the status in the app.",
|
||||
"webRequestLinked": "Request No. {{request}} received. An operator will contact you to confirm the time.",
|
||||
"conciergeReceived": "Passed to the concierge — we'll reply here, in this chat."
|
||||
"conciergeReceived": "Passed to the concierge — we'll reply here, in this chat.",
|
||||
"supportReceived": "Passed to support — we'll reply here, in this chat.",
|
||||
"channelSupport": "You are now writing to support. Just send a message, photo or file.",
|
||||
"channelConcierge": "You are now writing to your personal concierge. Just send a message, photo or file.",
|
||||
"cmdSupport": "Write to support",
|
||||
"cmdConcierge": "Write to the concierge (Barev+)"
|
||||
},
|
||||
"pro": {
|
||||
"notLinked": "This bot is for Barev Club specialists. Ask the operator for an invite link.",
|
||||
@@ -844,7 +865,11 @@
|
||||
"newRequest": "App request #{{number}}",
|
||||
"newWebRequest": "Site request #{{number}}",
|
||||
"loginOk": "Sign-in confirmed — go back to your browser.",
|
||||
"loginExpired": "The sign-in link has expired — reload the admin page."
|
||||
"loginExpired": "The sign-in link has expired — reload the admin page.",
|
||||
"channelSupport": "Support",
|
||||
"channelConcierge": "Concierge",
|
||||
"file": "file",
|
||||
"chatAssigned": "{{by}} passed you a conversation: {{name}}"
|
||||
},
|
||||
"partner": {
|
||||
"valid": "{{name}} — {{level}}, card is valid",
|
||||
@@ -948,7 +973,8 @@
|
||||
"openPlan": "Open the app",
|
||||
"concierge": "Barev Club concierge",
|
||||
"subscriptionActivated": "{{plan}} is on until {{date}}. Discounts are already in service prices.",
|
||||
"operator": "Barev Club operator"
|
||||
"operator": "Barev Club operator",
|
||||
"support": "Barev Club support"
|
||||
},
|
||||
"admin": {
|
||||
"nav": {
|
||||
|
||||
@@ -442,7 +442,9 @@
|
||||
"PROVIDER_NOT_LINKED": "El profesional aún no ha abierto el bot con la invitación: no hay dónde escribirle.",
|
||||
"CARD_EXPIRED": "El QR caducó o no es válido: pide al cliente que vuelva a abrir la tarjeta.",
|
||||
"OFFER_NOT_AVAILABLE": "Esta oferta no está disponible en el nivel del cliente.",
|
||||
"OFFER_LIMIT_REACHED": "Ya se agotó el límite de esta oferta."
|
||||
"OFFER_LIMIT_REACHED": "Ya se agotó el límite de esta oferta.",
|
||||
"CONCIERGE_SUBSCRIBERS_ONLY": "El conserje personal es para miembros de Barev+ y Barev Elite. De momento, escribe a soporte: también te ayudaremos.",
|
||||
"ASSIGNEE_NO_ACCESS": "Esta persona del equipo no tiene acceso a este canal."
|
||||
},
|
||||
"client": {
|
||||
"nav": {
|
||||
@@ -654,6 +656,20 @@
|
||||
"used_ONCE": "ya usado",
|
||||
"used_WEEK": "de nuevo la próxima semana",
|
||||
"used_MONTH": "de nuevo el próximo mes"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Chat con Barev Club",
|
||||
"support": "Soporte",
|
||||
"concierge": "Conserje personal",
|
||||
"hint": "Es la misma conversación que en el chat del bot: la respuesta llegará aquí y a Telegram.",
|
||||
"empty": "Cuéntanos en qué podemos ayudarte: te responderemos aquí.",
|
||||
"placeholder": "Mensaje…",
|
||||
"send": "Enviar",
|
||||
"attach": "Adjuntar foto o archivo",
|
||||
"conciergeOnly": "El conserje personal está incluido en Barev+ y Barev Elite",
|
||||
"team": "Barev Club",
|
||||
"fileTooBig": "El archivo supera los 10 MB",
|
||||
"file": "Archivo"
|
||||
}
|
||||
},
|
||||
"pro": {
|
||||
@@ -815,7 +831,12 @@
|
||||
"cmdHelp": "Ayuda",
|
||||
"webRequestOrdered": "Solicitud n.º {{request}} recibida y registrada como pedido n.º {{order}} para el {{date}}. El estado está en la app.",
|
||||
"webRequestLinked": "Solicitud n.º {{request}} recibida. Un operador te contactará para confirmar la hora.",
|
||||
"conciergeReceived": "Lo hemos pasado al conserje: te responderemos aquí, en este chat."
|
||||
"conciergeReceived": "Lo hemos pasado al conserje: te responderemos aquí, en este chat.",
|
||||
"supportReceived": "Lo hemos pasado a soporte: te responderemos aquí, en este chat.",
|
||||
"channelSupport": "Ahora escribes a soporte. Envía un mensaje, una foto o un archivo.",
|
||||
"channelConcierge": "Ahora escribes a tu conserje personal. Envía un mensaje, una foto o un archivo.",
|
||||
"cmdSupport": "Escribir a soporte",
|
||||
"cmdConcierge": "Escribir al conserje (Barev+)"
|
||||
},
|
||||
"pro": {
|
||||
"notLinked": "Este bot es para los especialistas de Barev Club. Pide al operador un enlace de invitación.",
|
||||
@@ -844,7 +865,11 @@
|
||||
"newRequest": "Solicitud de la app n.º {{number}}",
|
||||
"newWebRequest": "Solicitud de la web n.º {{number}}",
|
||||
"loginOk": "Inicio de sesión confirmado: vuelve al navegador.",
|
||||
"loginExpired": "El enlace de acceso ha caducado: recarga la página de administración."
|
||||
"loginExpired": "El enlace de acceso ha caducado: recarga la página de administración.",
|
||||
"channelSupport": "Soporte",
|
||||
"channelConcierge": "Conserje",
|
||||
"file": "archivo",
|
||||
"chatAssigned": "{{by}} te ha pasado una conversación: {{name}}"
|
||||
},
|
||||
"partner": {
|
||||
"valid": "{{name}} — {{level}}, tarjeta válida",
|
||||
@@ -948,7 +973,8 @@
|
||||
"openPlan": "Abrir la app",
|
||||
"concierge": "Conserje de Barev Club",
|
||||
"subscriptionActivated": "{{plan}} activo hasta el {{date}}. Los descuentos ya están en los precios.",
|
||||
"operator": "Operador de Barev Club"
|
||||
"operator": "Operador de Barev Club",
|
||||
"support": "Soporte de Barev Club"
|
||||
},
|
||||
"admin": {
|
||||
"nav": {
|
||||
|
||||
@@ -442,7 +442,9 @@
|
||||
"PROVIDER_NOT_LINKED": "Կատարողը դեռ չի բացել աշխատանքային բոտը հրավերով՝ գրելու տեղ չկա։",
|
||||
"CARD_EXPIRED": "QR-ը հնացել է կամ անվավեր է՝ խնդրեք հաճախորդին նորից բացել քարտը։",
|
||||
"OFFER_NOT_AVAILABLE": "Այս առաջարկը հասանելի չէ հաճախորդի մակարդակում։",
|
||||
"OFFER_LIMIT_REACHED": "Այս առաջարկի սահմանաչափն արդեն սպառված է։"
|
||||
"OFFER_LIMIT_REACHED": "Այս առաջարկի սահմանաչափն արդեն սպառված է։",
|
||||
"CONCIERGE_SUBSCRIBERS_ONLY": "Անձնական կոնսիերժը Barev+ և Barev Elite բաժանորդների համար է։ Առայժմ գրեք աջակցությանը՝ մենք էլ կօգնենք։",
|
||||
"ASSIGNEE_NO_ACCESS": "Այս աշխատակիցը մուտք չունի այս ալիքին։"
|
||||
},
|
||||
"client": {
|
||||
"nav": {
|
||||
@@ -654,6 +656,20 @@
|
||||
"used_ONCE": "արդեն ստացել եք",
|
||||
"used_WEEK": "կրկին հաջորդ շաբաթից",
|
||||
"used_MONTH": "կրկին հաջորդ ամսից"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Զրույց Barev Club-ի հետ",
|
||||
"support": "Աջակցություն",
|
||||
"concierge": "Անձնական կոնսիերժ",
|
||||
"hint": "Սա նույն զրույցն է, ինչ բոտի չատում․ պատասխանը կգա և՛ այստեղ, և՛ Telegram-ում։",
|
||||
"empty": "Գրեք, թե ինչով օգնել՝ կպատասխանենք այստեղ։",
|
||||
"placeholder": "Հաղորդագրություն…",
|
||||
"send": "Ուղարկել",
|
||||
"attach": "Կցել լուսանկար կամ ֆայլ",
|
||||
"conciergeOnly": "Անձնական կոնսիերժը՝ Barev+ և Barev Elite-ում",
|
||||
"team": "Barev Club",
|
||||
"fileTooBig": "Ֆայլը 10 ՄԲ-ից մեծ է",
|
||||
"file": "Ֆայլ"
|
||||
}
|
||||
},
|
||||
"pro": {
|
||||
@@ -815,7 +831,12 @@
|
||||
"cmdHelp": "Օգնություն",
|
||||
"webRequestOrdered": "Հայտ №{{request}}-ն ընդունված է և ձևակերպված որպես պատվեր №{{order}}՝ {{date}}։ Կարգավիճակը՝ հավելվածում։",
|
||||
"webRequestLinked": "Հայտ №{{request}}-ն ընդունված է։ Օպերատորը կկապվի քեզ հետ և կհաստատի ժամը։",
|
||||
"conciergeReceived": "Փոխանցեցինք կոնսիերժին՝ կպատասխանենք այստեղ, այս չատում։"
|
||||
"conciergeReceived": "Փոխանցեցինք կոնսիերժին՝ կպատասխանենք այստեղ, այս չատում։",
|
||||
"supportReceived": "Փոխանցեցինք աջակցությանը՝ կպատասխանենք այստեղ, այս չատում։",
|
||||
"channelSupport": "Այժմ գրում եք աջակցությանը։ Պարզապես ուղարկեք հաղորդագրություն, լուսանկար կամ ֆայլ։",
|
||||
"channelConcierge": "Այժմ գրում եք ձեր անձնական կոնսիերժին։ Պարզապես ուղարկեք հաղորդագրություն, լուսանկար կամ ֆայլ։",
|
||||
"cmdSupport": "Գրել աջակցությանը",
|
||||
"cmdConcierge": "Գրել կոնսիերժին (Barev+)"
|
||||
},
|
||||
"pro": {
|
||||
"notLinked": "Այս բոտը Barev Club-ի կատարողների համար է։ Խնդրեք օպերատորից հրավերի հղում։",
|
||||
@@ -844,7 +865,11 @@
|
||||
"newRequest": "Հայտ հավելվածից №{{number}}",
|
||||
"newWebRequest": "Հայտ կայքից №{{number}}",
|
||||
"loginOk": "Մուտքը հաստատված է — վերադարձեք բրաուզեր։",
|
||||
"loginExpired": "Մուտքի հղումը հնացել է — թարմացրեք ադմին էջը։"
|
||||
"loginExpired": "Մուտքի հղումը հնացել է — թարմացրեք ադմին էջը։",
|
||||
"channelSupport": "Աջակցություն",
|
||||
"channelConcierge": "Կոնսիերժ",
|
||||
"file": "ֆայլ",
|
||||
"chatAssigned": "{{by}}-ը ձեզ փոխանցեց զրույցը՝ {{name}}"
|
||||
},
|
||||
"partner": {
|
||||
"valid": "{{name}} — {{level}}, քարտը գործում է",
|
||||
@@ -948,7 +973,8 @@
|
||||
"openPlan": "Բացել հավելվածը",
|
||||
"concierge": "Barev Club կոնսիերժ",
|
||||
"subscriptionActivated": "{{plan}}-ը միացված է մինչև {{date}}։ Զեղչերն արդեն ծառայությունների գներում են։",
|
||||
"operator": "Barev Club օպերատոր"
|
||||
"operator": "Barev Club օպերատոր",
|
||||
"support": "Barev Club աջակցություն"
|
||||
},
|
||||
"admin": {
|
||||
"nav": {
|
||||
|
||||
@@ -442,7 +442,9 @@
|
||||
"PROVIDER_NOT_LINKED": "Исполнитель ещё не открыл рабочий бот по инвайту — написать ему пока некуда.",
|
||||
"CARD_EXPIRED": "QR устарел или недействителен — попросите клиента открыть карту заново.",
|
||||
"OFFER_NOT_AVAILABLE": "Это предложение недоступно на уровне клиента.",
|
||||
"OFFER_LIMIT_REACHED": "Лимит по этому предложению уже исчерпан."
|
||||
"OFFER_LIMIT_REACHED": "Лимит по этому предложению уже исчерпан.",
|
||||
"CONCIERGE_SUBSCRIBERS_ONLY": "Личный консьерж — для подписчиков Barev+ и Barev Elite. Пока пишите в поддержку — мы тоже поможем.",
|
||||
"ASSIGNEE_NO_ACCESS": "У этого сотрудника нет доступа к этому каналу."
|
||||
},
|
||||
"client": {
|
||||
"nav": {
|
||||
@@ -654,6 +656,20 @@
|
||||
"used_ONCE": "уже получено",
|
||||
"used_WEEK": "снова со следующей недели",
|
||||
"used_MONTH": "снова со следующего месяца"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Чат с Barev Club",
|
||||
"support": "Поддержка",
|
||||
"concierge": "Личный консьерж",
|
||||
"hint": "Это та же переписка, что в чате с ботом: ответ придёт и сюда, и в Telegram.",
|
||||
"empty": "Напишите, чем помочь, — ответим здесь.",
|
||||
"placeholder": "Сообщение…",
|
||||
"send": "Отправить",
|
||||
"attach": "Прикрепить фото или файл",
|
||||
"conciergeOnly": "Личный консьерж — в Barev+ и Barev Elite",
|
||||
"team": "Barev Club",
|
||||
"fileTooBig": "Файл больше 10 МБ",
|
||||
"file": "Файл"
|
||||
}
|
||||
},
|
||||
"pro": {
|
||||
@@ -815,7 +831,12 @@
|
||||
"cmdHelp": "Помощь",
|
||||
"webRequestOrdered": "Заявка №{{request}} принята и оформлена как заказ №{{order}} на {{date}}. Статус — в приложении.",
|
||||
"webRequestLinked": "Заявка №{{request}} принята. Оператор свяжется с тобой и подтвердит время.",
|
||||
"conciergeReceived": "Передали консьержу — ответим здесь, в этом чате."
|
||||
"conciergeReceived": "Передали консьержу — ответим здесь, в этом чате.",
|
||||
"supportReceived": "Передали в поддержку — ответим здесь, в этом чате.",
|
||||
"channelSupport": "Теперь вы пишете в поддержку. Просто отправьте сообщение, фото или файл.",
|
||||
"channelConcierge": "Теперь вы пишете личному консьержу. Просто отправьте сообщение, фото или файл.",
|
||||
"cmdSupport": "Написать в поддержку",
|
||||
"cmdConcierge": "Написать консьержу (Barev+)"
|
||||
},
|
||||
"pro": {
|
||||
"notLinked": "Этот бот — для исполнителей Barev Club. Попроси у оператора ссылку-приглашение.",
|
||||
@@ -844,7 +865,11 @@
|
||||
"newRequest": "Заявка из приложения №{{number}}",
|
||||
"newWebRequest": "Заявка с сайта №{{number}}",
|
||||
"loginOk": "Вход в админку подтверждён — вернитесь в браузер.",
|
||||
"loginExpired": "Ссылка для входа устарела — обновите страницу админки."
|
||||
"loginExpired": "Ссылка для входа устарела — обновите страницу админки.",
|
||||
"channelSupport": "Поддержка",
|
||||
"channelConcierge": "Консьерж",
|
||||
"file": "файл",
|
||||
"chatAssigned": "{{by}} передал(а) вам диалог: {{name}}"
|
||||
},
|
||||
"partner": {
|
||||
"valid": "{{name}} — {{level}}, карта действует",
|
||||
@@ -948,7 +973,8 @@
|
||||
"openPlan": "Открыть приложение",
|
||||
"concierge": "Консьерж Barev Club",
|
||||
"subscriptionActivated": "{{plan}} включён до {{date}}. Скидки уже в ценах услуг.",
|
||||
"operator": "Оператор Barev Club"
|
||||
"operator": "Оператор Barev Club",
|
||||
"support": "Поддержка Barev Club"
|
||||
},
|
||||
"admin": {
|
||||
"nav": {
|
||||
|
||||
@@ -442,7 +442,9 @@
|
||||
"PROVIDER_NOT_LINKED": "服务人员尚未通过邀请打开工作机器人,暂时无法发送消息。",
|
||||
"CARD_EXPIRED": "二维码已过期或无效——请客户重新打开会员卡。",
|
||||
"OFFER_NOT_AVAILABLE": "该优惠不适用于客户当前等级。",
|
||||
"OFFER_LIMIT_REACHED": "该优惠次数已用完。"
|
||||
"OFFER_LIMIT_REACHED": "该优惠次数已用完。",
|
||||
"CONCIERGE_SUBSCRIBERS_ONLY": "专属礼宾仅面向 Barev+ 和 Barev Elite 会员。目前请联系客服,我们同样会帮你。",
|
||||
"ASSIGNEE_NO_ACCESS": "该成员无权访问此频道。"
|
||||
},
|
||||
"client": {
|
||||
"nav": {
|
||||
@@ -654,6 +656,20 @@
|
||||
"used_ONCE": "已使用",
|
||||
"used_WEEK": "下周可再次使用",
|
||||
"used_MONTH": "下月可再次使用"
|
||||
},
|
||||
"chat": {
|
||||
"title": "与 Barev Club 聊天",
|
||||
"support": "客服",
|
||||
"concierge": "专属礼宾",
|
||||
"hint": "这与机器人聊天是同一个对话:回复会同时出现在这里和 Telegram。",
|
||||
"empty": "告诉我们需要什么帮助,我们会在这里回复。",
|
||||
"placeholder": "输入消息…",
|
||||
"send": "发送",
|
||||
"attach": "附加照片或文件",
|
||||
"conciergeOnly": "专属礼宾包含在 Barev+ 和 Barev Elite 中",
|
||||
"team": "Barev Club",
|
||||
"fileTooBig": "文件超过 10 MB",
|
||||
"file": "文件"
|
||||
}
|
||||
},
|
||||
"pro": {
|
||||
@@ -815,7 +831,12 @@
|
||||
"cmdHelp": "帮助",
|
||||
"webRequestOrdered": "申请 {{request}} 号已收到,并已生成订单 {{order}} 号,时间:{{date}}。状态请在应用中查看。",
|
||||
"webRequestLinked": "申请 {{request}} 号已收到。客服会联系你确认时间。",
|
||||
"conciergeReceived": "已转给礼宾,我们会在这个聊天中回复你。"
|
||||
"conciergeReceived": "已转给礼宾,我们会在这个聊天中回复你。",
|
||||
"supportReceived": "已转给客服,我们会在这个聊天中回复你。",
|
||||
"channelSupport": "现在你在给客服写信。直接发送消息、照片或文件即可。",
|
||||
"channelConcierge": "现在你在给专属礼宾写信。直接发送消息、照片或文件即可。",
|
||||
"cmdSupport": "联系客服",
|
||||
"cmdConcierge": "联系礼宾(Barev+)"
|
||||
},
|
||||
"pro": {
|
||||
"notLinked": "此机器人供 Barev Club 服务人员使用。请向客服索取邀请链接。",
|
||||
@@ -844,7 +865,11 @@
|
||||
"newRequest": "应用申请 #{{number}}",
|
||||
"newWebRequest": "网站申请 #{{number}}",
|
||||
"loginOk": "登录已确认,请返回浏览器。",
|
||||
"loginExpired": "登录链接已过期,请刷新管理页面。"
|
||||
"loginExpired": "登录链接已过期,请刷新管理页面。",
|
||||
"channelSupport": "客服",
|
||||
"channelConcierge": "礼宾",
|
||||
"file": "文件",
|
||||
"chatAssigned": "{{by}} 将对话转交给你:{{name}}"
|
||||
},
|
||||
"partner": {
|
||||
"valid": "{{name}} — {{level}},会员卡有效",
|
||||
@@ -948,7 +973,8 @@
|
||||
"openPlan": "打开应用",
|
||||
"concierge": "Barev Club 礼宾",
|
||||
"subscriptionActivated": "{{plan}} 已开通,有效期至 {{date}}。服务价格已含折扣。",
|
||||
"operator": "Barev Club 客服"
|
||||
"operator": "Barev Club 客服",
|
||||
"support": "Barev Club 客服支持"
|
||||
},
|
||||
"admin": {
|
||||
"nav": {
|
||||
|
||||
Reference in new issue
Block a user