从 ClickUp 和 Google Sheets 同步 KPI 指标到 Slack 和 Gmail
高级
这是一个自动化工作流,包含 20 个节点。主要使用 Set, Code, Cron, Gmail, Merge 等节点。 将 KPI 指标从 ClickUp 和 Google Sheets 同步至 Slack 和 Gmail
前置要求
- •Google 账号和 Gmail API 凭证
- •Slack Bot Token 或 Webhook URL
- •Google Sheets API 凭证
分类
-
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"id": "H39e9rSjzJIcbpt4",
"meta": {
"instanceId": "8443f10082278c46aa5cf3acf8ff0f70061a2c58bce76efac814b16290845177",
"templateCredsSetupCompleted": true
},
"name": "从ClickUp和Google Sheets同步KPI指标到Slack和Gmail",
"tags": [],
"nodes": [
{
"id": "7cad3d2c-f079-481d-bfe3-cdc1f7c68e2f",
"name": "工作流概览",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3360,
-896
],
"parameters": {
"color": 4,
"width": 360,
"height": 672,
"content": "## 📊 从ClickUp和Google Sheets同步KPI指标到Slack和Gmail"
},
"typeVersion": 1
},
{
"id": "aa0e932d-ad35-435e-9305-d806dcc81a28",
"name": "Cron触发器说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2896,
-896
],
"parameters": {
"height": 200,
"content": "## ⏰ 每日触发器"
},
"typeVersion": 1
},
{
"id": "67817fbe-5283-4be8-b0f6-22e452afefc2",
"name": "ClickUp任务说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2560,
-1072
],
"parameters": {
"height": 280,
"content": "## 🔗 获取ClickUp任务"
},
"typeVersion": 1
},
{
"id": "b38f8201-079c-4bd9-8b2a-41a1f7b5b8ea",
"name": "Google Sheets说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2608,
-384
],
"parameters": {
"height": 248,
"content": "## 📑 获取潜在客户数据"
},
"typeVersion": 1
},
{
"id": "6f599e7f-063d-45cb-9e92-17f7c7c86f33",
"name": "合并数据说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2224,
-896
],
"parameters": {
"height": 192,
"content": "## 🔀 合并数据"
},
"typeVersion": 1
},
{
"id": "4f4b18ab-89c0-4ab6-8d0b-35583341f914",
"name": "KPI计算说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2064,
-480
],
"parameters": {
"width": 260,
"height": 304,
"content": "## 🧮 KPI分析"
},
"typeVersion": 1
},
{
"id": "07cbcf4f-3de8-40cb-9b96-3fdeb452474d",
"name": "数据格式化说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1856,
-880
],
"parameters": {
"height": 180,
"content": "## 📤 格式化数据"
},
"typeVersion": 1
},
{
"id": "738d9580-b855-43fd-8be1-986759fae45e",
"name": "Slack仪表板说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1408,
-928
],
"parameters": {
"height": 180,
"content": "## 💬 Slack摘要"
},
"typeVersion": 1
},
{
"id": "9780b484-e977-44d3-b123-43f74d412cb3",
"name": "Gmail报告说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1392,
-496
],
"parameters": {
"height": 248,
"content": "## 📧 邮件报告"
},
"typeVersion": 1
},
{
"id": "cfc0967b-c5cc-49a6-9480-01615b9b449d",
"name": "错误处理说明",
"type": "n8n-nodes-base.stickyNote",
"position": [
-3024,
96
],
"parameters": {
"height": 180,
"content": "## ⚠️ 错误警报"
},
"typeVersion": 1
},
{
"id": "a9ce4348-b49b-47b8-a723-72328fc7c928",
"name": "错误触发器处理程序",
"type": "n8n-nodes-base.errorTrigger",
"position": [
-2720,
96
],
"parameters": {},
"typeVersion": 1
},
{
"id": "172f269d-41ce-4d11-8d2d-3442315a8d2d",
"name": "Slack - 发送错误警报",
"type": "n8n-nodes-base.slack",
"position": [
-2496,
96
],
"webhookId": "1abab466-eb85-4d88-a60d-c9028cff110b",
"parameters": {
"text": "=⌠*KPI Dashboard Workflow Failed*\n\n*Error Details:*\n{{ $json.error?.message || 'Unknown error occurred' }}\n\n*Failed Node:* {{ $json.node?.name || 'Unknown node' }}\n*Execution ID:* {{ $execution.id }}\n*Timestamp:* {{ $now.format('YYYY-MM-DD HH:mm:ss') }}\n\n*Action Required:*\n1. Check execution logs in n8n\n2. Verify data source connections\n3. Review node configurations\n4. Restart workflow if needed\n\n_Automated alert from KPI Dashboard System_",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "YOUR_ALERTS_CHANNEL_ID",
"cachedResultName": "workflow-alerts"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"id": "rNqvWj9TfChPVRYY",
"name": "Slack account vivek"
}
},
"typeVersion": 2
},
{
"id": "d7e79592-f836-4053-b917-c741784a61dd",
"name": "每日Cron触发器",
"type": "n8n-nodes-base.cron",
"position": [
-2672,
-672
],
"parameters": {},
"typeVersion": 1
},
{
"id": "9cad37f4-6719-439d-9652-5eeec89ad983",
"name": "ClickUp - 获取任务",
"type": "n8n-nodes-base.clickUp",
"position": [
-2448,
-768
],
"parameters": {
"list": "901611392518",
"team": "9016683627",
"limit": 5,
"space": "90162844741",
"folder": "90164394824",
"filters": {
"subtasks": false
},
"operation": "getAll",
"returnAll": false,
"authentication": "oAuth2"
},
"credentials": {
"clickUpOAuth2Api": {
"id": "6UikDwF6BMx3ln9O",
"name": "ClickUp account"
}
},
"typeVersion": 1
},
{
"id": "d5bb389e-dc40-45ad-bd5e-7c59d9a536c9",
"name": "Google Sheets - 获取潜在客户数据",
"type": "n8n-nodes-base.googleSheets",
"position": [
-2448,
-576
],
"parameters": {
"options": {},
"sheetName": {
"__rl": true,
"mode": "list",
"value": 1856159589,
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/17rcNd_ZpUQLm0uWEVbD-NY6GyFUkrD4BglvawlyBygM/edit#gid=1856159589",
"cachedResultName": "KPI Data"
},
"documentId": {
"__rl": true,
"mode": "list",
"value": "17rcNd_ZpUQLm0uWEVbD-NY6GyFUkrD4BglvawlyBygM",
"cachedResultUrl": "https://docs.google.com/spreadsheets/d/17rcNd_ZpUQLm0uWEVbD-NY6GyFUkrD4BglvawlyBygM/edit?usp=drivesdk",
"cachedResultName": "sample_leads_50"
}
},
"credentials": {
"googleSheetsOAuth2Api": {
"id": "kpPEOLCGn963qpoh",
"name": "automations@techdome.ai"
}
},
"typeVersion": 3
},
{
"id": "75c5f34a-d851-4eae-bff7-7289d608a03c",
"name": "合并 - 整合数据集",
"type": "n8n-nodes-base.merge",
"position": [
-2224,
-672
],
"parameters": {},
"typeVersion": 1
},
{
"id": "fa353353-2fd0-424a-a09a-639831c2dabc",
"name": "代码 - 计算KPI趋势",
"type": "n8n-nodes-base.code",
"position": [
-2000,
-672
],
"parameters": {
"jsCode": "// n8n Code Node - Compute KPI Trends\n// This code analyzes ClickUp tasks and lead data to generate KPIs\n\nconst items = $input.all();\nconst data = items.map(item => item.json);\n\n// Separate tasks and leads\nconst tasks = data.filter(item => item.id && item.id.startsWith('86d0'));\nconst leads = data.filter(item => item.leadId);\n\n// Initialize KPI object\nconst kpis = {\n timestamp: new Date().toISOString(),\n tasks: {},\n leads: {},\n overall: {}\n};\n\n// ===== TASK KPIs =====\nif (tasks.length > 0) {\n // Status distribution\n const statusCount = {};\n const priorityCount = {};\n const assigneeCount = {};\n let totalTimeSpent = 0;\n let tasksWithTime = 0;\n let overdueTasks = 0;\n const today = Date.now();\n\n tasks.forEach(task => {\n // Status counting\n const status = task.status?.status || 'unknown';\n statusCount[status] = (statusCount[status] || 0) + 1;\n\n // Priority counting\n const priority = task.priority?.priority || 'none';\n priorityCount[priority] = (priorityCount[priority] || 0) + 1;\n\n // Assignee counting\n task.assignees?.forEach(assignee => {\n assigneeCount[assignee.username] = (assigneeCount[assignee.username] || 0) + 1;\n });\n\n // Time tracking\n if (task.time_spent) {\n totalTimeSpent += task.time_spent;\n tasksWithTime++;\n }\n\n // Overdue calculation\n if (task.due_date && parseInt(task.due_date) < today && !task.date_done) {\n overdueTasks++;\n }\n });\n\n kpis.tasks = {\n total: tasks.length,\n byStatus: statusCount,\n byPriority: priorityCount,\n byAssignee: assigneeCount,\n overdue: overdueTasks,\n overduePercentage: ((overdueTasks / tasks.length) * 100).toFixed(2),\n avgTimeSpent: tasksWithTime > 0 ? Math.round(totalTimeSpent / tasksWithTime) : 0,\n avgTimeSpentHours: tasksWithTime > 0 ? (totalTimeSpent / tasksWithTime / 3600000).toFixed(2) : 0,\n completion: {\n done: statusCount['done'] || 0,\n inProgress: statusCount['in progress'] || 0,\n finalReview: statusCount['final review'] || 0,\n backlogs: statusCount['backlogs'] || 0\n }\n };\n}\n\n// ===== LEAD KPIs =====\nif (leads.length > 0) {\n const sourceCount = {};\n const statusCount = {};\n const responsesByDate = {};\n let positiveResponses = 0;\n let negativeResponses = 0;\n let neutralResponses = 0;\n\n // Keywords for sentiment analysis\n const positiveKeywords = ['interested', 'love', 'promising', 'sounds interesting', 'count me in', 'yes', 'great'];\n const negativeKeywords = ['not interested', 'not sure', 'concerns', 'difficult', 'already have'];\n\n leads.forEach(lead => {\n // Source counting\n const source = lead.source || 'unknown';\n sourceCount[source] = (sourceCount[source] || 0) + 1;\n\n // Status counting\n const status = lead.status || 'unknown';\n statusCount[status] = (statusCount[status] || 0) + 1;\n\n // Date grouping\n const date = lead.replyDate || 'unknown';\n responsesByDate[date] = (responsesByDate[date] || 0) + 1;\n\n // Simple sentiment analysis\n if (lead.reply) {\n const replyLower = lead.reply.toLowerCase();\n const hasPositive = positiveKeywords.some(kw => replyLower.includes(kw));\n const hasNegative = negativeKeywords.some(kw => replyLower.includes(kw));\n\n if (hasPositive && !hasNegative) {\n positiveResponses++;\n } else if (hasNegative && !hasPositive) {\n negativeResponses++;\n } else {\n neutralResponses++;\n }\n }\n });\n\n kpis.leads = {\n total: leads.length,\n bySource: sourceCount,\n byStatus: statusCount,\n byDate: responsesByDate,\n sentiment: {\n positive: positiveResponses,\n negative: negativeResponses,\n neutral: neutralResponses,\n positiveRate: ((positiveResponses / leads.length) * 100).toFixed(2),\n negativeRate: ((negativeResponses / leads.length) * 100).toFixed(2)\n },\n topSources: Object.entries(sourceCount)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([source, count]) => ({ source, count }))\n };\n}\n\n// ===== OVERALL KPIs =====\nkpis.overall = {\n totalItems: data.length,\n tasksCount: tasks.length,\n leadsCount: leads.length,\n dataQuality: {\n tasksWithDueDate: tasks.filter(t => t.due_date).length,\n tasksWithAssignees: tasks.filter(t => t.assignees?.length > 0).length,\n leadsWithCompany: leads.filter(l => l.company).length,\n leadsWithPhone: leads.filter(l => l.phone).length\n }\n};\n\n// ===== TREND INDICATORS =====\nkpis.trends = {\n taskTrend: tasks.length > 0 ? 'stable' : 'no data',\n leadTrend: leads.length > 5 ? 'active' : 'low activity',\n urgentTasks: tasks.filter(t => t.priority?.priority === 'urgent').length,\n newReplies: leads.filter(l => l.status === 'New Reply').length\n};\n\n// Return as single item with comprehensive KPI data\nreturn [{ json: kpis }];"
},
"typeVersion": 1
},
{
"id": "414f7e49-df31-4690-950c-d7f451fc6014",
"name": "设置 - 格式化输出数据",
"type": "n8n-nodes-base.set",
"position": [
-1776,
-672
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "dbb44ee9-0676-45ac-8c9e-e1ab1b801a6e",
"name": "tasks",
"type": "object",
"value": "={{ $json.tasks }}"
},
{
"id": "63779c8a-c71e-4267-9ec0-22098ecabd28",
"name": "tasks.overdue",
"type": "number",
"value": "={{ $json.tasks.overdue }}"
},
{
"id": "5d3f4a88-1526-4cb3-bc24-963efd1e50bf",
"name": "tasks.overduePercentage",
"type": "string",
"value": "={{ $json.tasks.overduePercentage }}"
},
{
"id": "709e0406-4d96-49ab-b0d1-40e35bc876f1",
"name": "tasks.avgTimeSpent",
"type": "number",
"value": "={{ $json.tasks.avgTimeSpent }}"
},
{
"id": "6aebe018-4dd6-4ca9-826a-8290c7b3c355",
"name": "tasks.avgTimeSpentHours",
"type": "string",
"value": "={{ $json.tasks.avgTimeSpentHours }}"
},
{
"id": "c5912159-c328-4403-8e50-8ae94511d307",
"name": "tasks.completion",
"type": "object",
"value": "={{ $json.tasks.completion }}"
},
{
"id": "0141ac77-5e6d-46f1-a8fe-8e781f33a632",
"name": "leads",
"type": "object",
"value": "={{ $json.leads }}"
},
{
"id": "54567372-ce0a-4aa1-b163-aed6f0b1d8db",
"name": "leads.topSources",
"type": "array",
"value": "={{ $json.leads.topSources }}"
},
{
"id": "6ea32021-40ad-454e-ad96-f905f8cf9967",
"name": "overall",
"type": "object",
"value": "={{ $json.overall }}"
},
{
"id": "f77c2c65-3b7e-43f1-9bbe-71b7e60468ea",
"name": "trends",
"type": "object",
"value": "={{ $json.trends }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "5449a5fb-aa27-4280-8e2f-dc1333b4611f",
"name": "Slack - 发布仪表板快照",
"type": "n8n-nodes-base.slack",
"position": [
-1552,
-768
],
"webhookId": "d987216d-dd09-4518-aacd-01fbda90908f",
"parameters": {
"text": "=📊 *Daily KPI Dashboard Updated*\n\n*Task Metrics:* \n• Total Task: {{ $json.tasks.total }} \n• Overdue: {{ $json.tasks.overdue }} ({{ $json.tasks.overduePercentage }}%)\n• Average Time Spent: {{ $json.tasks.avgTimeSpentHours }} hours\n\n*Lead Generation:*\n• Total: {{ $json.leads.total }} leads \n• Positive Sentiment: {{ $json.leads.sentiment.positiveRate }}%\n• New Replies: {{ $json.trends.newReplies }}\n\n*Top Sources:*\n{{ Object.entries($json.leads.bySource).map(([source, count]) => `• ${source}: ${count}`).join('\\n') }}\n\n*Overall Status:*\n• Total Items: {{ $json.overall.totalItems }} \n• Tasks: {{ $json.overall.tasksCount }} | Leads: {{ $json.overall.leadsCount }}\n• Urgent Tasks: {{ $json.trends.urgentTasks }}\n\n_Report generated at {{ $now.format('HH:mm') }}_",
"select": "channel",
"channelId": {
"__rl": true,
"mode": "list",
"value": "C09GNB90TED",
"cachedResultName": "general-information"
},
"otherOptions": {}
},
"credentials": {
"slackApi": {
"id": "rNqvWj9TfChPVRYY",
"name": "Slack account vivek"
}
},
"typeVersion": 2
},
{
"id": "3853cf8a-ca5c-44e0-93d1-b31f6c222b10",
"name": "Gmail - 发送KPI报告",
"type": "n8n-nodes-base.gmail",
"position": [
-1552,
-576
],
"webhookId": "ac83548d-967a-4ebc-b011-25668788c29c",
"parameters": {
"sendTo": "={{ $env.REPORT_EMAIL || 'your-email@example.com' }}",
"message": "=<!DOCTYPE html> <html lang=\"en\"> <head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>Daily KPI Report</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; background-color: #f4f4f4; } .container { background-color: #ffffff; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); overflow: hidden; } .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; } .header h1 { margin: 0; font-size: 28px; font-weight: 600; } .header p { margin: 10px 0 0 0; opacity: 0.9; font-size: 14px; } .section { padding: 25px 30px; border-bottom: 1px solid #e0e0e0; } .section:last-child { border-bottom: none; } .section-title { font-size: 20px; font-weight: 600; margin-bottom: 15px; color: #2c3e50; display: flex; align-items: center; } .section-title .icon { margin-right: 10px; font-size: 24px; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-top: 15px; } .stat-card { background-color: #f8f9fa; border-left: 4px solid #667eea; padding: 15px; border-radius: 5px; } .stat-card.urgent { border-left-color: #dc3545; background-color: #fff5f5; } .stat-card.success { border-left-color: #28a745; background-color: #f0fff4; } .stat-card.warning { border-left-color: #ffc107; background-color: #fffbf0; } .stat-label { font-size: 13px; color: #6c757d; font-weight: 500; text-transform: uppercase; letter-spacing: 0.5px; } .stat-value { font-size: 28px; font-weight: 700; margin: 5px 0; color: #2c3e50; } .stat-detail { font-size: 12px; color: #6c757d; } .progress-bar { background-color: #e9ecef; border-radius: 10px; height: 10px; margin-top: 8px; overflow: hidden; } .progress-fill { height: 100%; background: linear-gradient(90deg, #667eea 0%, #764ba2 100%); border-radius: 10px; transition: width 0.3s ease; } .progress-fill.danger { background: linear-gradient(90deg, #dc3545 0%, #c82333 100%); } .progress-fill.success { background: linear-gradient(90deg, #28a745 0%, #218838 100%); } .sentiment-container { display: flex; gap: 15px; margin-top: 15px; } .sentiment-item { flex: 1; text-align: center; padding: 15px; border-radius: 8px; background-color: #f8f9fa; } .sentiment-item.positive { background-color: #d4edda; border: 2px solid #28a745; } .sentiment-item.negative { background-color: #f8d7da; border: 2px solid #dc3545; } .sentiment-item.neutral { background-color: #fff3cd; border: 2px solid #ffc107; } .sentiment-emoji { font-size: 36px; margin-bottom: 5px; } .sentiment-count { font-size: 24px; font-weight: 700; color: #2c3e50; } .sentiment-label { font-size: 12px; color: #6c757d; font-weight: 600; text-transform: uppercase; } .trend-badge { display: inline-block; padding: 5px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; margin-top: 5px; } .trend-badge.stable { background-color: #cce5ff; color: #004085; } .trend-badge.active { background-color: #d4edda; color: #155724; } .footer { background-color: #f8f9fa; padding: 20px 30px; text-align: center; color: #6c757d; font-size: 12px; } .assignee-list { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; } .assignee-badge { background-color: #e9ecef; padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 500; } .source-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 10px; } .source-item { background-color: #f8f9fa; padding: 8px 12px; border-radius: 5px; font-size: 12px; border-left: 3px solid #667eea; } </style> </head> <body> <div class=\"container\"> <div class=\"header\"> <h1>📊 Daily KPI Dashboard Report</h1> <p>Generated on {{ $now.format('MMMM DD, YYYY') }} at {{ $now.format('HH:mm:ss') }}</p> </div> <div class=\"section\"> <div class=\"section-title\"> <span class=\"icon\">✅</span> <span>Tasks Overview</span> </div> <div class=\"stats-grid\"> <div class=\"stat-card\"> <div class=\"stat-label\">Total Tasks</div> <div class=\"stat-value\">{{ $json.tasks.total }}</div> </div> <div class=\"stat-card urgent\"> <div class=\"stat-label\">Overdue Tasks</div> <div class=\"stat-value\">{{ $json.tasks.overdue }}</div> <div class=\"stat-detail\">{{ $json.tasks.overduePercentage }}% of total</div> <div class=\"progress-bar\"> <div class=\"progress-fill danger\" style=\"width: {{ $json.tasks.overduePercentage }}%\"></div> </div> </div> <div class=\"stat-card warning\"> <div class=\"stat-label\">Urgent Priority</div> <div class=\"stat-value\">{{ $json.trends.urgentTasks }}</div> <div class=\"stat-detail\">Requires immediate attention</div> </div> <div class=\"stat-card\"> <div class=\"stat-label\">Avg Time Spent</div> <div class=\"stat-value\">{{ $json.tasks.avgTimeSpentHours }}</div> <div class=\"stat-detail\">hours per task</div> </div> </div> <div style=\"margin-top: 25px;\"> <strong style=\"font-size: 14px; color: #6c757d;\">TASK STATUS BREAKDOWN</strong> <div class=\"stats-grid\" style=\"margin-top: 10px;\"> <div class=\"stat-card\"> <div class=\"stat-label\">In Progress</div> <div class=\"stat-value\" style=\"font-size: 22px; color: #5f55ee;\">{{ $json.tasks.completion.inProgress }}</div> </div> <div class=\"stat-card\"> <div class=\"stat-label\">Final Review</div> <div class=\"stat-value\" style=\"font-size: 22px; color: #f8ae00;\">{{ $json.tasks.completion.finalReview }}</div> </div> <div class=\"stat-card\"> <div class=\"stat-label\">Backlogs</div> <div class=\"stat-value\" style=\"font-size: 22px; color: #87909e;\">{{ $json.tasks.completion.backlogs }}</div> </div> <div class=\"stat-card success\"> <div class=\"stat-label\">Completed</div> <div class=\"stat-value\" style=\"font-size: 22px; color: #28a745;\">{{ $json.tasks.completion.done }}</div> </div> </div> </div> <div style=\"margin-top: 25px;\"> <strong style=\"font-size: 14px; color: #6c757d;\">TASK DISTRIBUTION BY ASSIGNEE</strong> <div class=\"assignee-list\"> {{ Object.entries($json.tasks.byAssignee).map(([name, count]) => `<div class=\"assignee-badge\">${name}: <strong>${count}</strong> task(s)</div>`).join('') }} </div> </div> </div> <div class=\"section\"> <div class=\"section-title\"> <span class=\"icon\">👥</span> <span>Leads Overview</span> </div> <div class=\"stats-grid\"> <div class=\"stat-card\"> <div class=\"stat-label\">Total Leads</div> <div class=\"stat-value\">{{ $json.leads.total }}</div> </div> <div class=\"stat-card success\"> <div class=\"stat-label\">New Replies</div> <div class=\"stat-value\">{{ $json.trends.newReplies }}</div> <div class=\"stat-detail\">Awaiting response</div> </div> </div> <div style=\"margin-top: 25px;\"> <strong style=\"font-size: 14px; color: #6c757d;\">SENTIMENT ANALYSIS</strong> <div class=\"sentiment-container\"> <div class=\"sentiment-item positive\"> <div class=\"sentiment-emoji\">😊</div> <div class=\"sentiment-count\">{{ $json.leads.sentiment.positive }}</div> <div class=\"sentiment-label\">Positive</div> <div style=\"font-size: 14px; font-weight: 600; margin-top: 5px; color: #28a745;\">{{ $json.leads.sentiment.positiveRate }}%</div> </div> <div class=\"sentiment-item neutral\"> <div class=\"sentiment-emoji\">😐</div> <div class=\"sentiment-count\">{{ $json.leads.sentiment.neutral }}</div> <div class=\"sentiment-label\">Neutral</div> <div style=\"font-size: 14px; font-weight: 600; margin-top: 5px; color: #856404;\">{{ (($json.leads.sentiment.neutral / $json.leads.total) * 100).toFixed(2) }}%</div> </div> <div class=\"sentiment-item negative\"> <div class=\"sentiment-emoji\">😟</div> <div class=\"sentiment-count\">{{ $json.leads.sentiment.negative }}</div> <div class=\"sentiment-label\">Negative</div> <div style=\"font-size: 14px; font-weight: 600; margin-top: 5px; color: #dc3545;\">{{ $json.leads.sentiment.negativeRate }}%</div> </div> </div> </div> <div style=\"margin-top: 25px;\"> <strong style=\"font-size: 14px; color: #6c757d;\">TOP LEAD SOURCES</strong> <div class=\"source-list\"> {{ Object.entries($json.leads.bySource).map(([source, count]) => `<div class=\"source-item\"><strong>${source}</strong><br>${count} lead(s)</div>`).join('') }} </div> </div> <div style=\"margin-top: 25px;\"> <strong style=\"font-size: 14px; color: #6c757d;\">REPLIES BY DATE</strong> <div style=\"margin-top: 10px;\"> {{ Object.entries($json.leads.byDate).map(([date, count]) => ` <div style=\"display: flex; justify-content: space-between; padding: 8px 12px; background-color: #f8f9fa; margin-bottom: 5px; border-radius: 5px;\"> <span style=\"font-weight: 500;\">${date}</span> <span style=\"font-weight: 700; color: #667eea;\">${count} replies</span> </div> `).join('') }} </div> </div> </div> <div class=\"section\"> <div class=\"section-title\"> <span class=\"icon\">📈</span> <span>Trends & Insights</span> </div> <div style=\"display: grid; grid-template-columns: 1fr 1fr; gap: 20px;\"> <div> <div style=\"font-size: 14px; color: #6c757d; margin-bottom: 8px;\">TASK TREND</div> <div style=\"font-size: 20px; font-weight: 600; color: #2c3e50;\"> {{ $json.trends.taskTrend }} <span class=\"trend-badge stable\">{{ $json.trends.taskTrend }}</span> </div> </div> <div> <div style=\"font-size: 14px; color: #6c757d; margin-bottom: 8px;\">LEAD TREND</div> <div style=\"font-size: 20px; font-weight: 600; color: #2c3e50;\"> {{ $json.trends.leadTrend }} <span class=\"trend-badge active\">{{ $json.trends.leadTrend }}</span> </div> </div> </div> <div style=\"margin-top: 20px; padding: 15px; background-color: #e7f3ff; border-left: 4px solid #0066cc; border-radius: 5px;\"> <strong style=\"color: #004085;\">💡 Key Insights:</strong> <ul style=\"margin: 10px 0 0 0; padding-left: 20px; color: #004085;\"> <li>You have <strong>{{ $json.tasks.overdue }}</strong> overdue tasks that need immediate attention</li> <li><strong>{{ $json.leads.sentiment.positiveRate }}%</strong> of leads show positive interest</li> <li>Average task completion time: <strong>{{ $json.tasks.avgTimeSpentHours }} hours</strong></li> <li><strong>{{ $json.trends.newReplies }}</strong> new lead replies to follow up on</li> </ul> </div> </div> <div class=\"footer\"> <p style=\"margin: 0;\">This report was automatically generated by your KPI Dashboard System</p> <p style=\"margin: 5px 0 0 0;\">For questions or support, please contact your system administrator</p> </div> </div> </body> </html>",
"options": {},
"subject": "=Daily KPI Dashboard - {{ $now.format('MMMM DD, YYYY') }}"
},
"credentials": {
"gmailOAuth2": {
"id": "gEIaWCTvGfYjMSb3",
"name": "Gmail credentials"
}
},
"typeVersion": 2
}
],
"active": false,
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"versionId": "3d7eb7d9-714c-4f9a-b953-4b3cbe9a1cf4",
"connections": {
"Daily Cron Trigger": {
"main": [
[
{
"node": "Google Sheets - Fetch Lead Data",
"type": "main",
"index": 0
},
{
"node": "ClickUp - Fetch Tasks",
"type": "main",
"index": 0
}
]
]
},
"ClickUp - Fetch Tasks": {
"main": [
[
{
"node": "Merge - Consolidate Datasets",
"type": "main",
"index": 0
}
]
]
},
"Error Trigger Handler": {
"main": [
[
{
"node": "Slack - Send Error Alert",
"type": "main",
"index": 0
}
]
]
},
"Set - Format Output Data": {
"main": [
[
{
"node": "Slack - Post Dashboard Snapshot",
"type": "main",
"index": 0
},
{
"node": "Gmail - Send KPI Report",
"type": "main",
"index": 0
}
]
]
},
"Code - Compute KPI Trends": {
"main": [
[
{
"node": "Set - Format Output Data",
"type": "main",
"index": 0
}
]
]
},
"Merge - Consolidate Datasets": {
"main": [
[
{
"node": "Code - Compute KPI Trends",
"type": "main",
"index": 0
}
]
]
},
"Google Sheets - Fetch Lead Data": {
"main": [
[
{
"node": "Merge - Consolidate Datasets",
"type": "main",
"index": 1
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
从Monday.com和Jira到Outlook的AI驱动反馈分类与报告
使用Azure GPT-4、Jira任务和Outlook报告分析来自Monday.com的客户反馈
Set
Code
Jira
+12
27 节点Rahul Joshi
退信与无效检测 (Gmail 触发器)
使用 Gmail、Google Sheets 和 Slack 自动化邮件退信与无效检测
Code
Cron
Gmail
+6
26 节点Rahul Joshi
统一支持团队绩效仪表板(Zendesk & Freshdesk → Google Sheets + Slack + Gmail)
使用Sheets、Slack和Gmail跨Zendesk和Freshdesk跟踪支持绩效指标
If
Code
Cron
+7
24 节点Rahul Joshi
内容创作
API速率限制与认证FAQ测试
使用GPT-4o-mini、Google表格和Slack提醒自动化API常见问题质量测试
If
Set
Code
+7
19 节点Rahul Joshi
文档提取
个性化拒绝/下一步邮件
使用Google Sheets、GPT-4o-mini、Gmail和ClickUp自动化候选人拒绝
If
Set
Code
+7
38 节点Rahul Joshi
人力资源
从 GoHighLevel 自动发送客户续约提醒到 Gmail 和 Slack
从GoHighLevel自动发送客户续约提醒到Gmail、Slack和Google Sheets
If
Code
Gmail
+7
19 节点Rahul Joshi
客户关系管理
工作流信息
难度等级
高级
节点数量20
分类-
节点类型10
作者
Rahul Joshi
@rahul08Rahul Joshi is a seasoned technology leader specializing in the n8n automation tool and AI-driven workflow automation. With deep expertise in building open-source workflow automation and self-hosted automation platforms, he helps organizations eliminate manual processes through intelligent n8n ai agent automation solutions.
外部链接
在 n8n.io 查看 →
分享此工作流