{
  "name": "Jira Weekly Status Report",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "weeksInterval": 1,
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 7,
              "triggerAtMinute": 0
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        240,
        320
      ]
    },
    {
      "parameters": {
        "resource": "issue",
        "operation": "getAll",
        "returnAll": true,
        "options": {
          "jql": "project = DEMOEN AND updated >= -30d ORDER BY updated DESC"
        }
      },
      "id": "get-jira-issues",
      "name": "Get Jira Issues",
      "type": "n8n-nodes-base.jira",
      "typeVersion": 1,
      "position": [
        460,
        320
      ],
      "credentials": {
        "jiraSoftwareCloudApi": {
          "id": "",
          "name": "Jira Cloud account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// Prepare Summary Input (n8n Code node)\n// Reads this week's Jira issues, categorizes them and builds a text block\n// that INCLUDES each ticket's long description, ready for the AI summary.\n\nconst issues = $input.all().map((i) => i.json);\n\n// Jira Cloud returns the Description field as ADF (a nested object).\n// Walk it and pull out the plain text.\nfunction adfText(node) {\n  if (!node) return '';\n  if (typeof node === 'string') return node;\n  let out = node.text || '';\n  if (Array.isArray(node.content)) {\n    for (const c of node.content) out += adfText(c);\n  }\n  if (node.type === 'paragraph') out += '\\n';\n  return out;\n}\n\nconst counts = { new: 0, completed: 0, open_high: 0, blockers: 0, other: 0 };\nconst blocks = [];\n\nfor (const it of issues) {\n  const f = it.fields || {};\n  const status = (f.status?.name || '').toLowerCase();\n  const priority = (f.priority?.name || '').toLowerCase();\n  const labels = f.labels || [];\n  const key = it.key || '';\n  const summary = f.summary || '';\n  const description = adfText(f.description).replace(/\\s+/g, ' ').trim();\n\n  let bucket = 'other';\n  if (status.includes('done') || status.includes('closed') || status.includes('resolved')) bucket = 'completed';\n  else if (labels.includes('blocked')) bucket = 'blockers';\n  else if (priority.includes('high')) bucket = 'open_high';\n  else if (status.includes('to do') || status.includes('todo') || status.includes('backlog')) bucket = 'new';\n  counts[bucket] = (counts[bucket] || 0) + 1;\n\n  blocks.push(\n    `- [${key}] ${summary} (status: ${f.status?.name || 'n/a'}, priority: ${f.priority?.name || 'n/a'}, labels: ${labels.join(', ') || 'none'})\\n  ${description}`,\n  );\n}\n\nconst ticketText = `Counts: new=${counts.new}, completed=${counts.completed}, open_high=${counts.open_high}, blockers=${counts.blockers}, total=${issues.length}.\\n\\nTickets:\\n${blocks.join('\\n\\n')}`;\n\nreturn [{ json: { totalIssues: issues.length, counts, ticketText } }];\n"
      },
      "id": "prepare-summary",
      "name": "Prepare Summary Input",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        680,
        320
      ]
    },
    {
      "parameters": {
        "resource": "text",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "gpt-4o-mini",
          "cachedResultName": "gpt-4o-mini"
        },
        "messages": {
          "values": [
            {
              "role": "system",
              "content": "You are a project assistant. Based on the Jira ticket data and the long descriptions, write a concise weekly status update of 3 to 4 sentences for a team lead. Mention completed work, new items, high priority topics and any blockers. Plain, professional English."
            },
            {
              "content": "=Here is this week's Jira data:\n\n{{ $json.ticketText }}\n\nWrite the weekly status summary."
            }
          ]
        },
        "options": {}
      },
      "id": "ai-weekly-summary",
      "name": "AI Weekly Summary",
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 1.8,
      "position": [
        900,
        320
      ],
      "credentials": {
        "openAiApi": {
          "id": "",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "team@example.com",
        "subject": "=Weekly Jira Status",
        "emailType": "text",
        "message": "={{ $json.message?.content || $json.content || $json.text }}",
        "options": {}
      },
      "id": "send-email",
      "name": "Send Report (Email)",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1120,
        320
      ],
      "credentials": {
        "gmailOAuth2": {
          "id": "",
          "name": "Gmail account"
        }
      }
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Jira Issues",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Jira Issues": {
      "main": [
        [
          {
            "node": "Prepare Summary Input",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Summary Input": {
      "main": [
        [
          {
            "node": "AI Weekly Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Weekly Summary": {
      "main": [
        [
          {
            "node": "Send Report (Email)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {}
}