NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Sunsama Daily Shutdownn Today's Progress Generator // @namespace https://www.patabugen.co.uk/ // @version 1.0 // @description Extract and display today's task progress in Sunsama. Roughly mimicks the pre-AI Daily Shutdown routine. // @author Sami Walbury // @match https://app.sunsama.com/group/*/shutdownDay/reflect // @grant none // @license MIT // ==/UserScript== (function () { 'use strict'; function waitForElement(selector, callback) { const el = document.querySelector(selector); if (el) { callback(el); } else { const observer = new MutationObserver(() => { const el = document.querySelector(selector); if (el) { observer.disconnect(); callback(el); } }); observer.observe(document.body, { childList: true, subtree: true }); } } function extractTasks() { const firstDayContainer = document.querySelector('.kanban-day-tasks-items-container'); if (!firstDayContainer) return; const taskContainers = firstDayContainer.querySelectorAll('.kanban-task-container.kanban-task-container-draggable'); const groupedTasks = {}; taskContainers.forEach(task => { const titleElement = task.querySelector('.task-item-description'); const groupElement = task.querySelector('.kanban-task-stream-label'); if (!titleElement || !groupElement) return; const taskTitle = titleElement.textContent.trim(); const groupTitle = groupElement.textContent.trim(); const subtaskElements = task.querySelectorAll('.subtask-item-description-container'); const subtaskTexts = Array.from(subtaskElements).map(s => s.textContent.trim()); if (!groupedTasks[groupTitle]) { groupedTasks[groupTitle] = []; } groupedTasks[groupTitle].push({ title: taskTitle, subtasks: subtaskTexts }); }); let output = '<h3>Today\'s Progress:</h3><ul>'; for (const [group, tasks] of Object.entries(groupedTasks)) { output += `<li><strong>${group}</strong><ul>`; tasks.forEach(task => { output += `<li><strong>${task.title}</strong><ul>`; task.subtasks.forEach(sub => { output += `<li>${sub}</li>`; }); output += '</ul></li>'; }); output += '</ul></li>'; } output += '</ul>'; displayRichText(output); } function getInjectedContainer() { const groupBodyContainer = document.querySelector('#group-body-container'); if (!groupBodyContainer) return; // Remove an existing container if it exists let injectecdContainer = groupBodyContainer.querySelector('#injected-task-summary'); if (!injectecdContainer) { injectecdContainer = document.createElement('div'); injectecdContainer.className = 'kanban-day-container' injectecdContainer.id = 'injected-task-summary'; injectecdContainer.style.overflow = 'auto'; injectecdContainer.style.maxWidth = '100%'; injectecdContainer.style.boxSizing = 'border-box'; injectecdContainer.style.order = 10; // Put it at the far right of any columns let title = document.createElement('div') title.className = "kanban-day-label-container" title.innerHTML = 'Status Update'; injectecdContainer.appendChild(title); let spacer = document.createElement('div'); let body = document.createElement('div'); body.className = 'today-progress-body'; injectecdContainer.appendChild(body); } groupBodyContainer.appendChild(injectecdContainer); return injectecdContainer.querySelector('.today-progress-body'); } function displayRichText(htmlContent) { const richTextEditor = document.createElement('div'); richTextEditor.contentEditable = 'true'; richTextEditor.style.width = '100%'; richTextEditor.style.minHeight = '200px'; richTextEditor.style.border = '1px solid #ccc'; richTextEditor.style.padding = '10px'; richTextEditor.style.backgroundColor = '#fff'; richTextEditor.style.boxSizing = 'border-box'; richTextEditor.innerHTML = htmlContent; const injectedContainer = getInjectedContainer(); injectedContainer.replaceChildren(richTextEditor); } function injectButton() { const button = document.createElement('div'); button.className = 'plan-day-btn'; button.textContent = "Generate Today's Progress"; button.style.cursor = 'pointer'; button.style.padding = '10px'; button.style.textAlign = 'center'; button.style.borderRadius = '4px'; button.style.maxHeight = '40px'; button.style.overflow = 'hidden'; button.style.whiteSpace = 'nowrap'; button.style.textOverflow = 'ellipsis'; button.addEventListener('click', extractTasks); const injectedContainer = getInjectedContainer(); injectedContainer.replaceChildren(button); } waitForElement('.kanban-day-container', injectButton); })();