{
  "nodes": [
    {
      "id": "3e556607-b051-41c2-b055-9596380c2093",
      "name": "Format Email1",
      "type": "n8n-nodes-base.set",
      "position": [
        15152,
        5824
      ],
      "parameters": {
        "fields": {
          "values": [
            {
              "name": "=emailSubject",
              "stringValue": "=\ud83d\udcca Weekly Sales Report: {{ $node[\"Sales Analytics Engine\"].json.weekRange }}"
            },
            {
              "name": "emailBody",
              "stringValue": "=<div style=\"font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; color: #333; max-width: 650px; margin: auto; border: 1px solid #e0e0e0; border-radius: 12px; overflow: hidden; background-color: #ffffff;\">\n  \n  <div style=\"background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%); color: white; padding: 30px; text-align: center;\">\n    <h1 style=\"margin: 0; font-size: 24px; letter-spacing: 1px;\">Weekly Performance Report</h1>\n    <p style=\"margin: 5px 0; opacity: 0.8; font-size: 14px;\">{{ $('Sales Analytics Engine').item.json.weekRange }}</p>\n  </div>\n\n  <div style=\"padding: 25px;\">\n    \n    <div style=\"background-color: #f0f7ff; border-left: 5px solid #3498db; padding: 20px; border-radius: 4px; margin-bottom: 30px;\">\n      <h3 style=\"margin-top: 0; color: #2c3e50; font-size: 16px;\">\ud83e\udd16 AI Analyst Insights</h3>\n      <p style=\"margin: 0; line-height: 1.6; color: #444; font-style: italic;\">\"{{ $json.content.parts[0].text }}\"</p>\n    </div>\n\n    <table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"margin-bottom: 35px;\">\n      <tr>\n        <td width=\"48%\" style=\"background: #f8f9fa; padding: 15px; border-radius: 10px; text-align: center; border: 1px solid #eee;\">\n          <span style=\"font-size: 12px; color: #7f8c8d; text-transform: uppercase;\">This Week</span>\n          <div style=\"font-size: 20px; font-weight: bold; color: #2c3e50;\">\u20b9{{ $('Sales Analytics Engine').item.json.currentTotalFormatted }}</div>\n        </td>\n        <td width=\"4%\"></td> \n        <td width=\"48%\" style=\"background: #f8f9fa; padding: 15px; border-radius: 10px; text-align: center; border: 1px solid #eee;\">\n  <span style=\"font-size: 11px; color: #7f8c8d; text-transform: uppercase; font-weight: bold;\">Growth</span>\n  <div style=\"font-size: 20px; font-weight: bold; color: {{ $('Sales Analytics Engine').item.json.growthColor }}; margin-top: 5px;\">\n    {{ $('Sales Analytics Engine').item.json.growthStatus }} {{ $('Sales Analytics Engine').item.json.growth }}\n  </div>\n</td>\n      </tr>\n    </table>\n\n    <h3 style=\"color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; margin-bottom: 15px;\">\ud83d\udcca Performance: Week vs Week</h3>\n    \n    <img \n      src=\"https://quickchart.io/chart?c={{ encodeURIComponent(JSON.stringify({\n        type: 'bar',\n        data: {\n          labels: JSON.parse($('Sales Analytics Engine').item.json.catLabels),\n          datasets: [\n            {\n              label: 'Last Week',\n              backgroundColor: 'rgba(231,76,60,0.5)',\n              data: JSON.parse($('Sales Analytics Engine').item.json.catLabels).map(cat => JSON.parse($('Sales Analytics Engine').item.json.prevCatMap)[cat] || 0)\n            },\n            {\n              label: 'This Week',\n              backgroundColor: 'rgba(46,204,113,0.5)',\n              data: JSON.parse($('Sales Analytics Engine').item.json.catValues)\n            }\n          ]\n        }\n      })) }}\" \n      width=\"600\" \n      style=\"display: block; width: 100%; max-width: 100%; border-radius: 8px; border: 1px solid #eee;\" \n      alt=\"Sales Chart\"\n    >\n\n    <h3 style=\"color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 8px; margin-top: 40px; margin-bottom: 15px;\">\ud83d\udce6 Detailed Category Breakdown</h3>\n    <table style=\"width: 100%; border-collapse: collapse; font-size: 14px;\">\n      <thead>\n        <tr style=\"background-color: #f2f2f2;\">\n          <th style=\"padding: 12px; text-align: left; border-bottom: 2px solid #ddd;\">Category</th>\n          <th style=\"padding: 12px; text-align: right; border-bottom: 2px solid #ddd;\">Prev Week</th>\n          <th style=\"padding: 12px; text-align: right; border-bottom: 2px solid #ddd;\">This Week</th>\n          <th style=\"padding: 12px; text-align: right; border-bottom: 2px solid #ddd;\">Change</th>\n        </tr>\n      </thead>\n      <tbody>\n        {{ $('Sales Analytics Engine').item.json.categoryTableHtml }}\n      </tbody>\n    </table>\n\n  </div>\n\n  <div style=\"background-color: #f8f9fa; padding: 20px; text-align: center; border-top: 1px solid #eee;\">\n    <p style=\"margin: 0; font-size: 12px; color: #95a5a6;\">\n      Automated Business Intelligence Report </p>\n  </div>\n</div>"
            }
          ]
        },
        "options": {}
      },
      "typeVersion": 3
    },
    {
      "id": "49684349-9340-4d8b-8447-4bf72b3868c7",
      "name": "Send Email1",
      "type": "n8n-nodes-base.gmail",
      "position": [
        15376,
        5824
      ],
      "parameters": {
        "sendTo": "YOUR_GMAIL_RECIPIENT_EMAIL",
        "message": "={{ $json.emailBody }}",
        "options": {},
        "subject": "={{ $json.emailSubject }}"
      },
      "typeVersion": 2
    },
    {
      "id": "36a1e222-29ba-4299-89d6-bcf32209c695",
      "name": "Get row(s) in sheet1",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        13680,
        5824
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "list",
          "value": "gid=0",
          "cachedResultUrl": "",
          "cachedResultName": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "list",
          "value": "YOUR_GOOGLE_SHEETS_DOCUMENT_ID",
          "cachedResultUrl": "",
          "cachedResultName": "Sales Weekly Data"
        }
      },
      "typeVersion": 4.7
    },
    {
      "id": "605bbc71-d07d-4750-b1a2-7c5162d9222f",
      "name": "Removing Empty Rows1",
      "type": "n8n-nodes-base.code",
      "position": [
        13904,
        5824
      ],
      "parameters": {
        "jsCode": "// 1. Get all items from the Google Sheets node\nconst items = $input.all();\n\n// 2. Define the cleaning logic\nconst cleanedData = items\n  .filter(item => {\n    // REMOVE EMPTY ROWS: Only keep rows that have a Date and a Product Type\n    const data = item.json;\n    return data[\"Date\"] && data[\"Product type\"] && data[\"Date\"].toString().trim() !== \"\";\n  })\n  .map(item => {\n    const data = item.json;\n\n    // CLEAN COMMAS & CURRENCY: Convert \"\u20b928,000\" -> 28000 (Number)\n    let rawSales = data[\"Total sales (\u20b9)\"] ? data[\"Total sales (\u20b9)\"].toString() : \"0\";\n    let cleanSales = Number(rawSales.replace(/[\u20b9,]/g, ''));\n\n    // CLEAN QUANTITY: Ensure \"8\" is a Number, not a string\n    let cleanQty = Number(data[\"Net quantity\"]) || 0;\n\n    // RETURN CLEANED OBJECT: This creates the \"Table\" format n8n likes\n    return {\n      json: {\n        ...data, // Keeps original columns (Date, Day, etc.)\n        \"Total sales (\u20b9)\": cleanSales, // Overwrites with pure number\n        \"Net quantity\": cleanQty,     // Overwrites with pure number\n        \"IsCleaned\": true              // Flag for your own tracking\n      }\n    };\n  });\n\nreturn cleanedData;"
      },
      "typeVersion": 2
    },
    {
      "id": "3738983e-3783-4e50-b667-4cbbb413d5fc",
      "name": "Filter Latest Week1",
      "type": "n8n-nodes-base.code",
      "position": [
        14128,
        5824
      ],
      "parameters": {
        "jsCode": "// Define the full range: 19-Mar to 1-Apr\nconst rangeStart = new Date(2026, 2, 19); \nconst rangeEnd = new Date(2026, 3, 1);\nrangeEnd.setHours(23, 59, 59);\n\nfunction parseSheetDate(dateStr) {\n  const months = { \"Jan\":0, \"Feb\":1, \"Mar\":2, \"Apr\":3, \"May\":4, \"Jun\":5, \"Jul\":6, \"Aug\":7, \"Sep\":8, \"Oct\":9, \"Nov\":10, \"Dec\":11 };\n  const parts = dateStr.split('-');\n  return new Date(parts[2], months[parts[1]], parts[0]);\n}\n\nreturn items.filter(item => {\n  const rowDate = parseSheetDate(item.json[\"Date\"]);\n  return rowDate >= rangeStart && rowDate <= rangeEnd;\n});"
      },
      "typeVersion": 2
    },
    {
      "id": "e13b8672-1cf2-4cc6-82b9-474cc5bafa91",
      "name": "HTTP Request1",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        14928,
        5824
      ],
      "parameters": {
        "url": "https://quickchart.io/chart",
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "c",
              "value": "={\n  \"type\": \"bar\",\n  \"data\": {\n    \"labels\": {{ $('Sales Analytics Engine').item.json.catLabels }},\n    \"datasets\": [\n      {\n        \"label\": \"Last Week (\u20b9)\",\n        \"backgroundColor\": \"rgba(231, 76, 60, 0.5)\",\n        \"data\": {{ JSON.stringify(JSON.parse($('Sales Analytics Engine').item.json.catLabels).map(cat => JSON.parse($('Sales Analytics Engine').item.json.prevCatMap)[cat] || 0)) }}\n      },\n      {\n        \"label\": \"This Week (\u20b9)\",\n        \"backgroundColor\": \"rgba(46, 204, 113, 0.5)\",\n        \"data\": {{ $('Sales Analytics Engine').item.json.catValues }}\n      }\n    ]\n  },\n  \"options\": {\n    \"title\": { \"display\": true, \"text\": \"Category Performance: Week vs Week\" }\n  }\n}"
            }
          ]
        }
      },
      "typeVersion": 4.3
    },
    {
      "id": "84d19fd3-fb3c-4581-8a23-9f2f04f79df7",
      "name": "Schedule Trigger1",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        13456,
        5824
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 21
            }
          ]
        }
      },
      "typeVersion": 1.3
    },
    {
      "id": "5b3d568f-922b-4a31-88fd-79c1ffc77bf4",
      "name": "Message a model",
      "type": "@n8n/n8n-nodes-langchain.googleGemini",
      "position": [
        14576,
        5824
      ],
      "parameters": {
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "models/gemini-2.5-flash",
          "cachedResultName": "models/gemini-2.5-flash"
        },
        "options": {},
        "messages": {
          "values": [
            {
              "content": "=You are an expert Retail Analyst. Based on the following data, write a detailed 3-sentence summary.\n\nThe Data: {{ $json.aiContext }}\n\nInstructions: > 1. Start with the overall performance ({{ $json.growthStatus }} by {{ $json.growth }}).\n2. Specifically identify which category performed significantly better and which one performed lower than last week.\n3. Mention the total volume of {{ $json.totalQty }} units sold.\n4. End with a focused business recommendation for the coming week."
            }
          ]
        }
      },
      "typeVersion": 1
    },
    {
      "id": "bfe10aa6-803b-4541-acef-51354faf1847",
      "name": "Sales Analytics Engine",
      "type": "n8n-nodes-base.code",
      "position": [
        14352,
        5824
      ],
      "parameters": {
        "jsCode": "let currentTotal = 0;\nlet previousTotal = 0;\nlet totalQty = 0;\n\n// Track categories for both weeks\nconst currentCatMap = {}; \nconst prevCatMap = {}; \nconst dailyTrend = { \"Monday\":0, \"Tuesday\":0, \"Wednesday\":0, \"Thursday\":0, \"Friday\":0, \"Saturday\":0, \"Sunday\":0 };\n\n// 1. Define the split date\nconst splitDate = new Date(2026, 2, 26); \n\nfor (const item of items) {\n  const months = { \"Jan\":0, \"Feb\":1, \"Mar\":2, \"Apr\":3, \"May\":4, \"Jun\":5, \"Jul\":6, \"Aug\":7, \"Sep\":8, \"Oct\":9, \"Nov\":10, \"Dec\":11 };\n  const parts = item.json[\"Date\"].split('-');\n  const rowDate = new Date(parts[2], months[parts[1]], parts[0]);\n  const sales = Number(item.json[\"Total sales (\u20b9)\"]) || 0;\n  const qty = Number(item.json[\"Net quantity\"]) || 0;\n  const cat = item.json[\"Product type\"];\n\n  if (rowDate >= splitDate) {\n    // --- CURRENT WEEK ---\n    currentTotal += sales;\n    totalQty += qty;\n    currentCatMap[cat] = (currentCatMap[cat] || 0) + sales;\n    const dayName = item.json[\"Day\"];\n    if (dailyTrend.hasOwnProperty(dayName)) dailyTrend[dayName] += sales;\n  } else {\n    // --- PREVIOUS WEEK ---\n    previousTotal += sales;\n    prevCatMap[cat] = (prevCatMap[cat] || 0) + sales;\n  }\n}\n\n// 2. Performance Metrics & Color Logic\nconst isGrowthPositive = currentTotal >= previousTotal;\nconst growthValue = previousTotal === 0 ? 100 : ((currentTotal - previousTotal) / previousTotal * 100).toFixed(1);\n\nconst growthStatus = isGrowthPositive ? \"\ud83d\udcc8 Up\" : \"\ud83d\udcc9 Down\";\nconst growthColor = isGrowthPositive ? \"#27ae60\" : \"#e74c3c\"; // Green if up, Red if down\n\n// 3. Build Category Comparison Table\nlet categoryRows = \"\";\nconst allCategories = Array.from(new Set([...Object.keys(currentCatMap), ...Object.keys(prevCatMap)]));\nallCategories.sort((a, b) => (currentCatMap[b] || 0) - (currentCatMap[a] || 0));\n\nconst aiCategorySummary = [];\n\nfor (const cat of allCategories) {\n  const curVal = currentCatMap[cat] || 0;\n  const prevVal = prevCatMap[cat] || 0;\n  \n  let catChange = \"New\";\n  if (prevVal > 0) {\n    catChange = (((curVal - prevVal) / prevVal) * 100).toFixed(0) + \"%\";\n  }\n  \n  const statusIcon = curVal >= prevVal ? \"\u25b2\" : \"\u25bc\";\n  const statusColor = curVal >= prevVal ? \"#27ae60\" : \"#e74c3c\";\n\n  categoryRows += `\n    <tr style=\"border-bottom: 1px solid #eee; font-size: 13px;\">\n      <td style=\"padding: 12px 8px; color: #333;\"><strong>${cat}</strong></td>\n      <td style=\"padding: 12px 8px; text-align: right;\">\u20b9${prevVal.toLocaleString('en-IN')}</td>\n      <td style=\"padding: 12px 8px; text-align: right; font-weight: bold;\">\u20b9${curVal.toLocaleString('en-IN')}</td>\n      <td style=\"padding: 12px 8px; text-align: right; color: ${statusColor}; font-weight: bold;\">\n        ${statusIcon} ${catChange}\n      </td>\n    </tr>`;\n\n  aiCategorySummary.push(`${cat}: \u20b9${curVal} (vs \u20b9${prevVal} last week)`);\n}\n\n// 4. Final Output\nreturn [{\n  json: {\n    currentTotalFormatted: currentTotal.toLocaleString('en-IN'),\n    previousTotalFormatted: previousTotal.toLocaleString('en-IN'),\n    totalQty,\n    growth: (growthValue > 0 ? \"+\" : \"\") + growthValue + \"%\",\n    growthStatus,\n    growthColor, // <--- EXPLICIT COLOR PASSED TO HTML\n    isGrowthPositive,\n    prevCatMap: JSON.stringify(prevCatMap),\n    \n    aiContext: `This week: \u20b9${currentTotal}. Last week: \u20b9${previousTotal}. Growth: ${growthValue}%. Details per category: ${aiCategorySummary.join(', ')}.`,\n    \n    chartData: [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\", \"Sunday\"].map(d => dailyTrend[d]).join(','),\n    catLabels: JSON.stringify(allCategories),\n    catValues: JSON.stringify(allCategories.map(c => currentCatMap[c] || 0)),\n    \n    categoryTableHtml: categoryRows,\n    weekRange: \"26-Mar-2026 to 01-Apr-2026\"\n  }\n}];"
      },
      "typeVersion": 2
    },
    {
      "id": "08784599-f459-419a-ae7e-0597bf41ccb3",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13840,
        5488
      ],
      "parameters": {
        "color": 7,
        "width": 656,
        "height": 720,
        "content": "## Step 2: Clean & Analyze\nCleans data and calculates weekly performance metrics"
      },
      "typeVersion": 1
    },
    {
      "id": "0743db39-6cd8-456e-bb30-252460f861c0",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        14512,
        5488
      ],
      "parameters": {
        "color": 7,
        "width": 544,
        "height": 720,
        "content": "## Step 3: AI & Chart\nGenerates insights and builds visual performance chart"
      },
      "typeVersion": 1
    },
    {
      "id": "be3e3095-9a0b-4ccd-bc88-fb8d8ab40836",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        15072,
        5488
      ],
      "parameters": {
        "color": 7,
        "width": 512,
        "height": 720,
        "content": "## Step 4: Format & Send\nFormats report into HTML and sends via Gmail"
      },
      "typeVersion": 1
    },
    {
      "id": "04f852df-4bb4-48b9-a3cb-05bd539a09a1",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        12784,
        5488
      ],
      "parameters": {
        "width": 624,
        "height": 720,
        "content": "# Weekly Google Sheets Report \u2192 Email Summary\n\n### How it works\nThis workflow automatically generates a weekly sales performance report. It starts with a scheduled trigger, fetches raw sales data from Google Sheets, and cleans the dataset by removing empty rows and converting values into usable numeric formats. A custom analytics engine then compares current week vs previous week performance, calculates growth, and builds category-level insights. \n\nNext, an AI model generates a short business summary based on the computed metrics. A chart is created using QuickChart to visually compare category performance. Finally, everything is assembled into a clean HTML email including insights, KPIs, and tables, and sent via Gmail.\n\n### Setup steps\n1. Connect your Google Sheets account and select your sheet\n2. Add your Gemini API credentials for AI summaries\n3. Connect your Gmail account to send emails\n4. Update the recipient email address\n5. Adjust schedule timing if needed\n\n### Customization tips\n- Modify date logic inside the analytics node for custom ranges\n- Adjust email design styles inside the Set node\n- Change schedule trigger timing as needed"
      },
      "typeVersion": 1
    },
    {
      "id": "24a8bcd9-43bd-44ef-9353-305b89b87583",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        13424,
        5488
      ],
      "parameters": {
        "color": 7,
        "width": 400,
        "height": 720,
        "content": "## Step 1: Trigger & Fetch\nRuns weekly and pulls raw sales data from Google Sheets"
      },
      "typeVersion": 1
    }
  ],
  "connections": {
    "Format Email1": {
      "main": [
        [
          {
            "node": "Send Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "HTTP Request1": {
      "main": [
        [
          {
            "node": "Format Email1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Message a model": {
      "main": [
        [
          {
            "node": "HTTP Request1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger1": {
      "main": [
        [
          {
            "node": "Get row(s) in sheet1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Latest Week1": {
      "main": [
        [
          {
            "node": "Sales Analytics Engine",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get row(s) in sheet1": {
      "main": [
        [
          {
            "node": "Removing Empty Rows1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Removing Empty Rows1": {
      "main": [
        [
          {
            "node": "Filter Latest Week1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sales Analytics Engine": {
      "main": [
        [
          {
            "node": "Message a model",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}