{{ t('dash_title') }}

{{ t('dash_subtitle') }}

{{ s.label }}

{{ s.value }}

{{ s.sub }}

{{ t('dash_total_token_usage') }}

{{ formatTokens(dashTokenUsage.total_tokens) }}

{{ dashTokenUsage.call_count }} {{ t('dash_api_calls') }}

{{ t('dash_prompt_tokens') }}

{{ formatTokens(dashTokenUsage.prompt_tokens) }}

{{ t('dash_input_cost') }}

{{ t('dash_completion_tokens') }}

{{ formatTokens(dashTokenUsage.completion_tokens) }}

{{ t('dash_output_cost') }}

{{ t('dash_client_usage_ranking') }}

{{ t('dash_no_usage_data') }}

{{ i+1 }}
{{ cu.name }} {{ formatTokens(cu.total_tokens) }}

{{ t('dash_quick_actions') }}

{{ t('dash_system_status') }}

{{ s.name }} {{ s.ok ? t('dash_online') : t('dash_offline') }}

{{ t('dash_active_employees') }}

{{ t('dash_no_active_employees') }}

{{ a.name.charAt(0) }}

{{ a.name }}

{{ getTenantName(a.tenant_id) }}

Daily Token Usage (30d)

{{ analytics.usage_by_day[0]?.date?.slice(5) || '' }} {{ analytics.usage_by_day[analytics.usage_by_day.length-1]?.date?.slice(5) || '' }}

Daily Conversations (30d)

{{ analytics.conversations_by_day[0]?.date?.slice(5) || '' }} {{ analytics.conversations_by_day[analytics.conversations_by_day.length-1]?.date?.slice(5) || '' }}

Top Agents by Token Usage

{{ i+1 }}
{{ ag.agent_name }} {{ ag.tenant_name }}
{{ formatTokens(ag.tokens) }} / {{ ag.calls }} calls

{{ t('tenants_title') }}

{{ t('tenants_subtitle') }}

{{ t('tenants_no_clients') }}

{{ t('tenants_no_clients_desc') }}

{{ filteredTenants.length }} / {{ tenants.length }} clients

{{ tn.name.charAt(0) }}

{{ tn.name }}

{{ industryLabel(tn.industry) }}

Credits

{{ (tn.credits_balance || 0).toLocaleString() }}

Plan

{{ tn.plan || 'free' }}

Profile

View

{{ a.name }} +{{ agentsForTenant(tn.id).length - 3 }}

No employees yet

{{ t('workflows_title') }}

{{ t('workflows_subtitle') }}

{{ t('workflows_active') }}

{{ wf.name }}

{{ wf.description || t('workflows_no_description') }}

{{ wf.industry_template }} {{ t('workflows_template') }}

{{ t('workflows_template_library') }}

{{ industryLabel(industry) }}

{{ tpl.name }}

{{ tpl.display_name }}

{{ tpl.description }}

{{ tpl.steps_preview.length }} steps Setup: {{ tpl.estimated_setup }}

{{ t('connectors_title') }}

{{ t('connectors_subtitle') }}

{{ t('connectors_active_bindings') }}

{{ t('connectors_no_bindings') }} {{ t('connectors_channels_tab') }} {{ t('connectors_tab_suffix') }}

{{ t('connectors_available') }}

{{ c.name }}

{{ c.description }}

{{ t('connectors_actions') }}

{{ a }}

{{ t('exec_title') }}

{{ t('exec_subtitle') }}

{{ t('exec_no_data') }}

{{ t('exec_id') }} {{ t('exec_trigger') }} {{ t('exec_status') }}
{{ e.id.substring(0,8) }} {{ e.triggered_by }} {{ e.status }}

{{ currentLang === 'en' ? 'Platform Health' : '平台健康' }}

{{ currentLang === 'en' ? 'Live status of every external dependency, plus an inbox of unresolved system alerts.' : '所有外部依赖的实时状态 + 未处理告警收件箱。' }}

{{ name }}

{{ (s.status || 'unknown').toUpperCase() }}

{{ s.detail }}

{{ s.latency_ms }}ms

{{ currentLang === 'en' ? 'Tool Reliability (last 24h)' : '工具可用性 · 最近 24 小时' }}

{{ currentLang === 'en' ? 'No tool calls in the window yet.' : '近期还没有工具调用记录' }}
{{ currentLang === 'en' ? 'Tool' : '工具' }} {{ currentLang === 'en' ? 'Calls' : '调用' }} {{ currentLang === 'en' ? 'Success' : '成功率' }} {{ currentLang === 'en' ? 'Latency' : '延迟' }} {{ currentLang === 'en' ? 'Top failure' : '主要失败类型' }}
{{ t.tool_name }} {{ t.total }} {{ Math.round(t.success_rate * 100) }}% {{ t.avg_latency_ms }}ms {{ t.failures > 0 ? (t.top_error_class || '—') : '—' }}

{{ currentLang === 'en' ? 'Agent Delivery by Role (last 7d)' : '员工角色交付成绩 · 最近 7 天' }}

{{ currentLang === 'en' ? 'No agent activity in the window yet.' : '近 7 天还没有员工运行记录' }}
{{ currentLang === 'en' ? 'Role' : '角色' }} {{ currentLang === 'en' ? 'Sessions' : '会话数' }} {{ currentLang === 'en' ? 'Avg turns' : '平均轮次' }} {{ currentLang === 'en' ? 'Tool calls' : '工具调用' }} {{ currentLang === 'en' ? 'Tool success' : '工具成功率' }} {{ currentLang === 'en' ? 'Tasks' : '后台任务' }} {{ currentLang === 'en' ? 'Delivery rate' : '真实交付率' }}
{{ r.role }} {{ r.sessions || 0 }} {{ r.avg_user_turns_per_session != null ? r.avg_user_turns_per_session : '—' }} {{ r.tool_calls || 0 }} {{ r.tool_success_rate != null ? Math.round(r.tool_success_rate * 100) + '%' : '—' }} {{ r.tasks || 0 }} {{ Math.round(r.artifact_delivery_rate * 100) }}% ({{ r.artifact_delivered || 0 }}/{{ r.artifact_verified || 0 }})

{{ currentLang === 'en' ? 'Delivery rate is computed from artifact verification (only tasks with _delivered set count toward it).' : '真实交付率基于产物自检(只有写入 _delivered 字段的任务参与统计)' }}

{{ currentLang === 'en' ? 'Alerts' : '告警' }}

{{ currentLang === 'en' ? 'No alerts.' : '暂无告警' }}
{{ a.severity === 'critical' ? '🚨' : a.severity === 'warning' ? '⚠️' : 'ℹ️' }}
{{ a.title }} {{ a.category }} {{ a.source }} ×{{ a.occurrence_count }} {{ currentLang === 'en' ? 'Resolved' : '已解决' }}

{{ a.body }}

{{ formatTime(a.created_at) }} · last {{ formatTime(a.last_seen_at) }}

{{ t('nav_invocations') }}

{{ invocationSource === 'gateway' ? '通用 API 中转站调用记录(直通 LLM,按 token 扣 HKD 余额)' : '全平台 AI 员工调用记录(带工具/记忆,按 credit 扣费)' }}

总调用次数

{{ (gwInvocationData.summary.calls || 0).toLocaleString() }}

收入 (HKD)

HK${{ (gwInvocationData.summary.revenue_hkd || 0).toFixed(2) }}

Token 输入 / 输出

{{ (gwInvocationData.summary.input_tokens || 0).toLocaleString() }} / {{ (gwInvocationData.summary.output_tokens || 0).toLocaleString() }}

失败次数

{{ gwInvocationData.summary.errors || 0 }}

按模型分类

{{ m.model }}

{{ m.calls }} 次调用 · {{ (m.tokens || 0).toLocaleString() }} tok

HK${{ m.revenue_hkd.toFixed(4) }}

暂无数据

按客户分类

{{ t.tenant_name }}

{{ t.calls }} 次调用 · {{ (t.tokens || 0).toLocaleString() }} tok

HK${{ t.revenue_hkd.toFixed(4) }}

暂无数据
时间 客户 Key 模型 In / Out 扣费 延迟 状态
{{ c.created_at ? new Date(c.created_at).toLocaleString() : '' }} {{ c.tenant_name }} {{ c.key_prefix || '—' }} {{ c.model }} {{ c.input_tokens }} / {{ c.output_tokens }} HK${{ c.charged_hkd.toFixed(6) }} {{ c.latency_ms }}ms {{ c.status }}
暂无调用记录
第 {{ gwInvocationFilter.page }} 页

总调用次数

{{ (invocationData.summary.total_calls || 0).toLocaleString() }}

总Token消耗

{{ (invocationData.summary.total_tokens || 0).toLocaleString() }}

消耗点数

{{ (invocationData.summary.credits_used || 0).toLocaleString() }}

预估成本 (USD)

${{ (invocationData.summary.total_cost || 0).toFixed(2) }}

按模型分类

{{ m.model }}

{{ m.calls }} 次调用

{{ (m.tokens || 0).toLocaleString() }} tokens

${{ m.cost.toFixed(3) }}

暂无数据

按客户分类

{{ t.tenant_name }}

{{ t.calls }} 次调用 · {{ t.credits }} 点数

{{ (t.tokens || 0).toLocaleString() }} tokens

${{ t.cost.toFixed(3) }}

暂无数据
时间 客户 员工 模型 Prompt Completion Total 点数 成本
{{ new Date(inv.created_at).toLocaleString('zh-CN', {month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'}) }} {{ inv.tenant_name || '-' }} {{ inv.agent_name || '-' }} {{ inv.model }} {{ (inv.prompt_tokens||0).toLocaleString() }} {{ (inv.completion_tokens||0).toLocaleString() }} {{ (inv.total_tokens||0).toLocaleString() }} {{ inv.credits }} ${{ inv.cost_usd.toFixed(4) }}
暂无调用记录

共 {{ invocationData.total }} 条记录

{{ invocationFilter.page }} / {{ Math.ceil(invocationData.total / 50) }}

{{ selectedTenant.name }}

{{ industryLabel(selectedTenant.industry) }} · {{ selectedTenant.plan }}

{{ t('client_digital_employees') }}

{{ t('client_no_employees') }}

{{ a.name.charAt(0) }}

{{ a.name }}

{{ a.title || roleLabel(a.role) }} · {{ a.tasks_completed }} tasks

{{ a.status }}

{{ t('client_connected_services') }}

Gmail / Google Workspace

{{ gmailStatus.email }}

Connect to read/send emails via AI agents

Connected

{{ t('client_no_connectors') }}

{{ connName(c.connector_type) }}

{{ c.connector_type }}

{{ c.is_active ? t('client_active') : t('client_disabled') }}

{{ currentLang === 'en' ? 'Company Profile' : currentLang === 'zh-CN' ? '公司资料' : '公司資料' }}

{{ currentLang === 'en' ? 'Your tenant\'s identity DNA — read by every AI generator (outreach emails, video brief, mascot brief, CS prompts). Fill in incrementally; the more complete it is, the more on-brand the outputs.' : currentLang === 'zh-CN' ? '你公司的身份基因 — 所有 AI 生成(邮件建联、视频 brief、吉祥物 brief、客服话术)都会读取这份资料。可逐步填写,越完整 AI 输出越贴合你的品牌。' : '你公司的身份基因 — 所有 AI 生成(郵件建聯、影片 brief、吉祥物 brief、客服話術)都會讀取這份資料。可逐步填寫,越完整 AI 輸出越貼合你的品牌。' }}

{{ currentLang === 'en' ? 'Profile completeness' : '完整度' }}

{{ companyProfile.completion_pct || 0 }}%
{{ currentLang === 'en' ? 'Loading…' : '加载中…' }}

{{ currentLang === 'en' ? '1 · Basic Info' : '一、基本信息' }}

{{ cpSectionProgress('basic') }}

{{ currentLang === 'en' ? '2 · Brand Core' : '二、品牌核心' }}

{{ cpSectionProgress('brand_core') }}

{{ currentLang === 'en' ? '3 · Value Propositions' : '三、差异化卖点' }}

{{ cpSectionProgress('value_props') }}

{{ currentLang === 'en' ? 'Why do customers pick you over competitors? Add 3-5 punchy bullets.' : '客户为什么选你而不选别人?写 3-5 条简短的点。' }}

{{ currentLang === 'en' ? '4 · Target Customer' : '四、目标客户' }}

{{ cpSectionProgress('target_customer') }}

{{ currentLang === 'en' ? 'Industries' : '行业' }}

{{ currentLang === 'en' ? 'Customer sizes' : '客户规模' }}

{{ currentLang === 'en' ? 'Decision-maker roles' : '决策人角色' }}

{{ currentLang === 'en' ? 'Geographies' : '地域' }}

{{ currentLang === 'en' ? '5 · Voice / Tone' : '五、口吻 / 风格' }}

{{ cpSectionProgress('voice_tone') }}

{{ currentLang === 'en' ? 'Forbidden phrases' : '禁用词' }}

{{ currentLang === 'en' ? '6 · Email Signatures' : '六、邮件签名' }}

{{ cpSectionProgress('signatures') }}

{{ currentLang === 'en' ? 'Sign-off blocks used by outreach emails.' : '建联邮件结尾会用这些签名。' }}

{{ currentLang === 'en' ? '7 · Compliance & Notes' : '七、合规 + 备注' }}

{{ cpSectionProgress('compliance') }}

{{ t('client_documents_data') }}

{{ knowledgeStats.document_count || 0 }}

{{ t('client_documents') }}

{{ knowledgeStats.chunk_count || 0 }}

{{ t('client_text_chunks') }}

{{ knowledgeStats.structured_rows || 0 }}

{{ t('client_data_rows') }}

{{ Math.round((knowledgeStats.total_chars || 0) / 1000) }}k

{{ t('client_characters') }}

{{ knowledgeStats.has_embeddings ? 'ON' : 'OFF' }}

{{ t('client_vector_search') || 'Vector Search' }}

{{ knowledgeStats.processing_count }} {{ t('client_docs_processing') || 'document(s) processing...' }}

{{ t('client_supported_formats') }} {{ t('client_supported_formats_desc') }}

{{ t('client_excel_note') }}

{{ t('client_no_documents') }}

{{ (d.file_type || '').toUpperCase() }}

{{ d.filename }}

{{ d.chunk_count }} chunks · {{ d.scope }} {{ d.status }}... Error {{ d.status }} · {{ d.size_bytes > 1048576 ? (d.size_bytes/1048576).toFixed(1)+'MB' : Math.round(d.size_bytes/1024)+'KB' }}

{{ d.summary }}

{{ selectedAgent.name.charAt(0) }}

{{ selectedAgent.name }}

{{ selectedAgent.title || roleLabel(selectedAgent.role) }} · {{ getTenantName(selectedAgent.tenant_id) }}

{{ selectedAgent.status }}
{{ agentRoleLabel(selectedAgent.role) }}
{{ t('unsaved_changes') || 'You have unsaved changes' }}

{{ t('cap_external_title') }}

{{ t('cap_external_desc') }}

{{ t('vc_tab_channels') || 'Communication Channels' }}

{{ t('vc_channels_desc') || 'Connect a messaging channel so you can send video requests from your phone.' }}

{{ t('cap_channels_title') }}

{{ t('cap_channels_desc') }}

{{ channelLabel(ch.channel_type) }}

{{ ch.active ? t('channels_active') : t('channels_inactive') }}

{{ t('channels_no_bound') }}

{{ t("channels_bot_token_desc") }} @BotFather {{ t("channels_bot_token_desc2") }}

{{ t('channels_bound_success') }}

{{ bindResult.error || t('channels_bind_failed') }}

{{ t('cap_no_channels') }}

{{ t('cap_internal_title') }}

{{ t('cap_internal_desc') }}

{{ t('tpl_knowledge_base') }}

{{ t('tpl_knowledge_base_desc') }}

Upload reference materials in the Reference Materials tab. The AI will search them when answering questions.

{{ t('tpl_social_media') }}

{{ t('tpl_social_media_desc') }}

{{ t('tpl_data_entry') }}

{{ t('tpl_data_entry_desc') }}

Coming soon

{{ currentLang === 'en' ? 'Media Intelligence Workbench' : '媒體情報工作台' }}

{{ currentLang === 'en' ? 'On-demand Hong Kong advertising + media news harvesting.' : '按需抓取香港廣告 + 媒體新聞情報' }}

{{ adIntelLastStatus }}

{{ currentLang === 'en' ? 'No ads yet. Click "Harvest Ads Now" to pull from Meta / Google / TikTok.' : '尚無廣告資料,按上面「立即抓取廣告」開始。' }}

{{ currentLang === 'en' ? 'Platform' : '平台' }} {{ currentLang === 'en' ? 'Advertiser' : '廣告主' }} {{ currentLang === 'en' ? 'Industry' : '行業' }} {{ currentLang === 'en' ? 'First Seen' : '首次發現' }} {{ currentLang === 'en' ? 'Status' : '狀態' }}
{{ f.platform }} {{ f.advertiser_name }} {{ f.industry || '—' }} {{ formatTime(f.first_seen) }} {{ f.is_active ? (currentLang === 'en' ? 'Active' : '進行中') : (currentLang === 'en' ? 'Inactive' : '已下架') }}

{{ currentLang === 'en' ? 'No news yet. Click "Pull HK News Now" to fetch from Sing Tao / HK01 / Ming Pao / etc.' : '尚無新聞,按上面「立即抓取新聞」拉取星島 / HK01 / 明報等' }}

{{ currentLang === 'en' ? 'Source' : '來源' }} {{ currentLang === 'en' ? 'Title' : '標題' }} {{ currentLang === 'en' ? 'Brand Match' : '匹配品牌' }} {{ currentLang === 'en' ? 'Published' : '發佈時間' }}
{{ n.source }} {{ n.title }} {{ n.matched_brand_name || '—' }} {{ formatTime(n.published_at || n.discovered_at) }}

{{ currentLang === 'en' ? 'No advertisers tracked yet. Run a harvest first.' : '暫無已追蹤的廣告主,先做一次抓取' }}

{{ currentLang === 'en' ? 'Brand' : '品牌' }} {{ currentLang === 'en' ? 'Industry' : '行業' }} {{ currentLang === 'en' ? 'Platforms' : '出現平台' }} {{ currentLang === 'en' ? 'First Seen' : '首次發現' }} {{ currentLang === 'en' ? 'Enrichment' : '深度資料' }}
{{ a.display_name }} {{ a.industry || '—' }} {{ (a.platforms_seen || []).join(' · ') }} {{ formatTime(a.first_seen) }} {{ currentLang === 'en' ? 'Enriched' : '已完成' }}
{{ selectedAgent.name.charAt(0) }}

{{ t('chat_with') }} {{ selectedAgent.name }}

{{ t('chat_test_desc') }}

{{ t('chat_start') }}

{{ t('chat_send_test') }} {{ selectedAgent.name }}{{ t('chat_send_test_suffix') }}

{{ msg.content }}

{{ prettyToolName(tc.tool) }} {{ JSON.stringify(tc.arguments).substring(0,60) }}
{{ msg.tokens }} {{ t('chat_tokens') }} {{ msg.iterations }} {{ t('chat_step') }}
{{ t('chat_session') }}: {{ chatTokens.total }} {{ t('chat_tokens') }} {{ t('chat_prompt') }}: {{ chatTokens.prompt }} {{ t('chat_completion') }}: {{ chatTokens.completion }}

{{ t('appt_title') }}

{{ t('sales_title') }}

{{ t('usage_recent') }}

{{ t('usage_no_records') }}
{{ t('usage_time') }} {{ t('usage_source') }} {{ t('usage_model') }} {{ isAdmin ? t('usage_tokens') : (t('usage_credits') || '点数') }}
{{ formatTime(r.created_at) }} {{ r.source }} {{ r.model }} {{ isAdmin ? r.total_tokens : Math.ceil((r.total_tokens || 0) / 1000) }}

{{ t('api_title') }}

{{ t('api_subtitle') }}

Select an agent to configure

Choose an agent above to set up AI customer service

CS Mode: {{ csAgentConfig.enabled ? 'Active' : 'Inactive' }}
{{ csSelectedAgent.name }} — {{ csAgentConfig.company_name || 'No company set' }}
{{ preset.label }}
{{ preset.description }}

Company Context

Provide company info for customer service context. The agent's identity (name, role, personality) comes from its own profile settings.

Agent Identity: {{ csSelectedAgent.name }} — {{ csSelectedAgent.role || 'AI Agent' }}

Edit the agent's name, role, and personality in the Agent Management page.

Greeting Messages

Customize greeting templates for different scenarios.

Knowledge Domains

Define what topic areas the agent should focus on when searching the knowledge base.

{{ domain }} No domains configured — agent will search all knowledge
Common domains: products, faq, policies, pricing, shipping, returns, troubleshooting, account, billing

Working Hours

Set business hours for human availability. The AI still responds 24/7 but adjusts messaging.

Enable working hours
{{ dayLabel }}

Escalation Rules

Define when the AI should transfer to a human agent.

Enable escalation
{{ trigger.replace(/_/g, ' ') }}

Email will receive escalation notifications with conversation summary

Safety & Guardrails

Protect your brand by controlling what the agent can and cannot discuss.

{{ topic.replace(/_/g, ' ') }}

Language Settings

Configure which languages the agent supports and how it detects language.

{{ lang }}
Auto-detect language from customer messages

Connected Channels

Manage messaging channels that this CS agent listens on.

Loading channels...

{{ ch.channel_type }}

{{ ch.active ? 'Active' : 'Inactive' }} · Token configured

{{ ch.channel_type === 'whatsapp' ? (currentLang === 'en' ? 'Paste this Inbound webhook URL into Twilio → Senders → ' + (csSelectedAgent && csSelectedAgent.config && csSelectedAgent.config.channels && csSelectedAgent.config.channels.whatsapp && csSelectedAgent.config.channels.whatsapp.twilio_phone ? csSelectedAgent.config.channels.whatsapp.twilio_phone : 'your number') + ' → Edit Sender:' : '把这条 Webhook URL 粘到 Twilio → Senders → 你的号码 → Edit Sender → Inbound webhook 字段(方法选 POST):') : (currentLang === 'en' ? 'Paste this URL into WeChat dashboard:' : '把这条 Webhook URL 粘到 WeChat 公众号后台:') }}

{{ csChannelWebhookUrl(ch.channel_type) }}

No channels connected yet. Connect a channel below to start receiving messages.

Connect New Channel

Connect WhatsApp via Twilio

Setup: Go to console.twilio.com → Messaging → Try it out → Send a WhatsApp message

Copy the Account SID and Auth Token from the Twilio Console dashboard.

The Twilio WhatsApp Number is the sandbox number (e.g. +14155238886) or your approved business number.

After connecting, copy the Webhook URL shown below into Twilio Sandbox Settings → "When a message comes in".

WhatsApp connected successfully!

Copy this Webhook URL into Twilio Sandbox Settings → "When a message comes in":

{{ csBindResult.webhook_url }}
{{ csBindResult.error }}

Connect Telegram Bot

Create a bot via @BotFather on Telegram, then paste the bot token below. Webhook will be configured automatically.

{{ csBindResult.ok ? 'Telegram bot connected! Webhook set up automatically.' : csBindResult.error }}

Connect WeChat Official Account

{{ csBindResult.ok ? 'WeChat connected!' : csBindResult.error }}

Web Chat Widget

Web chat widget is already available via the Website Chat feature. Go to your website settings to embed the chat widget.

Channel-Specific Overrides

Customize response behavior per channel.

{{ ch.channel_type }}

Response Style

Control how the agent formats and structures responses.

Use Markdown formatting
Include relevant links

{{ currentLang === 'en' ? 'Bookings & Deposits' : '預約 + 收訂金' }}

{{ currentLang === 'en' ? 'When enabled, the AI can atomically hold a time slot and send a Stripe deposit link in one message — the slot stays held until the customer pays or the window expires.' : '啟用之後,AI 喺對話入面可以一氣呵成保留時段同發訂金 Stripe 連結 — 客人未付款之前個位會保住,超時自動釋放。' }}

{{ currentLang === 'en' ? 'Deposit collection enabled' : '啟用收訂金' }}
{{ currentLang === 'en' ? 'Off = the AI uses plain create_appointment (no deposit asked).' : '關閉 = AI 用普通預約(不收訂金)。' }}

{{ currentLang === 'en' ? 'AI uses this when no service-specific price exists. HK F&B ~HK$200, beauty ~HK$300, training trial ~HK$100.' : 'AI 喺冇對應服務價錢時用呢個數。餐廳常見 HK$200,美容 HK$300,補習試堂 HK$100。' }}

{{ currentLang === 'en' ? 'After this, the slot auto-releases if unpaid. 10 min good for beauty; 15-20 min for restaurants (WeChat Pay app-switch takes time).' : '超時未付款自動釋放。美容 10 分鐘,餐廳建議 15-20 分鐘(畀客人切返 WeChat Pay app 嘅時間)。' }}

{{ currentLang === 'en' ? 'Placeholders: {date} {time} {service} {customer}. Keep it short — this is what your customer reads after paying.' : '可用佔位符:{date} {time} {service} {customer}。簡短啲,客人付完款就見到呢段。' }}

{{ currentLang === 'en' ? 'Unsaved changes' : '有未儲存嘅修改' }}

{{ currentLang === 'en' ? 'Allowed reply languages' : '允許 AI 使用嘅語言' }}

{{ currentLang === 'en' ? 'The AI will only reply in these. If a customer writes in another language, it falls back to the first one in the list.' : 'AI 只會用呢度勾選嘅語言回覆。客人用其他語言時會用第一種做回退。' }}

{{ currentLang === 'en' ? 'Primary (fallback): ' : '預設 (回退): ' }}{{ csAllowedLangsLocal[0] }}

{{ idx + 1 }}

{{ csWizardSteps[csWizardStep]?.label }}

{{ csWizardSteps[csWizardStep]?.desc }}

{{ agent.name }}
{{ agentRoleLabel(agent.role) }} — {{ agent.description || 'No description' }}
{{ preset.label }}
{{ preset.description }}

Agent: {{ csSelectedAgent.name }} — {{ csSelectedAgent.role || 'AI Agent' }}

Agent identity comes from its profile. Only company context needs to be set here.

Agent{{ csSelectedAgent?.name }} ({{ csSelectedAgent?.role || 'AI Agent' }})
Company{{ csAgentConfig.company_name || '—' }}
Escalation{{ csAgentConfig.escalation?.enabled ? 'Enabled' : 'Disabled' }}

{{ t('sm_title') }}

{{ t('sm_subtitle') }}

{{ t('sm_select_agent') }}

{{ t('sm_select_agent_desc') }}

{{ t('sm_no_agent') }}

AI Customer Service

Configure your AI customer service agents

{{ agent.name }}

{{ t('workspace_title') }}

{{ t('workspace_subtitle') }}

{{ agent.name }}

{{ getWsChatLastTime(agent.id) }}

{{ agent.title || agentRoleLabel(agent.role) }}

{{ t('ws_no_selection') }}

{{ t('ws_no_selection_desc') }}

{{ chat.agentName }}

{{ chat.agentTitle || 'AI Employee' }}

{{ t('ws_drop_files') }}

{{ t('ws_drop_files_hint') }}

{{ chat.agentName }}

Start a conversation below

{{ chat.loadingStatus?.text || 'Thinking...' }}

!

Action Requires Approval

{{ chat.pendingApproval.tool }}

?

Agent Needs Input

{{ chat.pendingApproval.question }}

{{ t('client_dash_welcome') }} {{ authUser?.name }}

{{ clientDashData.company || t('client_dash_workspace') }}

{{ t('client_dash_not_configured') }}

{{ t('client_dash_not_configured_desc') }}

{{ currentLang === 'en' ? 'Simulate Customer Conversation' : '模擬客戶對話' }}

{{ currentLang === 'en' ? 'You play the customer. The AI replies as it would to a real WhatsApp inquiry — tools fire for real (real Stripe links, real DB rows). Use this to test your AI before you wire up channels.' : '你扮演客戶,AI 會用對外客戶嘅角色同你對話。工具會真正執行(真嘅 Stripe 連結、真嘅資料庫紀錄),但唔會經 WhatsApp 發出去。連接客戶之前先喺呢度試。' }}

{{ currentLang === 'en' ? 'This is a real run — book_and_collect_deposit will create real Stripe links and real appointment rows. Use test card 4242 4242 4242 4242 if you want to walk the full deposit flow.' : '呢度係真實執行 — book_and_collect_deposit 會生成真嘅 Stripe 付款連結同預約紀錄。想行完整個收訂金流程嘅話用測試卡 4242 4242 4242 4242。' }}

{{ currentLang === 'en' ? 'Testing as customer of:' : '扮演客戶 — 對話員工:' }}
{{ currentLang === 'en' ? 'Start by sending a message as a customer would — e.g. "Hi, I want to book a table for 6 next Friday 7pm".' : '試下用客戶語氣發第一條訊息 — 例如「你好,我想訂位,下週五晚上 7 點 6 個人」。' }}
{{ m.content }}

{{ currentLang === 'en' ? 'Tools fired' : '已調用工具' }}:

{{ tc.name }} {{ currentLang === 'en' ? 'open Stripe link' : '打開 Stripe 連結' }} → appt {{ tc.result.appointment_id.slice(0,8) }}… → {{ tc.result.error.slice(0,80) }}

{{ currentLang === 'en' ? 'Inbox' : '收件箱' }}

{{ currentLang === 'en' ? 'See what your AI employees are saying. Take over any conversation in one click.' : '睇下你嘅 AI 員工同客人傾緊咩。隨時一鍵接管。' }}

{{ currentLang === 'en' ? 'Pick an employee above to see their conversations.' : '揀上面其中一個員工,睇佢嘅對話。' }}
{{ currentLang === 'en' ? 'Loading...' : '載入中...' }}
{{ currentLang === 'en' ? 'No conversations yet.' : '暫時無對話。' }}
{{ currentLang === 'en' ? '← Pick a conversation to read.' : '← 揀返一個對話睇。' }}

{{ t('client_agents_title') }}

{{ t('client_agents_subtitle') }}

{{ clientAgentsList.length }} / {{ agentMaxCount }}

{{ t('client_agents_not_configured') }}

{{ t('client_agents_not_configured_desc') }}

{{ currentLang === 'en' ? 'API Gateway' : 'API 中转站' }}

{{ currentLang === 'en' ? 'A direct OpenAI-compatible LLM proxy. Pay-as-you-go from your HKD balance. Separate from agent credits.' : '通用 LLM API 中转,按量从你的 HKD 现金余额扣费,跟员工 credit 池完全分开。' }}

{{ currentLang === 'en' ? 'Current balance' : '当前余额' }}

{{ apiGw?.balance?.display || 'HK$0.00' }}

{{ currentLang === 'en' ? 'Spent total: ' : '累计消耗: ' }}HK${{ (apiGw?.balance?.total_spent_hkd ?? 0).toFixed(2) }} · {{ currentLang === 'en' ? 'Topped up: ' : '累计充值: ' }}HK${{ (apiGw?.balance?.total_topped_up_hkd ?? 0).toFixed(2) }}

{{ currentLang === 'en' ? 'Top up' : '充值' }}

{{ currentLang === 'en' ? 'Card · Alipay · WeChat Pay accepted. One-time charge — no auto-renewal.' : '信用卡 / 支付宝 / 微信支付。一次性扣款,不会自动续费。' }}

{{ currentLang === 'en' ? 'Endpoint' : '接口地址' }}

{{ apiGw?.base_url || 'https://www.everflowhk.com/gateway/v1' }}

{{ currentLang === 'en' ? 'Example (Python):' : '示例 (Python):' }}

from openai import OpenAI
client = OpenAI(
    base_url="{{ apiGw?.base_url || 'https://www.everflowhk.com/gateway/v1' }}",
    api_key="",
)
resp = client.chat.completions.create(
    model="{{ (apiGw?.models?.[0]?.id) || 'gpt-5.4' }}",
    messages=[{"role": "user", "content": "Hello"}],
)
print(resp.choices[0].message.content)

{{ currentLang === 'en' ? 'Use any API key from the "API keys" section below.' : '把下面"API 密钥"那一节里的任意一个 key 填进去就能调。' }}

{{ currentLang === 'en' ? 'API keys' : 'API 密钥' }}

{{ currentLang === 'en' ? 'Bearer tokens to authenticate calls to the gateway. Create one per integration.' : 'Bearer 令牌,调用上面接口时填到 api_key 字段。每个集成场景建一个 key 方便管理和吊销。' }}

{{ currentLang === 'en' ? 'New key — copy now, it will not be shown again:' : '新密钥 — 现在拷走,关闭后就再看不到了:' }}

{{ newApiKey }}
{{ currentLang === 'en' ? 'No keys yet. Create one to start calling the API.' : '还没有任何密钥。点上面"创建密钥"开一个就能用。' }}

{{ k.name }}

{{ k.key_prefix }}··· · {{ k.calls_count || 0 }} {{ currentLang === 'en' ? 'calls' : '次调用' }} · {{ k.is_active ? (currentLang === 'en' ? 'Active' : '启用') : (currentLang === 'en' ? 'Revoked' : '已吊销') }}

{{ currentLang === 'en' ? 'Quick test' : '快速测试' }}

{{ currentLang === 'en' ? 'Paste a key, pick a model, send a prompt. Charges your balance just like a real call.' : '填一个 key、挑一个模型、发一句话。跟正常调用一样会从余额扣费。' }}

{{ apiGwTest.latencyMs }} ms · {{ apiGwTest.tokens.in }}/{{ apiGwTest.tokens.out }} tok · −HK${{ apiGwTest.charged.toFixed(4) }}
{{ apiGwTest.error }}
{{ apiGwTest.reply }}

{{ currentLang === 'en' ? 'Available models + pricing' : '可用模型 + 计费' }}

{{ m.display_name || m.id }}

{{ currentLang === 'en' ? 'Call as' : '调用模型名' }}: {{ m.id }}

{{ m.description }}

{{ currentLang === 'en' ? 'Input' : '输入' }}: HK${{ (m.input_price_per_1k_hkd * 1000).toFixed(2) }} / 1M tokens

{{ currentLang === 'en' ? 'Output' : '输出' }}: HK${{ (m.output_price_per_1k_hkd * 1000).toFixed(2) }} / 1M tokens

{{ currentLang === 'en' ? 'No models configured yet.' : '还没有配置任何模型。' }}

{{ currentLang === 'en' ? 'Recent calls' : '最近调用' }}

{{ currentLang === 'en' ? 'No calls yet.' : '暂无调用记录。' }}
{{ c.status }} {{ c.model }} {{ c.input_tokens }}/{{ c.output_tokens }} -HK${{ c.charged_hkd.toFixed(4) }}

{{ currentLang === 'en' ? 'Recent top-ups' : '最近充值' }}

{{ currentLang === 'en' ? 'No top-ups yet.' : '暂无充值记录。' }}
{{ t.status }} +HK${{ t.amount_hkd.toFixed(2) }} {{ t.created_at ? new Date(t.created_at).toLocaleString() : '' }}

{{ t('client_usage_title') }}

{{ t('client_usage_subtitle') }}

{{ t("client_usage_not_configured") }}

{{ t("client_usage_not_configured_desc") }}

{{ t('files_title') }}

{{ t('files_subtitle') }}

{{ t('files_empty') }}

{{ f.filename }}

{{ lang === 'zh' ? '公共' : 'Shared' }} {{ getClientAgentName(f.agent_id) || (lang === 'zh' ? '专属' : 'Private') }} {{ lang === 'zh' ? '对话附件' : 'Chat' }} {{ (f.size / 1024).toFixed(1) }}KB · {{ formatTime(f.created_at) }}

{{ t('files_download') || 'Download' }}

{{ t('api_keys_title') || 'API Keys' }}

{{ t('api_keys_subtitle') || 'Integrate your AI employees into your own systems' }}

{{ t('api_keys_created') || 'API Key Created — copy it now, it won\'t be shown again:' }}

{{ newApiKey }}

{{ t('api_keys_empty') || 'No API keys yet. Create one to start integrating.' }}

{{ k.name }}

{{ k.key_prefix }}... · {{ k.calls_count || 0 }} calls · {{ k.is_active ? 'Active' : 'Revoked' }}

Revoked

{{ t('webhooks_title') || 'Webhooks' }}

{{ t('webhooks_subtitle') || 'Receive notifications when events happen with your AI employees' }}

{{ t('webhooks_empty') || 'No webhooks configured. Add one to receive event notifications.' }}

{{ wh.name }}

{{ wh.url }}

{{ ev }}

{{ currentLang === 'en' ? 'Company Profile' : currentLang === 'zh-CN' ? '公司资料' : '公司資料' }}

{{ currentLang === 'en' ? 'Your tenant\'s identity DNA — read by every AI generator (CS replies, outreach emails, video brief, mascot brief). Fill in incrementally; the more complete it is, the more on-brand the outputs.' : currentLang === 'zh-CN' ? '你公司的身份基因 — 所有 AI 生成(客服话术、邮件建联、视频 brief、吉祥物 brief)都会读取这份资料。可逐步填写,越完整 AI 输出越贴合你的品牌。' : '你公司的身份基因 — 所有 AI 生成(客服話術、郵件建聯、影片 brief、吉祥物 brief)都會讀取這份資料。可逐步填寫,越完整 AI 輸出越貼合你的品牌。' }}

{{ currentLang === 'en' ? 'Profile completeness' : '完整度' }}

{{ companyProfile.completion_pct || 0 }}%
{{ currentLang === 'en' ? 'Loading…' : '加载中…' }}

{{ currentLang === 'en' ? '1 · Basic Info' : '一、基本信息' }}

{{ cpSectionProgress('basic') }}

{{ currentLang === 'en' ? '2 · Brand Core' : '二、品牌核心' }}

{{ cpSectionProgress('brand_core') }}

{{ currentLang === 'en' ? '3 · Value Propositions' : '三、差异化卖点' }}

{{ cpSectionProgress('value_props') }}

{{ currentLang === 'en' ? 'Why do customers pick you over competitors? Add 3-5 punchy bullets.' : '客户为什么选你而不选别人?写 3-5 条简短的点。' }}

{{ currentLang === 'en' ? '4 · Target Customer' : '四、目标客户' }}

{{ cpSectionProgress('target_customer') }}

{{ currentLang === 'en' ? 'Industries' : '行业' }}

{{ currentLang === 'en' ? 'Customer sizes' : '客户规模' }}

{{ currentLang === 'en' ? 'Decision-maker roles' : '决策人角色' }}

{{ currentLang === 'en' ? 'Geographies' : '地域' }}

{{ currentLang === 'en' ? '5 · Voice / Tone' : '五、口吻 / 风格' }}

{{ cpSectionProgress('voice_tone') }}

{{ currentLang === 'en' ? 'Forbidden phrases' : '禁用词' }}

{{ currentLang === 'en' ? '6 · Email Signatures' : '六、邮件签名' }}

{{ cpSectionProgress('signatures') }}

{{ currentLang === 'en' ? 'Sign-off blocks used by outreach emails.' : '建联邮件结尾会用这些签名。' }}

{{ currentLang === 'en' ? '7 · Compliance & Notes' : '七、合规 + 备注' }}

{{ cpSectionProgress('compliance') }}

{{ t('ch_title') }}

{{ t('ch_subtitle') }}

{{ chError }}
{{ chSuccess }}

{{ t('ch_select_platform') }}

{{ t('ch_select_platform_desc') }}

{{ t('ch_select_agent') }}

{{ t('ch_select_agent_desc') }}

{{ t('ch_no_agents') }}

{{ t('ch_connect') }} {{ chPlatformLabel(chFlow.platform) }}

{{ getClientAgentName(chFlow.agent_id) }}

{{ t('ch_tg_token_help') }}

{{ t('ch_wa_token_help') }}

{{ t('ch_line_token_help') }}

{{ t('ch_wx_scan_title') }}

{{ t('ch_wx_scan_desc') }}

WeChat QR Code
{{ t('ch_wx_loading_qr') }}
{{ t('ch_wx_waiting') }}

{{ t('ch_wx_desc') }}

WeChat

{{ t('ch_connected') }}

{{ getClientAgentName(wechatBinding.agent_id) }} · {{ wechatBinding.wechat_nickname }}

{{ channelLabel(conn.channel_type) }}

{{ conn.active ? t('ch_connected') : t('ch_disconnected') }}

{{ getClientAgentName(conn.agent_id) }}

{{ t('ch_no_connections') }}

{{ t('ch_no_connections_desc') }}

{{ t('ch_how_it_works') }}

{{ t('ch_how_it_works_desc') }}

{{ t('credits_title') }}

{{ t('credits_subtitle') }}

{{ t('credits_no_requests') }}

{{ t('credits_time') }} {{ t('credits_company') }} {{ t('credits_amount') }} {{ t('credits_price') }} {{ t('credits_payment') }} {{ t('credits_notes') }} {{ t('credits_status') }} {{ t('credits_actions') }}
{{ formatTime(cr.created_at) }} {{ cr.tenant_name || getTenantName(cr.tenant_id) }} {{ cr.credits }} ¥{{ cr.price }} {{ cr.payment_method }} {{ cr.payment_note || '—' }} {{ cr.status === 'approved' ? t('credits_status_approved') : cr.status === 'rejected' ? t('credits_status_rejected') : t('credits_status_pending') }}

{{ t('llm_title') }}

{{ t('llm_subtitle') }}

{{ currentLang === 'en' ? 'API Gateway — Models & Pricing' : 'API 中转站 — 模型与定价' }}

{{ currentLang === 'en' ? 'Public models the gateway exposes + their per-1K-token HKD prices. Charges hit the tenant on every call.' : '中转站对外暴露的模型 + 按 1K token 的 HKD 单价。每次调用都按当前价格扣 tenant 现金余额。' }}

{{ currentLang === 'en' ? 'Calls (30d)' : '调用次数 (30天)' }}

{{ apiGwAdminStats.calls }}

{{ currentLang === 'en' ? 'Revenue (30d)' : '收入 (30天)' }}

HK${{ apiGwAdminStats.revenue_hkd.toFixed(2) }}

{{ currentLang === 'en' ? 'Tokens in/out' : 'Token 输入/输出' }}

{{ apiGwAdminStats.input_tokens.toLocaleString() }} / {{ apiGwAdminStats.output_tokens.toLocaleString() }}

{{ currentLang === 'en' ? 'Error count' : '失败次数' }}

{{ apiGwAdminStats.errors }}

{{ currentLang === 'en' ? 'Display' : '展示名' }} {{ currentLang === 'en' ? 'Model ID' : '模型 ID' }} {{ currentLang === 'en' ? 'Channel · Deployment' : '渠道 · Deployment' }} {{ currentLang === 'en' ? 'In / 1M' : '输入 / 1M' }} {{ currentLang === 'en' ? 'Out / 1M' : '输出 / 1M' }} {{ currentLang === 'en' ? 'Enabled' : '启用' }} {{ currentLang === 'en' ? 'Actions' : '操作' }}
{{ m.display_name || m.model_name }} {{ m.model_name }} {{ m.upstream_channel }} · {{ m.upstream_model }} HK${{ (m.input_price_per_1k_hkd * 1000).toFixed(2) }} HK${{ (m.output_price_per_1k_hkd * 1000).toFixed(2) }} {{ m.is_enabled ? (currentLang === 'en' ? 'on' : '启用') : (currentLang === 'en' ? 'off' : '停用') }}
{{ currentLang === 'en' ? 'No models configured.' : '还没有配置任何模型。' }}

{{ t('llm_global_default') }}

{{ t('llm_global_default_desc') }}

{{ t('llm_effective') }}: {{ llmEffectiveModel }}

{{ t('llm_health_dashboard') }}

{{ h.display_name }} {{ h.healthy_keys }}/{{ h.total_keys }} {{ t('llm_active_keys') }}
{{ k.label || k.key_id.slice(0,8) }}
{{ k.avg_latency_ms }}ms {{ k.total_requests }} req {{ k.is_healthy ? t('llm_healthy') : k.circuit_open ? t('llm_circuit_open') : t('llm_unhealthy') }}

{{ t('llm_scenarios') }}

{{ t('llm_scenarios_desc') }}

{{ label }}

{{ t('llm_no_providers') }}

{{ p.display_name }} {{ p.name }}
{{ p.base_url }} P{{ p.priority }} {{ p.active_key_count }}/{{ p.key_count }} {{ t('llm_keys') }} {{ p.supported_models.join(', ') }}
{{ llmTestResult.success ? t('llm_test_success') : t('llm_test_failed') }} {{ llmTestResult.latency_ms }}ms {{ llmTestResult.model }} {{ llmTestResult.response }} {{ llmTestResult.error }}

{{ t('llm_keys') }} ({{ p.keys?.length || 0 }})

{{ t('llm_key_label') }} {{ t('llm_masked_key') }} {{ t('llm_requests') }} {{ t('llm_errors') }} {{ t('llm_enabled') }}
{{ k.label || '—' }} {{ k.masked_key }} {{ k.requests_count || 0 }} {{ k.errors_count || 0 }}
No API keys configured

{{ t('users_title') }}

{{ t('users_subtitle') }}

{{ t('users_name') }} {{ t('users_email') }} {{ t('users_role') }} {{ t('users_tenant') }} AI員工點數 {{ currentLang === 'en' ? 'API balance' : 'API 余额' }} {{ t('users_status') }} {{ t('users_actions') }}
{{ u.name.charAt(0) }}
{{ u.name }}
{{ u.email }} {{ u.role }} {{ u.tenant_id ? getTenantName(u.tenant_id) : '—' }}
{{ u.credits_balance || 0 }}
HK${{ (u.api_balance_hkd || 0).toFixed(2) }}
{{ u.is_active ? t('users_active') : t('users_disabled') }}

{{ t('users_recycle_empty') }}

{{ t('users_recycle_empty_desc') }}

{{ t('users_recycle_notice') }}

{{ t('users_name') }} {{ t('users_email') }} {{ t('users_role') }} {{ t('users_deleted_at') }} {{ t('users_expires') }} {{ t('users_actions') }}
{{ u.name.charAt(0) }}
{{ u.name }}
{{ u.email }} {{ u.role }} {{ formatTime(u.deleted_at) }} {{ u.days_remaining }} {{ t('users_days_left') }}