This report utilizes the Jira “get issue limit“ API, and is not yet added to the Report Builder gallery permanently. However, you can import it using the JSON code provided on this page.

Report description

This report provides the following data and capabilities at a glance:

image-20240716-143255.png

Steps to add this to your list of Report Builder reports

Step 1

Download Report Builder from the Atlassian Marketplace

Step 2

Copy the JSON code below:

{
  "category": "scriptedReport",
  "tags": [],
  "script": "SR.render({}, async () => {\r\n    // Define the container where we will inject the cards\r\n    const cardContainer = document.getElementById('cardContainer');\r\n\r\n    try {\r\n        // Define input field names\r\n        const cardsPerRowField = 'cards-per-row'; // Replace with actual field name\r\n\r\n        // Fetch input values for ranges and layout\r\n        const cardsPerRow = SR.getValueByFieldName(cardsPerRowField) || 3; // Default to 3 if not set\r\n\r\n        // Calculate the width of each card based on the number of cards per row\r\n        const cardWidthPercentage = 100 / cardsPerRow;\r\n\r\n        // Set the CSS for the card container dynamically\r\n        const styleElement = document.createElement('style');\r\n        styleElement.textContent = `\r\n            .card {\r\n                width: calc(${cardWidthPercentage}% - 16px);\r\n            }\r\n        `;\r\n        document.head.appendChild(styleElement);\r\n\r\n        // Fetch the base URL\r\n        const baseUrl = await SR.jira.getHostBaseUrl();\r\n\r\n        // Fetch the capacity limits data from the API\r\n        const response = await AP.request('/rest/api/3/issue/limit/report');\r\n        const data = JSON.parse(response.body);\r\n\r\n        // Extract the limits, issues approaching limit, and issues breaching limit\r\n        const limits = data.limits || {};\r\n        const issuesApproachingLimit = data.issuesApproachingLimit || {};\r\n        const issuesBreachingLimit = data.issuesBreachingLimit || {};\r\n\r\n        // Clear any existing data in the card container\r\n        cardContainer.innerHTML = '';\r\n\r\n        // Function to create JQL URL for issue IDs\r\n        function createJqlUrl(issueIds) {\r\n            const jqlQuery = `id in (${issueIds.join(',')})`;\r\n            return `${baseUrl}/issues/?jql=${encodeURIComponent(jqlQuery)}`;\r\n        }\r\n\r\n        // Function to process issues data\r\n        function processIssuesData(issuesData) {\r\n            const issueIds = [];\r\n            for (const issueType in issuesData) {\r\n                if (issuesData.hasOwnProperty(issueType)) {\r\n                    const issues = issuesData[issueType];\r\n                    if (issues) {\r\n                        issueIds.push(...Object.keys(issues));\r\n                    }\r\n                }\r\n            }\r\n            return issueIds;\r\n        }\r\n\r\n        // Function to determine card color based on issue status\r\n        function getCardColor(hasApproachingIssues, hasBreachingIssues) {\r\n            if (hasBreachingIssues) {\r\n                // return 'linear-gradient(135deg, #e74c3c 0%, #c0392b 100%)'; // Red gradient\r\n                return '#c90000'; // Red \r\n            } else if (hasApproachingIssues) {\r\n                // return 'linear-gradient(135deg, #f39c12 0%, #e67e22 100%)'; // Yellow gradient\r\n                return '#ffba00'; // Yellow \r\n            } else {\r\n                // return 'linear-gradient(135deg, #1abc9c 0%, #16a085 100%)'; // Green gradient\r\n                return '#05bf00'; // Green \r\n            }\r\n        }\r\n\r\n        // Loop through each item in the limits node\r\n        Object.keys(limits).forEach(limitType => {\r\n            const limitValue = limits[limitType];\r\n\r\n            // Get the list of issue IDs for issues approaching the limit\r\n            const approachingIssueIds = processIssuesData(issuesApproachingLimit[limitType] || {});\r\n            const hasApproachingIssues = approachingIssueIds.length > 0;\r\n\r\n            // Get the list of issue IDs for issues breaching the limit\r\n            const breachingIssueIds = processIssuesData(issuesBreachingLimit[limitType] || {});\r\n            const hasBreachingIssues = breachingIssueIds.length > 0;\r\n\r\n            // Create card element for each item in the limits node\r\n            const card = document.createElement('div');\r\n            card.className = 'card';\r\n            /* card.style.background = getCardColor(hasApproachingIssues, hasBreachingIssues); */\r\n            card.style.border = '1px solid' + getCardColor(hasApproachingIssues, hasBreachingIssues);\r\n            card.style.color = '#253858';\r\n\r\n            const titleElement = document.createElement('p');\r\n            titleElement.classList.add('title');\r\n            // titleElement.textContent = `For: ${limitType}`;\r\n            \r\n            if ( limitType == \"worklog\" ) { titleElement.textContent = \"Worklogs\"; }\r\n            if ( limitType == \"attachment\" ) { titleElement.textContent = \"Attachments\"; }\r\n            if ( limitType == \"remoteIssueLinks\" ) { titleElement.textContent = \"Remote Issue Links\"; }\r\n            if ( limitType == \"issuelinks\" ) { titleElement.textContent = \"Issue Links\"; }\r\n            if ( limitType == \"comment\" ) { titleElement.textContent = \"Comments\"; }\r\n\r\n            const limitElement = document.createElement('h3');\r\n            // limitElement.textContent = `Limit: ${limitValue}`;\r\n            limitElement.textContent = `${limitValue}`;\r\n\r\n            const approachingCountElement = document.createElement('p');\r\n            approachingCountElement.innerHTML = `Issues approaching limit: <a href=\"${createJqlUrl(approachingIssueIds)}\" target=\"_blank\" style=\"color: ` + getCardColor(hasApproachingIssues, hasBreachingIssues) + `\">${approachingIssueIds.length}</a>`;\r\n            approachingCountElement.style.cursor = 'pointer';\r\n\r\n            const breachingCountElement = document.createElement('p');\r\n            breachingCountElement.innerHTML = `Issues breaching limit: <a href=\"${createJqlUrl(breachingIssueIds)}\" target=\"_blank\" style=\"color: ` + getCardColor(hasApproachingIssues, hasBreachingIssues) + `\">${breachingIssueIds.length}</a>`;\r\n            breachingCountElement.style.cursor = 'pointer';\r\n\r\n            // Append elements to the card\r\n            card.appendChild(titleElement);\r\n            card.appendChild(limitElement);\r\n            card.appendChild(document.createElement(\"div\")).classList.add('divider'); \r\n            card.appendChild(approachingCountElement);\r\n            card.appendChild(breachingCountElement);\r\n\r\n            // Append the card to the card container\r\n            cardContainer.appendChild(card);\r\n        });\r\n    } catch (error) {\r\n        console.error('Error fetching limit data:', error);\r\n        // Optionally display an error message in the UI\r\n        cardContainer.innerHTML = '<div>Error fetching limit data. Please try again later.</div>';\r\n    }\r\n});\r\n",
  "template": "<div class=\"report-container\">\r\n  <h2>Issue Content Limits</h2>\r\n  <p>Shown are limits per issue. Click on the numbers to get to the issue navigator.</p>\r\n  <div id=\"cardContainer\" class=\"card-container\">\r\n    <!-- Cards will be injected here by the script -->\r\n  </div>\r\n</div>\r\n\r\n<style>\r\n\r\n    h2 {\r\n        text-align: center;\r\n        font-size: 40px;\r\n        margin-bottom: -10px;\r\n        font-weight: 300;\r\n    }\r\n    \r\n    p {\r\n        font-size: 20px;\r\n        margin-bottom: 20px;\r\n        text-align: center;\r\n        font-weight: 300;\r\n        color: #95a5a6;\r\n    }\r\n\r\n    .card-container {\r\n        display: flex;\r\n        flex-wrap: wrap;\r\n        gap: 16px;\r\n        justify-content: flex-start;\r\n    }\r\n    \r\n    .card {\r\n        border-radius: 12px;\r\n        padding: 25px;\r\n        box-sizing: border-box;\r\n        text-align: center;\r\n        margin-bottom: 16px;\r\n        color: #fff;\r\n        /*box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);*/\r\n        transition: transform 0.2s ease-in-out;\r\n    }\r\n    .card:hover {\r\n        /*transform: scale(1.05);*/\r\n        transition: 0.2s ease-in-out;\r\n        background-color: #f9f9f9 !important;\r\n    }\r\n    .card h3 {\r\n        margin: 0;\r\n        font-size: 70px;\r\n        font-weight: 100;\r\n        color: inherit; /* Inherit color from card */\r\n    }\r\n    .card p {\r\n        margin: 10px 0 0;\r\n        font-size: 16px;\r\n        font-weight: normal;\r\n        color: inherit; /* Inherit color from card */\r\n    }\r\n    \r\n    .card p.title {\r\n        width: max-content;\r\n        padding: 5px 15px;\r\n        margin: 0 auto 10px auto;\r\n        color: white;\r\n        background-color: #403c90;\r\n        border-radius: 200px;\r\n    }\r\n    \r\n    .card a {\r\n        text-decoration: none;\r\n        color: inherit; /* Inherit color from card */\r\n    }\r\n    .card a:hover {\r\n        text-decoration: underline;\r\n    }\r\n    \r\n    .card .divider {\r\n        width: 90%;\r\n        height: 1px;\r\n        background-color: #cfc9d2;\r\n        margin: 10px auto 15px auto;\r\n    }\r\n    \r\n</style>\r\n",
  "name": "Issues Content Limits",
  "fields": [
    {
      "type": "2",
      "name": "cards-per-row",
      "title": "cards per row",
      "optionsList": [],
      "isMulti": false,
      "order": 5,
      "defaultValue": "5",
      "_id": "6684791b4aea9797226812d3"
    }
  ],
  "permissions": {
    "view": [
      {
        "type": "private",
        "value": "private"
      }
    ],
    "edit": [
      {
        "type": "private",
        "value": "private"
      }
    ]
  },
  "filterBy": []
}

Step 3

Click “Import Scripted Report from JSON“ in Report Builder, and paste the code as shown in the following video:

Create report.mp4

After you have created the report you may share it with your colleagues, or add to a dashboard.

If you have any trouble, please raise a ticket in our support portal.