Define Your Fulfillment Profile
Select/Define Your Fulfillment Pillars
Choose what makes work fulfilling for you, or add your own. Optionally, rate their importance.
Your Fulfillment Pillars:
Log Work Activity & Fulfillment
Recent Logged Activities:
| Date | Activity | Overall Engagement | Top Pillar(s) Hit | Actions |
Fulfillment Analysis & Insights
Analysis for: This Week
Fulfillment Pillar Alignment (Avg. Score)
Key Insights & Observations
- Run analysis to see insights.
Most Fulfilling Activities This Period:
- No activities logged or analyzed yet.
Action Plan & Resources
My Fulfillment Action Plan
Based on your analysis, what steps can you take to increase work fulfillment?
General Resources for Work Fulfillment (External Links)
Please define your fulfillment pillars first in the "My Fulfillment Profile" tab.
'; return;}
wfa_pillars.forEach(pillar => {
const div = document.createElement('div');
div.className = 'wfa-pillar-rating-item';
div.innerHTML = `
`;
container.appendChild(div);
});
}
document.getElementById('wfa-activityLogForm').onsubmit = (e) => {
e.preventDefault();
const id = document.getElementById('wfa-activityLogId').value;
const pillarRatings = {};
document.querySelectorAll('#wfa-pillarRatingsContainer input[type="range"]').forEach(slider => {
pillarRatings[slider.dataset.pillarId] = parseInt(slider.value);
});
const logData = {
id: id || wfa_generateId(),
date: document.getElementById('wfa-activityDate').value,
description: document.getElementById('wfa-activityDescription').value.trim(),
pillarRatings: pillarRatings,
engagementRating: parseInt(document.getElementById('wfa-activityEngagement').value),
notes: document.getElementById('wfa-activityNotes').value.trim()
};
if(!logData.date || !logData.description) { alert("Date and Activity Description are required."); return;}
if (id) wfa_loggedActivities = wfa_loggedActivities.map(act => act.id === id ? logData : act);
else wfa_loggedActivities.push(logData);
wfa_loggedActivities.sort((a,b) => new Date(b.date) - new Date(a.date));
wfa_saveData(); wfa_renderRecentActivitiesTable(); wfa_resetActivityLogForm();
};
function wfa_resetActivityLogForm() {
document.getElementById('wfa-activityLogForm').reset();
document.getElementById('wfa-activityLogId').value = '';
document.getElementById('wfa-activityDate').value = WFA_TODAY_CONTEXT.toISOString().split('T')[0];
document.querySelectorAll('#wfa-pillarRatingsContainer input[type="range"]').forEach(slider => {
slider.value = 0;
document.getElementById(`val_pillar_${slider.dataset.pillarId}`).textContent = '0';
});
document.getElementById('wfa-activityEngagement').value = 3;
document.getElementById('val-activityEngagement').textContent = '3';
document.getElementById('wfa-cancelActivityLogEditBtn').style.display = 'none';
}
function wfa_renderRecentActivitiesTable() { /* Show recent N entries */
const tbody = document.getElementById('wfa-recentActivitiesTableBody'); tbody.innerHTML = '';
const recentEntries = wfa_loggedActivities.slice(0, 5); // Show last 5
if(recentEntries.length === 0) { tbody.innerHTML = '
| No activities logged yet. |
'; return;}
recentEntries.forEach(entry => {
let topPillarText = 'N/A';
if(entry.pillarRatings){
const ratedPillars = Object.entries(entry.pillarRatings)
.filter(([,rating]) => rating > 1) // Consider rating > 1 as significant
.sort(([,a],[,b])=>b-a); // Sort by rating desc
if(ratedPillars.length > 0) {
const topPillarId = ratedPillars[0][0];
const pillar = wfa_pillars.find(p=>p.id === topPillarId);
topPillarText = pillar ? pillar.name : 'Unknown';
if(ratedPillars.length > 1) topPillarText += ` (+${ratedPillars.length-1})`;
}
}
const row = tbody.insertRow();
row.innerHTML = `
${new Date(entry.date+"T00:00:00Z").toLocaleDateString()} | ${entry.description.substring(0,30)}... |
${entry.engagementRating}⭐ | ${topPillarText} |
| `;
});
}
function wfa_editActivityLog(id) { /* Load entry to form */
const entry = wfa_loggedActivities.find(e=>e.id===id); if(!entry) return;
wfa_openTab(null, 'wfa-logActivityTab'); // Switch to log tab if not already there
document.getElementById('wfa-activityLogId').value = entry.id;
document.getElementById('wfa-activityDate').value = entry.date;
document.getElementById('wfa-activityDescription').value = entry.description;
document.querySelectorAll('#wfa-pillarRatingsContainer input[type="range"]').forEach(slider => {
const pillarId = slider.dataset.pillarId;
const rating = (entry.pillarRatings && entry.pillarRatings[pillarId] !== undefined) ? entry.pillarRatings[pillarId] : 0;
slider.value = rating;
document.getElementById(`val_pillar_${pillarId}`).textContent = rating;
});
document.getElementById('wfa-activityEngagement').value = entry.engagementRating || 3;
document.getElementById('val-activityEngagement').textContent = entry.engagementRating || 3;
document.getElementById('wfa-activityNotes').value = entry.notes || '';
document.getElementById('wfa-cancelActivityLogEditBtn').style.display = 'inline-block';
}
function wfa_deleteActivityLog(id) { if(confirm('Delete this logged activity?')){wfa_loggedActivities = wfa_loggedActivities.filter(e=>e.id!==id);wfa_saveData();wfa_renderRecentActivitiesTable();}}
// --- Fulfillment Analysis Tab ---
function wfa_initAnalysisTab() { /* ... set default period, generate ... */
const todayForPicker = WFA_TODAY_CONTEXT;
document.getElementById('wfa-reportStartDate').value = new Date(todayForPicker.getFullYear(), todayForPicker.getMonth(), 1).toISOString().split('T')[0]; // Default to start of current month
document.getElementById('wfa-reportEndDate').value = todayForPicker.toISOString().split('T')[0];
wfa_generateFulfillmentAnalysis();
}
function wfa_toggleCustomDateRange() { document.getElementById('wfa-customDateRangePicker').style.display = document.getElementById('wfa-reportPeriod').value === 'custom' ? 'flex' : 'none';}
function wfa_getReportDateRange() { /* Similar to other tools, use WFA_TODAY_CONTEXT */
const period = document.getElementById('wfa-reportPeriod').value;
const today = new Date(WFA_TODAY_CONTEXT); today.setHours(0,0,0,0);
let startDate = new Date(today), endDate = new Date(today); endDate.setHours(23,59,59,999);
switch(period) { /* cases for today, thisWeek, thisMonth, lastWeek, lastMonth, lastQuarter, custom, allTime */
case 'thisWeek': startDate = new Date(today); const day = today.getDay(); startDate.setDate(today.getDate() - day + (day === 0 ? -6 : 1)); endDate = new Date(startDate); endDate.setDate(startDate.getDate() + 6); endDate.setHours(23,59,59,999); break;
case 'thisMonth': startDate = new Date(today.getFullYear(), today.getMonth(), 1); endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0); endDate.setHours(23,59,59,999); break;
// ... Add other cases from previous tools as needed (lastWeek, lastMonth, lastQuarter)
case 'lastQuarter': startDate = new Date(today); startDate.setMonth(today.getMonth() - 3); startDate.setDate(1); endDate = new Date(today.getFullYear(), startDate.getMonth() + 3, 0); endDate.setHours(23,59,59,999); break;
case 'custom':
const startVal = document.getElementById('wfa-reportStartDate').value; const endVal = document.getElementById('wfa-reportEndDate').value;
if (!startVal || !endVal) { alert("Please select both start and end dates."); return null; }
startDate = new Date(startVal + "T00:00:00"); endDate = new Date(endVal + "T23:59:59");
if (isNaN(startDate.getTime()) || isNaN(endDate.getTime()) || startDate > endDate) { alert("Invalid custom date range."); return null; }
break;
case 'allTime': startDate = new Date(1970,0,1); endDate = new Date(2999,11,31); break;
default: /* today or other default */ startDate = new Date(today);
}
return { start: startDate.toISOString().split('T')[0], end: endDate.toISOString().split('T')[0] };
}
function wfa_generateFulfillmentAnalysis() {
const range = wfa_getReportDateRange(); if (!range) return;
document.getElementById('wfa-analysisResultsArea').style.display = 'block';
try {
document.getElementById('wfa-analysisPeriodDisplay').textContent = `Analysis for: ${new Date(range.start+"T00:00:00Z").toLocaleDateString()} - ${new Date(range.end+"T00:00:00Z").toLocaleDateString()}`;
} catch(e) { document.getElementById('wfa-analysisPeriodDisplay').textContent = `Analysis for: ${range.start} - ${range.end}`; }
const entriesInPeriod = wfa_loggedActivities.filter(act => act.date >= range.start && act.date <= range.end);
const pillarScores = {}; // { pillarId: { totalScore: X, count: Y, name: Z, color: C, importance: I } }
wfa_pillars.forEach(p => pillarScores[p.id] = { totalScore:0, count:0, name: p.name, color: p.color, importance: p.importance || 3 });
if (entriesInPeriod.length === 0) {
document.getElementById('wfa-fulfillmentInsightsList').innerHTML = '
No activities logged in this period to analyze.';
document.getElementById('wfa-topFulfillingActivities').innerHTML = '
N/A';
if(wfa_pillarAlignmentChartInstance) wfa_pillarAlignmentChartInstance.destroy();
const ctx = document.getElementById('wfa-pillarAlignmentChart').getContext('2d');
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height); ctx.textAlign="center";ctx.fillText("No data for chart.",ctx.canvas.width/2,ctx.canvas.height/2);
document.getElementById('wfa-downloadPdfBtn').style.display = 'none';
return;
}
entriesInPeriod.forEach(entry => {
for (const pillarId in entry.pillarRatings) {
if (pillarScores[pillarId] && entry.pillarRatings[pillarId] > 0) {
pillarScores[pillarId].totalScore += entry.pillarRatings[pillarId];
pillarScores[pillarId].count++;
}
}
});
const chartLabels = [], chartData = [], chartColors = [], chartImportanceWeights = [];
Object.values(pillarScores).forEach(ps => {
if(ps.count > 0 || wfa_pillars.find(p=>p.id === Object.keys(pillarScores).find(k=>pillarScores[k]===ps))) { // Ensure all defined pillars are shown, even if 0
chartLabels.push(ps.name);
chartData.push(ps.count > 0 ? parseFloat((ps.totalScore / ps.count).toFixed(1)) : 0); // Avg score
chartColors.push(ps.color);
chartImportanceWeights.push(ps.importance); // Could use this to weight the bar heights visually or in score.
}
});
if (wfa_pillarAlignmentChartInstance) wfa_pillarAlignmentChartInstance.destroy();
const ctxPillar = document.getElementById('wfa-pillarAlignmentChart').getContext('2d');
wfa_pillarAlignmentChartInstance = new Chart(ctxPillar, {
type: 'bar', // Could be radar
data: { labels: chartLabels, datasets: [{ label: 'Avg. Fulfillment Score (0-4)', data: chartData, backgroundColor: chartColors }] },
options: { responsive: true, maintainAspectRatio: false, indexAxis:'y', scales: { x: { beginAtZero: true, max: 4, title:{display:true, text:'Average Rating'} } } }
});
// Insights & Fulfilling Activities
const insightsList = document.getElementById('wfa-fulfillmentInsightsList'); insightsList.innerHTML = '';
const topFulfillingList = document.getElementById('wfa-topFulfillingActivities'); topFulfillingList.innerHTML = '';
const sortedPillars = Object.values(pillarScores).filter(p=>p.count>0).sort((a,b) => (b.totalScore/b.count || 0) - (a.totalScore/a.count || 0));
if(sortedPillars.length > 0) {
insightsList.innerHTML += `
Your most fulfilled pillar appears to be: ${sortedPillars[0].name}.`;
const leastFulfilled = Object.values(pillarScores).filter(p=> wfa_pillars.some(wp => wp.id === Object.keys(pillarScores).find(k => pillarScores[k] === p))).sort((a,b) => (a.totalScore/a.count || 0) - (b.totalScore/b.count || 0)); // find configured pillars that have low scores
if(leastFulfilled.length > 0 && (leastFulfilled[0].totalScore / leastFulfilled[0].count || 0) < 1.5) { // Arbitrary threshold for "low"
insightsList.innerHTML += `
Consider focusing on activities that boost your ${leastFulfilled[0].name} pillar.`;
}
} else { insightsList.innerHTML = '
Not enough data to determine pillar trends.'; }
const topActivities = entriesInPeriod.map(entry => {
let activityScore = 0;
for(const pId in entry.pillarRatings) {
activityScore += entry.pillarRatings[pId] * (pillarScores[pId]?.importance || 3); // Weight by pillar importance
}
activityScore += (entry.engagementRating || 0) * 5; // Add engagement score (weighted)
return { ...entry, fulfillmentScore: activityScore };
}).sort((a,b) => b.fulfillmentScore - a.fulfillmentScore).slice(0,3);
if(topActivities.length > 0) topActivities.forEach(act => topFulfillingList.innerHTML += `
${act.description} (Overall Score: ${act.fulfillmentScore.toFixed(0)})`);
else topFulfillingList.innerHTML = '
No specific activities stood out this period.';
document.getElementById('wfa-downloadPdfBtn').style.display = 'inline-block';
}
// --- Action Plan & Resources Tab ---
function wfa_saveActionPlan() {
wfa_actionPlanNotes = document.getElementById('wfa-actionPlanNotes').value;
wfa_saveData();
alert("Action plan notes saved!");
}
// --- Settings Tab ---
function wfa_loadQuadrantSettingsToForm() { /* No quadrant settings for this tool, this is for settings like pillar management */ }
// For this tool, settings tab would be for managing default pillar lists or advanced params not implemented in V1.
// --- PDF Download ---
document.getElementById('wfa-downloadPdfBtn').onclick = async () => {
const range = wfa_getReportDateRange();
const resultsArea = document.getElementById('wfa-analysisResultsArea');
if (!range || !resultsArea || resultsArea.style.display === 'none') {
alert("Please generate an analysis report first."); return;
}
const { jsPDF } = window.jspdf; const pdf = new jsPDF('p', 'mm', 'a4');
let currentY = 15; const margin = 15; const contentWidth = pdf.internal.pageSize.getWidth() - 2*margin;
pdf.setFontSize(18); pdf.setTextColor('#673AB7');
pdf.text("Work Fulfillment Analysis Report", pdf.internal.pageSize.getWidth() / 2, currentY, { align: 'center' });
currentY += 8;
pdf.setFontSize(10); pdf.setTextColor('#512DA8');
const periodText = document.getElementById('wfa-analysisPeriodDisplay').textContent.replace('Analysis for: ','');
pdf.text(`Period: ${periodText}`, pdf.internal.pageSize.getWidth() / 2, currentY, { align: 'center'});
currentY += 10;
// Defined Pillars
pdf.setFontSize(12); pdf.setTextColor('#673AB7');
pdf.text("My Fulfillment Pillars:", margin, currentY); currentY += 6;
pdf.setFontSize(9); pdf.setTextColor('#311B92');
wfa_pillars.forEach(p => {
if (currentY > pdf.internal.pageSize.getHeight() - 10) { pdf.addPage(); currentY = margin; }
const pillarColorRgb = wfa_hexToRgb(p.color);
if (pillarColorRgb) pdf.setFillColor(pillarColorRgb.r, pillarColorRgb.g, pillarColorRgb.b);
else pdf.setFillColor(103, 58, 183); // Default purple
pdf.rect(margin, currentY - 2.5, 3, 3, 'F');
pdf.text(`${p.name} ${p.importance ? '(Importance: '+p.importance+'/5)' : ''}`, margin + 5, currentY); currentY += 5;
});
currentY += 5;
// Pillar Alignment Chart (Image)
const chartCanvas = document.getElementById('wfa-pillarAlignmentChart');
if (chartCanvas && wfa_pillarAlignmentChartInstance && wfa_pillarAlignmentChartInstance.data.datasets[0].data.length > 0) {
if (currentY > pdf.internal.pageSize.getHeight() - 70) { pdf.addPage(); currentY = margin; }
pdf.setFontSize(11); pdf.setTextColor('#673AB7');
pdf.text("Fulfillment Pillar Alignment (Avg. Score):", margin, currentY); currentY += 5;
try {
const chartImgData = chartCanvas.toDataURL('image/png', 1.0);
const imgProps = pdf.getImageProperties(chartImgData);
let pdfImgWidth = contentWidth * 0.8;
let pdfImgHeight = (imgProps.height * pdfImgWidth) / imgProps.width;
const maxChartHeight = 70;
if (pdfImgHeight > maxChartHeight) { pdfImgHeight = maxChartHeight; pdfImgWidth = (imgProps.width * pdfImgHeight) / imgProps.height; }
if(pdfImgWidth > contentWidth) { pdfImgWidth = contentWidth; pdfImgHeight = (imgProps.height * pdfImgWidth) / imgProps.width; if (pdfImgHeight > maxChartHeight) pdfImgHeight = maxChartHeight;}
pdf.addImage(chartImgData, 'PNG', margin + (contentWidth - pdfImgWidth)/2 , currentY, pdfImgWidth, pdfImgHeight, undefined, 'FAST');
currentY += pdfImgHeight + 7;
} catch(e) { console.error("PDF chart error", e); currentY +=5; }
}
// Key Insights
const insightsItems = document.getElementById('wfa-fulfillmentInsightsList').getElementsByTagName('li');
if (insightsItems.length > 0 && !insightsItems[0].textContent.includes("Run analysis")) {
if (currentY > pdf.internal.pageSize.getHeight() - 25) { pdf.addPage(); currentY = margin; }
pdf.setFontSize(11); pdf.setTextColor('#673AB7');
pdf.text("Key Insights:", margin, currentY); currentY += 5;
pdf.setFontSize(9); pdf.setTextColor('#311B92');
Array.from(insightsItems).forEach(item => {
if (currentY > pdf.internal.pageSize.getHeight() - 10) { pdf.addPage(); currentY = margin; }
const itemLines = pdf.splitTextToSize(`- ${item.textContent}`, contentWidth - 5);
pdf.text(itemLines, margin + 5, currentY); currentY += (itemLines.length * 4) + 1;
});
currentY += 3;
}
// Action Plan
if (wfa_actionPlanNotes) {
if (currentY > pdf.internal.pageSize.getHeight() - 30) { pdf.addPage(); currentY = margin; }
pdf.setFontSize(11); pdf.setTextColor('#673AB7');
pdf.text("My Fulfillment Action Plan Notes:", margin, currentY); currentY += 6;
pdf.setFontSize(9); pdf.setTextColor('#311B92');
const planLines = pdf.splitTextToSize(wfa_actionPlanNotes, contentWidth);
pdf.text(planLines, margin, currentY);
}
pdf.save(`Work_Fulfillment_Analysis_${range.start}_to_${range.end}.pdf`);
};
function wfa_hexToRgb(hex) { const r = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return r ? {r:parseInt(r[1],16),g:parseInt(r[2],16),b:parseInt(r[3],16)}:null;}
// --- Initial Load ---
document.addEventListener('DOMContentLoaded', () => {
document.getElementById('wfa-activityDate').value = WFA_TODAY_CONTEXT.toISOString().split('T')[0];
wfa_openTab(null, wfa_tabs[0]);
const firstTabButton = document.querySelector(`.wfa-tab-button[onclick*="'${wfa_tabs[0]}'"]`);
if (firstTabButton) firstTabButton.classList.add('active');
else console.error("First tab button not found for initial activation.");
wfa_updateNavButtons();
});