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)
'; 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();
});