📊 Personal Dashboards
🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
✅ Tạo morning briefing email tự động
✅ Xây dựng weekly dashboard với multiple data sources
✅ Implement Slack dashboard commands
✅ Thiết kế Notion dashboard page với real-time data
Start mỗi ngày với tất cả thông tin cần thiết ở một nơi. Bài này hướng dẫn tạo automated personal dashboards và morning briefings.
🔍 Dashboard Automation Overview
What to Include:
Checkpoint
Personal dashboard nên bao gồm những components nào?
⚡ Workflow 1: Morning Briefing
Daily Briefing Email:
Morning Briefing
Get Weather Data:
1{2 "name": "Get Weather",3 "type": "n8n-nodes-base.httpRequest",4 "parameters": {5 "url": "https://api.openweathermap.org/data/2.5/weather",6 "qs": {7 "q": "Ho Chi Minh City",8 "appid": "={{ $credentials.openWeatherApiKey }}",9 "units": "metric"10 }11 }12}Generate Briefing HTML:
1const weather = $('Get Weather').item.json;2const events = $('Get Calendar').all();3const tasks = $('Get Tasks').all();4const emailStats = $('Get Email Stats').item.json;5const news = $('Get News').all();67const today = new Date();8const dateStr = today.toLocaleDateString('en-US', { 9 weekday: 'long', 10 year: 'numeric', 11 month: 'long', 12 day: 'numeric' 13});1415const briefing = `16<!DOCTYPE html>17<html>18<head>19 <style>20 body { 21 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; 22 max-width: 600px; 23 margin: 0 auto; 24 padding: 20px;25 background: #f5f5f5;26 }27 .card { 28 background: white; 29 border-radius: 12px; 30 padding: 20px; 31 margin: 15px 0; 32 box-shadow: 0 2px 4px rgba(0,0,0,0.1);33 }34 h1 { color: #333; font-size: 24px; }35 h2 { color: #555; font-size: 18px; border-bottom: 2px solid #4CAF50; padding-bottom: 5px; }36 .weather { display: flex; align-items: center; gap: 15px; }37 .weather-temp { font-size: 48px; font-weight: bold; color: #4CAF50; }38 .task { padding: 8px 0; border-bottom: 1px solid #eee; }39 .task.priority { color: #e74c3c; font-weight: bold; }40 .event { padding: 8px 0; display: flex; gap: 10px; }41 .event-time { color: #888; min-width: 80px; }42 .stat { display: inline-block; margin: 10px 20px 10px 0; }43 .stat-value { font-size: 24px; font-weight: bold; color: #4CAF50; }44 .stat-label { color: #888; font-size: 12px; }45 .news-item { padding: 10px 0; border-bottom: 1px solid #eee; }46 </style>47</head>48<body>49 <h1>☀️ Good Morning!</h1>50 <p style="color: #888;">${dateStr}</p>5152 <!-- Weather -->53 <div class="card">54 <div class="weather">55 <div class="weather-temp">${Math.round(weather.main.temp)}°</div>56 <div>57 <div>${weather.weather[0].description}</div>58 <div style="color: #888;">Feels like ${Math.round(weather.main.feels_like)}°</div>59 <div style="color: #888;">Humidity: ${weather.main.humidity}%</div>60 </div>61 </div>62 </div>6364 <!-- Email Stats -->65 <div class="card">66 <h2>�� Inbox Status</h2>67 <div class="stat">68 <div class="stat-value">${emailStats.unread}</div>69 <div class="stat-label">Unread</div>70 </div>71 <div class="stat">72 <div class="stat-value">${emailStats.vip}</div>73 <div class="stat-label">VIP</div>74 </div>75 <div class="stat">76 <div class="stat-value">${emailStats.needsResponse}</div>77 <div class="stat-label">Need Response</div>78 </div>79 </div>8081 <!-- Calendar -->82 <div class="card">83 <h2>�� Today's Schedule</h2>84 ${events.length === 0 ? '<p style="color: #888;">No meetings today! 🎉</p>' : ''}85 ${events.slice(0, 5).map(e => `86 <div class="event">87 <div class="event-time">${new Date(e.json.start.dateTime).toLocaleTimeString('en-US', {hour: '2-digit', minute:'2-digit'})}</div>88 <div>${e.json.summary}</div>89 </div>90 `).join('')}91 ${events.length > 5 ? `<p style="color: #888;">+${events.length - 5} more</p>` : ''}92 </div>9394 <!-- Tasks -->95 <div class="card">96 <h2>✅ Priority Tasks</h2>97 ${tasks.slice(0, 5).map(t => `98 <div class="task ${t.json.priority === 'high' ? 'priority' : ''}">99 ☐ ${t.json.title}100 </div>101 `).join('')}102 ${tasks.length > 5 ? `<p style="color: #888;">+${tasks.length - 5} more tasks</p>` : ''}103 </div>104105 <!-- News -->106 <div class="card">107 <h2>�� Headlines</h2>108 ${news.slice(0, 3).map(n => `109 <div class="news-item">110 <a href="${n.json.url}" style="color: #1a73e8; text-decoration: none;">${n.json.title}</a>111 <div style="color: #888; font-size: 12px;">${n.json.source}</div>112 </div>113 `).join('')}114 </div>115116 <p style="text-align: center; color: #888; font-size: 12px; margin-top: 30px;">117 Have a productive day! ��<br>118 <em>Generated by n8n at ${new Date().toLocaleTimeString()}</em>119 </p>120</body>121</html>122`;123124return { briefing };Checkpoint
Morning briefing email thu thập data từ bao nhiêu nguồn?
🔧 Workflow 2: Weekly Dashboard
Weekly Stats Summary:
Weekly Dashboard
Calculate Weekly KPIs:
1const calendar = $('Get Calendar Stats').item.json;2const tasks = $('Get Tasks').item.json;3const timeTracking = $('Get Time Tracking').item.json;45// Calculate metrics6const kpis = {7 // Meeting efficiency8 totalMeetingHours: calendar.totalMinutes / 60,9 focusTimeHours: 40 - (calendar.totalMinutes / 60),10 meetingEfficiency: calendar.meetingsWithOutcome / calendar.totalMeetings * 100,11 12 // Task completion13 tasksCompleted: tasks.completed,14 tasksCreated: tasks.created,15 completionRate: tasks.completed / tasks.created * 100,16 17 // Time tracking18 productiveHours: timeTracking.productive,19 distractedHours: timeTracking.distracted,20 productivityScore: timeTracking.productive / (timeTracking.productive + timeTracking.distracted) * 100,21 22 // Trends23 meetingsTrend: calendar.totalMeetings > calendar.lastWeekMeetings ? 'up' : 'down',24 tasksTrend: tasks.completed > tasks.lastWeekCompleted ? 'up' : 'down'25};2627// Set targets and status28kpis.focusTimeTarget = 20; // hours29kpis.focusTimeStatus = kpis.focusTimeHours >= kpis.focusTimeTarget ? 'on-track' : 'behind';3031kpis.completionTarget = 80; // percent32kpis.completionStatus = kpis.completionRate >= kpis.completionTarget ? 'on-track' : 'behind';3334return kpis;Checkpoint
Weekly dashboard tính toán những KPIs nào?
🏗️ Workflow 3: Real-Time Slack Dashboard
Slash Command Dashboard:
/dashboard Command
Slack Dashboard Response:
1const email = $('Get Email').item.json;2const events = $('Get Events').all();3const tasks = $('Get Tasks').all();45const now = new Date();67const blocks = [8 {9 type: "header",10 text: {11 type: "plain_text",12 text: `📊 Quick Dashboard - ${now.toLocaleTimeString()}`13 }14 },15 {16 type: "section",17 fields: [18 {19 type: "mrkdwn",20 text: `*📧 Unread Emails*\n${email.unread}`21 },22 {23 type: "mrkdwn",24 text: `*📅 Meetings Today*\n${events.length}`25 },26 {27 type: "mrkdwn",28 text: `*⚡ Urgent Tasks*\n${tasks.filter(t => t.json.priority === 'high').length}`29 },30 {31 type: "mrkdwn",32 text: `*✅ Tasks Due Today*\n${tasks.filter(t => t.json.dueToday).length}`33 }34 ]35 },36 {37 type: "divider"38 },39 {40 type: "section",41 text: {42 type: "mrkdwn",43 text: events.length > 0 44 ? `*Next Meeting:* ${events[0].json.summary} at ${new Date(events[0].json.start.dateTime).toLocaleTimeString()}`45 : "*Next Meeting:* None scheduled 🎉"46 }47 }48];4950return { blocks };Checkpoint
Slack slash command dashboard trả về những thông tin gì?
📊 Workflow 4: Notion Dashboard Page
Auto-Update Notion Dashboard:
Update Notion Dashboard
Update Notion Database:
1{2 "name": "Update Dashboard",3 "type": "n8n-nodes-base.notion",4 "parameters": {5 "operation": "update",6 "pageId": "dashboard-page-id",7 "properties": {8 "Email Count": {9 "number": "={{ $json.emailCount }}"10 },11 "Tasks Due": {12 "number": "={{ $json.tasksDue }}"13 },14 "Meetings Today": {15 "number": "={{ $json.meetingsToday }}"16 },17 "Last Updated": {18 "date": { "start": "={{ $now.toISOString() }}" }19 }20 }21 }22}Checkpoint
Notion dashboard page update với frequency nào?
💡 Workflow 5: Financial Dashboard
Personal Finance Summary:
1// Aggregate financial data2const transactions = $('Get Transactions').all();3const investments = $('Get Portfolio').item.json;4const budget = $('Get Budget').item.json;56// Calculate spending by category7const spending = {};8for (const tx of transactions) {9 const cat = tx.json.category || 'Other';10 spending[cat] = (spending[cat] || 0) + tx.json.amount;11}1213// Budget status14const totalSpent = Object.values(spending).reduce((a, b) => a + b, 0);15const budgetRemaining = budget.monthly - totalSpent;16const budgetPercent = (totalSpent / budget.monthly * 100).toFixed(1);1718// Investment summary19const portfolioChange = investments.currentValue - investments.costBasis;20const portfolioChangePercent = (portfolioChange / investments.costBasis * 100).toFixed(2);2122return {23 spending: {24 byCategory: spending,25 total: totalSpent,26 budgetRemaining,27 budgetPercent,28 onTrack: totalSpent < budget.monthly29 },30 investments: {31 value: investments.currentValue,32 change: portfolioChange,33 changePercent: portfolioChangePercent,34 isPositive: portfolioChange > 035 },36 topCategories: Object.entries(spending)37 .sort((a, b) => b[1] - a[1])38 .slice(0, 5)39 .map(([cat, amount]) => ({ category: cat, amount }))40};Checkpoint
Financial dashboard tính toán spending by category bằng cách nào?
🌟 Workflow 6: Health Dashboard
Aggregate Health Data:
Health Dashboard
Health Score Calculator:
1const health = $('Get Health Data').item.json;23// Calculate component scores (0-100)4const scores = {5 sleep: Math.min(100, (health.sleepHours / 8) * 100),6 steps: Math.min(100, (health.steps / 10000) * 100),7 water: Math.min(100, (health.waterOz / 64) * 100),8 exercise: Math.min(100, (health.activeMinutes / 30) * 100),9 mood: health.moodScore * 20 // 1-5 scale to 0-10010};1112// Overall wellness score (weighted average)13const overallScore = Math.round(14 scores.sleep * 0.25 +15 scores.steps * 0.20 +16 scores.water * 0.15 +17 scores.exercise * 0.25 +18 scores.mood * 0.1519);2021// Status indicators22const getStatus = (score) => {23 if (score >= 80) return { emoji: '🟢', text: 'Excellent' };24 if (score >= 60) return { emoji: '🟡', text: 'Good' };25 if (score >= 40) return { emoji: '🟠', text: 'Fair' };26 return { emoji: '🔴', text: 'Needs Attention' };27};2829return {30 scores,31 overallScore,32 overallStatus: getStatus(overallScore),33 breakdown: Object.entries(scores).map(([key, score]) => ({34 metric: key,35 score,36 status: getStatus(score)37 }))38};Checkpoint
Health score calculator sử dụng weighted average với những weights nào?
📋 Dashboard Templates
Minimalist Morning Briefing:
1const data = $input.item.json;23const briefing = `4�� **Today at a Glance**56�� ${data.events.length} meetings | ✅ ${data.tasks} tasks | �� ${data.emails} emails78**Focus:** ${data.topPriority || 'Check your tasks'}910**Weather:** ${data.weather.temp}° ${data.weather.condition}1112*Have a great day!*13`;1415return { briefing };Executive Summary Style:
1const data = $input.item.json;23const summary = `4# Executive Dashboard56## Key Metrics7| Metric | Value | Status |8|--------|-------|--------|9| Revenue MTD | $${data.revenue.toLocaleString()} | ${data.revenueStatus} |10| Tasks Completed | ${data.tasksCompleted}/${data.tasksTotal} | ${data.taskStatus} |11| Team Velocity | ${data.velocity} pts | ${data.velocityStatus} |1213## Action Required14${data.actionItems.map(a => `- ${a}`).join('\n')}1516## Upcoming Deadlines17${data.deadlines.map(d => `- ${d.date}: ${d.item}`).join('\n')}1819*Updated: ${new Date().toLocaleString()}*20`;2122return { summary };Checkpoint
Minimalist morning briefing bao gồm những thông tin gì?
📝 Bài Tập Thực Hành
Build your command center:
- Create morning briefing email
- Build Slack quick dashboard command
- Set up weekly summary report
- Design Notion dashboard page
- Implement one specialized dashboard (finance/health/work)
Own your day! 📊
Checkpoint
Bạn đã hoàn thành những challenges nào trong bài tập?
🧠 Key Takeaways
- 📊 Aggregate don't duplicate - Pull from source of truth
- ⏰ Timing matters - Send at useful times
- 📱 Multi-channel - Email, Slack, Notion options
- 🎯 Keep it focused - Show what matters
- 🔄 Update frequency - Match your needs
Checkpoint
Tại sao nên Aggregate don't duplicate?
🚀 Bài tiếp theo
Team Notifications — Automate updates, alerts, và reminders cho team.
