Untitled
unknown
plain_text
7 months ago
8.3 kB
6
Indexable
function HabitTracker() {
// State Management
const [activeView, setActiveView] = dc.useState('weekly');
const [selectedDate, setSelectedDate] = dc.useState(dc.luxon.DateTime.now());
const [editingTime, setEditingTime] = dc.useState(null);
const [currentPage, setCurrentPage] = dc.useState(0);
// Data Queries and Utility Functions
const dailyNotes = dc.useQuery(`
@page
AND path("002 Journal")
`);
const sortedNotes = dc.useMemo(() => {
return [...dailyNotes].sort((a, b) => b.$name.localeCompare(a.$name));
}, [dailyNotes]);
const getNotesForPeriod = (startDate) => {
return sortedNotes.filter(note => {
const noteDate = dc.luxon.DateTime.fromISO(note.$name);
return noteDate >= startDate;
});
};
// Add new function to get notes for specific date range
const getNotesForDateRange = (startDate, endDate) => {
return sortedNotes.filter(note => {
const noteDate = dc.luxon.DateTime.fromISO(note.$name);
// Use startOf('day') and endOf('day') to ensure full day coverage
return noteDate >= startDate.startOf('day') && noteDate <= endDate.endOf('day');
});
};
const last30DaysNotes = dc.useMemo(() =>
getNotesForPeriod(selectedDate.minus({ days: 30 })),
[sortedNotes, selectedDate]
);
const yearToDateNotes = dc.useMemo(() =>
getNotesForPeriod(selectedDate.startOf('year')),
[sortedNotes, selectedDate]
);
const currentMonthNotes = dc.useMemo(() =>
getNotesForPeriod(selectedDate.startOf('month')),
[sortedNotes, selectedDate]
);
const previousMonthNotes = dc.useMemo(() =>
sortedNotes.filter(note => {
const noteDate = dc.luxon.DateTime.fromISO(note.$name);
const monthAgo = selectedDate.minus({ months: 1 });
return noteDate >= monthAgo && noteDate < selectedDate.startOf('month');
}),
[sortedNotes, selectedDate]
);
const getHabitStatus = (entry, habitId) => {
const habit = HABITS.find(h => h.id === habitId);
const value = entry?.value(habitId) ?? 0;
return value >= habit.defaultDuration;
};
const getHabitDuration = (entry, habitId) => {
return entry?.value(habitId) ?? null;
};
const calculateCompletedHabits = (entry) => {
if (!entry) return 0;
return HABITS.reduce((count, habit) =>
count + (getHabitStatus(entry, habit.id) ? 1 : 0), 0);
};
const calculatePerfectDays = (notes) => {
return notes.reduce((count, note) =>
count + (calculateCompletedHabits(note) === HABITS.length ? 1 : 0), 0);
};
const calculateTrends = () => {
const trends = {
last30Days: {
perfectDays: calculatePerfectDays(last30DaysNotes),
habitMetrics: {}
},
yearToDate: {
perfectDays: calculatePerfectDays(yearToDateNotes),
habitMetrics: {}
},
currentMonth: {
perfectDays: calculatePerfectDays(currentMonthNotes),
progress: 0
}
};
trends.currentMonth.progress = (trends.currentMonth.perfectDays / GOALS.perfectDays.monthly) * 100;
HABITS.forEach(habit => {
const last30Total = last30DaysNotes.reduce((sum, note) => {
const value = note?.value(habit.id);
const numValue = value ? Number(value) : 0;
return sum + (isNaN(numValue) ? 0 : numValue);
}, 0);
const ytdTotal = yearToDateNotes.reduce((sum, note) => {
const value = note?.value(habit.id);
const numValue = value ? Number(value) : 0;
return sum + (isNaN(numValue) ? 0 : numValue);
}, 0);
const previousMonthTotal = previousMonthNotes.reduce((sum, note) => {
const value = note?.value(habit.id);
const numValue = value ? Number(value) : 0;
return sum + (isNaN(numValue) ? 0 : numValue);
}, 0);
trends.last30Days.habitMetrics[habit.id] = {
total: last30Total,
previousPeriodTotal: previousMonthTotal
};
trends.yearToDate.habitMetrics[habit.id] = {
total: ytdTotal
};
});
return trends;
};
// Get current week's notes based on selected date
const currentWeekNotes = dc.useMemo(() =>
sortedNotes.filter(note => {
const noteDate = dc.luxon.DateTime.fromISO(note.$name);
const startOfWeek = selectedDate.startOf('week');
const endOfWeek = selectedDate.endOf('week');
return noteDate >= startOfWeek && noteDate <= endOfWeek;
}),
[sortedNotes, selectedDate]
);
// Action Handlers
async function updateHabit(entry, habitId) {
const file = app.vault.getAbstractFileByPath(entry.$path);
await app.fileManager.processFrontMatter(file, (frontmatter) => {
const habit = HABITS.find(h => h.id === habitId);
const currentValue = frontmatter[habitId];
frontmatter[habitId] = currentValue ? 0 : habit.defaultDuration;
});
}
async function updateHabitDuration(entry, habitId, duration) {
const file = app.vault.getAbstractFileByPath(entry.$path);
await app.fileManager.processFrontMatter(file, (frontmatter) => {
frontmatter[habitId] = parseInt(duration) || 0;
});
setEditingTime(null);
}
const navigateDate = (direction) => {
setSelectedDate(prev => prev.plus({ days: direction }));
};
// Main Layout
return (
<div style={{
width: '100%',
margin: '0 auto',
padding: '24px',
display: 'flex',
flexDirection: 'column',
gap: '24px'
}}>
<NavigationControls
selectedDate={selectedDate}
navigateDate={navigateDate}
activeView={activeView}
setActiveView={setActiveView}
/>
<StyledCard>
<CalendarView
selectedDate={selectedDate}
sortedNotes={getNotesForDateRange(selectedDate.minus({ days: 6 }), selectedDate)}
getHabitStatus={getHabitStatus}
calculateCompletedHabits={calculateCompletedHabits}
updateHabit={updateHabit}
getHabitDuration={getHabitDuration}
editingTime={editingTime}
setEditingTime={setEditingTime}
updateHabitDuration={updateHabitDuration}
/>
<div style={{
display: 'flex',
justifyContent: 'center',
gap: '16px',
marginTop: '16px',
paddingTop: '16px',
borderTop: '1px solid var(--background-modifier-border)'
}}>
<ActionButton
icon="📅"
onClick={() => setActiveView(activeView === 'weekly' ? null : 'weekly')}
isActive={activeView === 'weekly'}
extraStyles={{ padding: '12px' }}
/>
<ActionButton
icon="🚀"
onClick={() => setActiveView(activeView === 'goals' ? null : 'goals')}
isActive={activeView === 'goals'}
extraStyles={{ padding: '12px' }}
/>
<ActionButton
icon="🚧"
onClick={() => setActiveView(activeView === 'stats' ? null : 'stats')}
isActive={activeView === 'stats'}
extraStyles={{ padding: '12px' }}
/>
<ActionButton
icon="🎯"
onClick={() => setActiveView(activeView === 'history' ? null : 'history')}
isActive={activeView === 'history'}
extraStyles={{ padding: '12px' }}
/>
</div>
</StyledCard>
{activeView === 'weekly' && (
<WeeklyGoalsView entries={currentWeekNotes} />
)}
{activeView === 'goals' && (
<GoalsView
entries={currentMonthNotes}
daysInMonth={new Date(new Date().getFullYear(), new Date().getMonth() + 1, 0).getDate()}
/>
)}
{activeView === 'stats' && <TrendsView trends={calculateTrends()} />}
{activeView === 'history' && (
<HistoricalView
sortedNotes={sortedNotes}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
updateHabit={updateHabit}
getHabitStatus={getHabitStatus}
getHabitDuration={getHabitDuration}
editingTime={editingTime}
setEditingTime={setEditingTime}
updateHabitDuration={updateHabitDuration}
calculateCompletedHabits={calculateCompletedHabits}
/>
)}
</div>
);
}
return HabitTracker;Editor is loading...
Leave a Comment