{{ 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') }}
{{ t('dash_quick_actions') }}
{{ t('dash_system_status') }}
{{ t('dash_active_employees') }}
{{ t('dash_no_active_employees') }}
{{ a.name }}
{{ getTenantName(a.tenant_id) }}
Daily Token Usage (30d)
Daily Conversations (30d)
Top Agents by Token Usage
{{ t('tenants_title') }}
{{ t('tenants_subtitle') }}
{{ t('tenants_no_clients') }}
{{ t('tenants_no_clients_desc') }}
{{ filteredTenants.length }} / {{ tenants.length }} clients
{{ tn.name }}
Credits
{{ (tn.credits_balance || 0).toLocaleString() }}
Plan
{{ tn.plan || 'free' }}
Profile
View
No employees yet
{{ t('workflows_title') }}
{{ t('workflows_subtitle') }}
{{ t('workflows_active') }}
{{ wf.name }}
{{ wf.description || t('workflows_no_description') }}
{{ t('workflows_template_library') }}
{{ industryLabel(industry) }}
{{ tpl.name }}
{{ tpl.display_name }}{{ tpl.description }}
{{ t('connectors_title') }}
{{ t('connectors_subtitle') }}
{{ t('connectors_active_bindings') }}
{{ t('connectors_no_bindings') }} {{ t('connectors_channels_tab') }} {{ t('connectors_tab_suffix') }}
{{ a.name }}
{{ t('connectors_available') }}
{{ c.name }}
{{ c.description }}
{{ t('connectors_actions') }}
{{ 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.' : '所有外部依赖的实时状态 + 未处理告警收件箱。' }}
{{ (s.status || 'unknown').toUpperCase() }}
{{ s.detail }}
{{ s.latency_ms }}ms
{{ currentLang === 'en' ? 'Tool Reliability (last 24h)' : '工具可用性 · 最近 24 小时' }}
| {{ 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' ? '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' : '告警' }}
{{ 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 }} |
| 暂无调用记录 | |||||||
总调用次数
{{ (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 }} 条记录
{{ selectedTenant.name }}
{{ industryLabel(selectedTenant.industry) }} · {{ selectedTenant.plan }}
{{ t('client_digital_employees') }}
{{ t('client_no_employees') }}
{{ a.name }}
{{ a.title || roleLabel(a.role) }} · {{ a.tasks_completed }} tasks
{{ t('client_connected_services') }}
Gmail / Google Workspace
{{ gmailStatus.email }}
Connect to read/send emails via AI agents
{{ t('client_no_connectors') }}
{{ connName(c.connector_type) }}
{{ c.connector_type }}
{{ 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' : '完整度' }}
{{ 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' : '行业' }}
cpUpdateCSV('target_industries', event.target.value)" :placeholder="currentLang === 'en' ? 'F&B, beauty, real estate' : '餐饮、美容、地产'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Customer sizes' : '客户规模' }}
cpUpdateCSV('target_sizes', event.target.value)" :placeholder="currentLang === 'en' ? 'SME, mid-market, enterprise' : '中小企业、中型企业、大型企业'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Decision-maker roles' : '决策人角色' }}
cpUpdateCSV('target_roles', event.target.value)" :placeholder="currentLang === 'en' ? 'Marketing Director, CEO, Founder' : '市场总监、CEO、创始人'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Geographies' : '地域' }}
cpUpdateCSV('target_geographies', event.target.value)" :placeholder="currentLang === 'en' ? 'HK, Greater Bay Area, Southeast Asia' : '香港、大湾区、东南亚'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? '5 · Voice / Tone' : '五、口吻 / 风格' }}
{{ cpSectionProgress('voice_tone') }}{{ currentLang === 'en' ? 'Forbidden phrases' : '禁用词' }}
cpUpdateCSV('forbidden_phrases', event.target.value)" :placeholder="currentLang === 'en' ? 'cutting-edge, world-class, revolutionary' : '业界领先、颠覆性、赋能'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ 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.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 }}
{{ selectedAgent.title || roleLabel(selectedAgent.role) }} · {{ getTenantName(selectedAgent.tenant_id) }}
{{ selectedAgent.name }}
AI Video Producer
Models
Seedance 2.0 / 2.0 Fast
Resolution
720p / 1080p
Duration
4 – 15 seconds
{{ t('vc_ref_image') || 'Reference' }}
Multi-Image (up to 5)
{{ t('vc_config_rules') || 'Video Generation Rules' }}
{{ t('vc_config_rules_desc') || 'Custom instructions for how this employee handles video creation requests.' }}
{{ t('vc_config_auto_gen') || 'Agent-Initiated Generation' }}
{{ t('vc_config_auto_gen_desc') || 'Allow employee to propose video generation. You will be asked to approve before credits are spent.' }}
{{ selectedAgent.name }}
{{ t('fa_subtitle') }}
{{ t('fa_cap_us') }}
NYSE · NASDAQ
{{ t('fa_cap_hk') }}
HKEX
{{ t('fa_cap_data') }}
{{ t('fa_cap_data_val') }}
{{ t('fa_cap_output') }}
{{ t('fa_cap_output_val') }}
{{ t('fa_config_rules') }}
{{ t('fa_config_rules_desc') }}
{{ t('agent_system_prompt') }}
{{ t('agent_system_prompt_desc') }}
{{ t('agent_behavior_rules') }}
{{ t('agent_behavior_rules_desc') }}
{{ t('agent_model_settings') }}
{{ t('agent_builtin_title') || 'Built-in Capabilities' }}
{{ t('agent_default_on') || 'Default ON' }}{{ t('agent_builtin_desc') || 'These capabilities are enabled by default. Toggle off only if you want to restrict this employee.' }}
{{ t('agent_kb_enabled_desc') }}
{{ t('agent_workflow') }}
{{ t('agent_workflow_desc') }}
{{ t('agent_autonomous_desc') }}
{{ t('agent_workflow_desc2') }}
{{ t('agent_pause_external') }}
{{ t('agent_pause_external_desc') }}
{{ 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) }}
{{ 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_cs_settings') }}
{{ t('cap_cs_desc') }}
{{ t('appt_title') }}
{{ t('tpl_appointment_desc') }}
{{ t('sales_title') }}
{{ t('tpl_sales_desc') }}
{{ 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') }}
{{ 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' : '已完成' }} |
{{ t('chat_with') }} {{ selectedAgent.name }}
{{ t('chat_test_desc') }}
{{ t('chat_start') }}
{{ t('chat_send_test') }} {{ selectedAgent.name }}{{ t('chat_send_test_suffix') }}
{{ t('appt_title') }}
{{ t('appt_business_hours') }}
Reminders
{{ t('appt_services_title') }}
{{ t('appt_services_desc') }}
{{ svc.description }}
{{ t('appt_appointments_title') }}
| Customer | Service | Time | Status | Actions |
|---|---|---|---|---|
| {{ apt.customer_name }} | {{ apptServiceName(apt.service_id) }} | {{ formatTime(apt.start_time) }} | {{ t('appt_status_'+apt.status) || apt.status }} |
{{ t('appt_tab_overrides') }}
{{ t('sales_title') }}
{{ s.stage }}
{{ s.count }}
{{ s.total_value ? ('HKD ' + s.total_value.toLocaleString()) : '' }}
{{ t('sales_pipeline_title') }}
{{ t('sales_pipeline_desc') }}
{{ t('sales_leads_title') }}
| {{ t('sales_lead_name') }} | {{ t('sales_lead_company') }} | {{ t('sales_lead_status') }} | {{ t('sales_lead_score') }} | {{ t('sales_lead_source') }} | Actions |
|---|---|---|---|---|---|
| {{ lead.name }} | {{ lead.company || '-' }} | {{ lead.status }} | {{ lead.score || '-' }} | {{ lead.source }} |
{{ t('sales_products_title') }}
{{ t('sales_products_desc') }}
{{ prod.description }}
{{ t('sales_activities_title') }}
{{ act.description }}
{{ formatTime(act.created_at) }} Auto
{{ t('usage_total_calls') }}
{{ agentUsage.call_count || 0 }}
{{ t('usage_total_tokens') }}
{{ formatTokens(agentUsage.total_tokens || 0) }}
{{ t('usage_prompt_tokens') }}
{{ formatTokens(agentUsage.prompt_tokens || 0) }}
{{ t('usage_completion_tokens') }}
{{ formatTokens(agentUsage.completion_tokens || 0) }}
{{ t('usage_total_calls') }}
{{ agentUsage.call_count || 0 }}
{{ t('usage_credits_used') || '点数消耗' }}
{{ Math.ceil((agentUsage.total_tokens || 0) / 1000) }}
{{ t('usage_recent') }}
| {{ 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') }}
{{ (csSelectedAgent && csSelectedAgent.name) ? csSelectedAgent.name : (currentLang === 'en' ? 'CS Employee Setup' : '客服员工配置') }}
{{ currentLang === 'en' ? 'Persona, greetings, hours, escalation, safety, knowledge, channels, style — everything this employee needs to handle real customers.' : '身份口吻 / 问候 / 营业时间 / 客户上报 / 安全规则 / 知识 / 渠道接入 / 风格 — 让这位员工真正能接客户的全部设定。' }}
Select an agent to configure
Choose an agent above to set up AI customer service
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.
Working Hours
Set business hours for human availability. The AI still responds 24/7 but adjusts messaging.
Escalation Rules
Define when the AI should transfer to a human agent.
Email will receive escalation notifications with conversation summary
Safety & Guardrails
Protect your brand by controlling what the agent can and cannot discuss.
Language Settings
Configure which languages the agent supports and how it detects language.
Connected Channels
Manage messaging channels that this CS agent listens on.
{{ ch.channel_type }}
{{ 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 }}
Connect Telegram Bot
Create a bot via @BotFather on Telegram, then paste the bot token below. Webhook will be configured automatically.
Connect WeChat Official Account
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.
Response Style
Control how the agent formats and structures responses.
{{ 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' ? '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' ? '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] }}
{{ csWizardSteps[csWizardStep]?.label }}
{{ csWizardSteps[csWizardStep]?.desc }}
Agent: {{ csSelectedAgent.name }} — {{ csSelectedAgent.role || 'AI Agent' }}
Agent identity comes from its profile. Only company context needs to be set here.
{{ t('sm_title') }}
{{ t('sm_subtitle') }}
{{ t('sm_select_agent') }}
{{ t('sm_select_agent_desc') }}
{{ t('sm_no_agent') }}
{{ t('sm_workflow_title') }}
{{ t('sm_workflow_desc') }}
{{ t('sm_wf_intelligent_desc') }}
{{ t('sm_wf_intelligent_note') }}
{{ t('sm_wf_no_platforms_hint') }}
{{ t('sm_workflow_empty') }}
{{ t('sm_wf_add_step_hint') }}
{{ t('sm_platforms_title') }}
{{ t('sm_platforms_desc') }}
{{ platform.name || platform.type }}
{{ platform.account_id || 'Connected' }}
{{ t('sm_connect') }} {{ smBindForm.platform_type.replace('_', ' ') }}
{{ t('sm_meta_api_note') }}
{{ t('sm_telegram_note') }}
{{ t('sm_tiktok_note') }}
{{ t('sm_strategy_title') }}
{{ t('sm_strategy_desc') }}
{{ t('sm_media_title') }}
{{ t('sm_media_desc') }}
{{ t('sm_upload_files') }}
{{ t('sm_upload_hint') }}
{{ t('sm_recent_uploads') }}
{{ t('sm_no_media') }}
{{ file.name }}
{{ t('sm_publishing_title') }}
{{ t('sm_publishing_desc') }}
{{ t('sm_queue_empty') }}
{{ t('sm_queue_empty_hint') }}
{{ item.content }}
AI Customer Service
Configure your AI customer service agents
Company Context
Greetings
Same configuration panels as admin view — all tabs functional
{{ t('workspace_title') }}
{{ t('workspace_subtitle') }}
{{ agent.name }}
{{ getWsChatLastTime(agent.id) }}{{ agent.title || agentRoleLabel(agent.role) }}
{{ chat.agentName }}
{{ t('vc_subtitle') }}
{{ t('ws_drop_files') }}
{{ chat.agentName }}
{{ t('vc_chat_hint') }}
{{ chat.loadingStatus?.text || 'Thinking...' }}
Action Requires Approval
{{ chat.pendingApproval.tool }}
{{ vcAgentStatus }}
{{ t('vc_generating') }}...
{{ r.duration_seconds || vcDuration }}s video
{{ t('vc_generation_failed') }}
{{ r.error }}
{{ r.prompt || r.title }}
{{ t('vc_generating') }}…
{{ r.prompt || r.title }}
{{ t('vc_panel_title') }}
{{ t('vc_panel_hint') }}
{{ t('vc_ref_images_hint') }}
{{ chat.agentName }}
{{ t('fa_subtitle') }}
{{ t('ws_drop_files') }}
{{ t('fa_chat_hint') }}
{{ t('fa_watchlist_empty') }}
{{ t('fa_watchlist_empty_hint') }}
{{ item.name }}
${{ faGetPrice(item.ticker).price?.toFixed(2) }}
{{ (faGetPrice(item.ticker).change_percent||0) >= 0 ? '+' : '' }}{{ (faGetPrice(item.ticker).change_percent||0).toFixed(2) }}%
{{ chat._faStatus }}
{{ t('fa_section_overview') }}
{{ t('fa_section_financials') }}
{{ t('fa_section_news') }}
{{ faViewingReport.title }}
{{ faViewingReport.market === 'HK' ? 'HKEX' : 'NYSE/NASDAQ' }} · {{ faViewingReport.ticker }} · {{ faViewingReport.report_type }}
{{ chat._faReport.title }}
{{ chat._faReport.market === 'HK' ? 'HKEX' : 'NYSE/NASDAQ' }} · {{ chat._faReport.ticker }} · {{ chat._faReport.report_type }}
{{ t('fa_saved_reports') }}
{{ rpt.title }}
{{ rpt.summary || rpt.title }}
{{ t('fa_panel_empty_title') }}
{{ t('fa_panel_empty_desc') }}
Holdings
{{ faPortfolio.length }}
Total Value
${{ faPortfolio.reduce((s,h) => { const p = faGetPrice(h.ticker); return s + (p?.price ? p.price * h.shares : h.avg_cost * h.shares) }, 0).toLocaleString(undefined, {minimumFractionDigits:0, maximumFractionDigits:0}) }}
Total P&L
{{ faPortfolio.reduce((s,h) => { const c = faCalcPnL(h); return s + (c ? parseFloat(c.pnl) : 0) }, 0) >= 0 ? '+' : '' }}${{ Math.abs(faPortfolio.reduce((s,h) => { const c = faCalcPnL(h); return s + (c ? parseFloat(c.pnl) : 0) }, 0)).toLocaleString(undefined, {minimumFractionDigits:2, maximumFractionDigits:2}) }}
{{ t('fa_portfolio_empty') }}
{{ t('fa_portfolio_empty_hint') }}
{{ h.name }}
${{ faGetPrice(h.ticker).price?.toFixed(2) }}
{{ parseFloat(faCalcPnL(h).pnl) >= 0 ? '+' : '' }}${{ faCalcPnL(h).pnl }}
{{ parseFloat(faCalcPnL(h).pnlPct) >= 0 ? '+' : '' }}{{ faCalcPnL(h).pnlPct }}%
{{ t('fa_alerts_empty') }}
{{ t('fa_alerts_empty_hint') }}
{{ a.condition === 'above' ? '↑ Above' : '↓ Below' }} ${{ a.target_price?.toFixed(2) }} · {{ a.name }}
{{ 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' ? "Today's conversations" : '今日對話' }}
{{ csDashData?.kpis?.conversations_today ?? 0 }}
{{ currentLang === 'en' ? 'New 24h windows opened today' : '今日新開 24h 對話窗口' }}
{{ currentLang === 'en' ? 'Pending replies' : '待回覆' }}
{{ csDashData?.kpis?.pending_replies ?? 0 }}
{{ currentLang === 'en' ? 'Open threads with unread customer messages' : '未讀客戶訊息' }}
{{ currentLang === 'en' ? 'Bookings today' : '今日預約' }}
{{ csDashData?.kpis?.appointments_today ?? 0 }}
{{ currentLang === 'en' ? 'Confirmed by the AI today' : 'AI 員工今日確認嘅預約' }}
{{ currentLang === 'en' ? "Today's revenue" : '今日收款' }}
HK${{ (csDashData?.kpis?.paid_amount_hkd_today ?? 0).toLocaleString() }}
{{ currentLang === 'en' ? 'Paid via Stripe links sent by the AI' : 'AI 收款連結今日已收到' }}
{{ currentLang === 'en' ? 'Plan usage this month' : '本月用量' }}
{{ csDashData.cap_usage.used }} / {{ csDashData.cap_usage.cap }} {{ currentLang === 'en' ? 'conversations' : '條對話' }}
{{ currentLang === 'en' ? 'Plan' : '方案' }}: {{ csDashData.cap_usage.plan }} · {{ currentLang === 'en' ? 'Renews' : '下次更新' }} {{ new Date(csDashData.cap_usage.period_end).toLocaleDateString() }}
{{ currentLang === 'en' ? 'Upcoming appointments' : '即將到嘅預約' }}
{{ currentLang === 'en' ? 'Next 7 days' : '未來 7 日' }}{{ appt.customer_name }}
{{ new Date(appt.start_time).toLocaleString(undefined, { weekday: 'short', hour: '2-digit', minute: '2-digit' }) }}
{{ currentLang === 'en' ? 'Pending payment links' : '未付款連結' }}
{{ currentLang === 'en' ? 'Worth nudging' : '可能要追單' }}{{ pr.customer_name || (currentLang === 'en' ? 'Customer' : '客戶') }}
{{ pr.amount_display }}
{{ pr.description }}
{{ currentLang === 'en' ? 'Open link →' : '打開連結 →' }}{{ t('client_dash_credits') }}
{{ effectiveCredits.toLocaleString() }}
{{ effectiveCredits <= 0 ? t('client_dash_recharge') : t('client_dash_remaining') }}
{{ t('client_dash_active_employees') }}
{{ clientAgentsList.filter(a => a.status === 'active').length }}/{{ clientAgentsList.length }}
{{ t('client_dash_online') }}
{{ t('client_dash_total_conversations') }}
{{ (clientDashData.usage?.call_count || 0).toLocaleString() }}
{{ t('client_dash_conversations') }}
{{ t('client_dash_total_credits_used') }}
{{ (clientDashData.usage?.credits_used || 0).toLocaleString() }}
{{ t('client_dash_credits_spent') }}
{{ t('client_dash_quick_chat') }}
{{ a.name }}
{{ t('client_dash_recent_activity') }}
{{ getClientAgentName(r.agent_id) || t('client_dash_conversation') }}
{{ new Date(r.created_at).toLocaleString() }}
{{ Math.ceil((r.total_tokens || 0) / 1000) }} {{ t('client_dash_credits_unit') }}
{{ t('brand_logo_hint') }}
{{ t('brand_logo_light') }}
{{ t('brand_logo_dark') }}
{{ t('brand_color_hint') }}
{{ 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' ? 'Tools fired' : '已調用工具' }}:
{{ currentLang === 'en' ? 'Inbox' : '收件箱' }}
{{ currentLang === 'en' ? 'See what your AI employees are saying. Take over any conversation in one click.' : '睇下你嘅 AI 員工同客人傾緊咩。隨時一鍵接管。' }}
{{ t('client_agents_title') }}
{{ t('client_agents_subtitle') }}
{{ t('client_agents_not_configured') }}
{{ t('client_agents_not_configured_desc') }}
{{ t('agent_recycle_empty') }}
{{ t('agent_recycle_empty_desc') }}
| {{ t('users_name') }} | {{ t('users_role') }} | {{ t('users_deleted_at') }} | {{ t('users_days_left') }} | {{ t('users_actions') }} |
|---|---|---|---|---|
| {{ a.name }} | {{ a.role }} | {{ a.deleted_at ? new Date(a.deleted_at).toLocaleString() : '-' }} | {{ a.days_remaining }}d |
|
{{ t('client_agents_no_assigned') }}
{{ t('client_agents_no_assigned_desc') }}
{{ a.name }}
{{ a.title || agentRoleLabel(a.role) }}
{{ a.description || t('agent_no_description') }}
{{ 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 }}
{{ 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、挑一个模型、发一句话。跟正常调用一样会从余额扣费。' }}
{{ 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' ? 'Recent calls' : '最近调用' }}
{{ currentLang === 'en' ? 'Recent top-ups' : '最近充值' }}
{{ t('client_usage_title') }}
{{ t('client_usage_subtitle') }}
{{ t("client_usage_not_configured") }}
{{ t("client_usage_not_configured_desc") }}
{{ t('client_usage_total_conversations') }}
{{ (clientDashData.usage?.call_count || 0).toLocaleString() }}
{{ t('client_dash_conversations') || '' }}
{{ t('client_usage_credits_used') }}
{{ (clientDashData.usage?.credits_used || 0).toLocaleString() }}
{{ t('client_dash_credits_spent') || '' }}
{{ t('client_usage_credits_remaining') }}
{{ (clientDashData.credits?.balance || 0).toLocaleString() }}
{{ t('client_dash_remaining') || '' }}
{{ t('client_usage_recent') }}
{{ t('client_usage_no_records') }}
| {{ t('client_usage_time') }} | {{ t('client_usage_employee') }} | {{ t('client_usage_source') }} | {{ t('client_usage_credits') }} |
|---|---|---|---|
| {{ formatTime(r.created_at) }} | {{ getClientAgentName(r.agent_id) }} | {{ r.source }} | {{ Math.ceil(r.total_tokens / 1000) }} |
{{ 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('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' }}
{{ 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.url }}
{{ 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' : '完整度' }}
{{ 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' : '行业' }}
cpUpdateCSV('target_industries', event.target.value)" :placeholder="currentLang === 'en' ? 'F&B, beauty, real estate' : '餐饮、美容、地产'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Customer sizes' : '客户规模' }}
cpUpdateCSV('target_sizes', event.target.value)" :placeholder="currentLang === 'en' ? 'SME, mid-market, enterprise' : '中小企业、中型企业、大型企业'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Decision-maker roles' : '决策人角色' }}
cpUpdateCSV('target_roles', event.target.value)" :placeholder="currentLang === 'en' ? 'Marketing Director, CEO, Founder' : '市场总监、CEO、创始人'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? 'Geographies' : '地域' }}
cpUpdateCSV('target_geographies', event.target.value)" :placeholder="currentLang === 'en' ? 'HK, Greater Bay Area, Southeast Asia' : '香港、大湾区、东南亚'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ currentLang === 'en' ? '5 · Voice / Tone' : '五、口吻 / 风格' }}
{{ cpSectionProgress('voice_tone') }}{{ currentLang === 'en' ? 'Forbidden phrases' : '禁用词' }}
cpUpdateCSV('forbidden_phrases', event.target.value)" :placeholder="currentLang === 'en' ? 'cutting-edge, world-class, revolutionary' : '业界领先、颠覆性、赋能'" class="w-full px-3 py-1.5 rounded-lg text-[12.5px] outline-none" style="background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08); color: rgba(255,255,255,0.9);">{{ 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') }}
{{ 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') }}
{{ t('ch_wx_desc') }}
{{ 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_health_dashboard') }}
{{ t('llm_scenarios') }}
{{ t('llm_scenarios_desc') }}
{{ t('llm_no_providers') }}
{{ 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 }} |
{{ t('llm_add_from_preset') }}
{{ llmEditingId ? t('llm_edit_provider') : t('llm_create_provider') }}
{{ 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') }} |
|