active nav style; fixed stats; added age; fixed sunday issue; frame style, 1st step;
This commit is contained in:
parent
725bb127b0
commit
26784b4aba
6 changed files with 110 additions and 26 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { writable } from 'svelte/store';
|
import { writable } from 'svelte/store';
|
||||||
|
import { convertMinutes } from '$lib/utils';
|
||||||
|
|
||||||
export let actions = []; // Array of actions passed to the component
|
export let actions = []; // Array of actions passed to the component
|
||||||
|
|
||||||
|
|
@ -36,16 +37,6 @@
|
||||||
|
|
||||||
// Update immediately when the component has access to actions
|
// Update immediately when the component has access to actions
|
||||||
updateTimes();
|
updateTimes();
|
||||||
|
|
||||||
function convertMinutes(minutes: number): string {
|
|
||||||
if (minutes < 60) {
|
|
||||||
return `${minutes}m`;
|
|
||||||
} else {
|
|
||||||
const hours = Math.floor(minutes / 60);
|
|
||||||
const remainingMinutes = minutes % 60;
|
|
||||||
return `${hours}h${remainingMinutes}m`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const interval = setInterval(updateTimes, 2000); // Update every 2 minutes
|
const interval = setInterval(updateTimes, 2000); // Update every 2 minutes
|
||||||
|
|
|
||||||
9
src/lib/utils.ts
Normal file
9
src/lib/utils.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
export function convertMinutes(minutes: number): string {
|
||||||
|
if (minutes < 60) {
|
||||||
|
return `${minutes}m`;
|
||||||
|
} else {
|
||||||
|
const hours = Math.floor(minutes / 60);
|
||||||
|
const remainingMinutes = minutes % 60;
|
||||||
|
return `${hours}h${remainingMinutes}m`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { page } from '$app/stores';
|
||||||
import { currentUser } from '$lib/pocketbase';
|
import { currentUser } from '$lib/pocketbase';
|
||||||
import 'normalize.css/normalize.css';
|
import 'normalize.css/normalize.css';
|
||||||
import 'milligram/dist/milligram.min.css';
|
import 'milligram/dist/milligram.min.css';
|
||||||
|
|
@ -16,18 +17,18 @@
|
||||||
|
|
||||||
<main class="container">
|
<main class="container">
|
||||||
<header>
|
<header>
|
||||||
<h1>Coover Tracker</h1>
|
<h1><a href="/">Coover Tracker</a></h1>
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/">Home</a></li>
|
<li><a href="/" class={$page.url.pathname === '/' ? 'active' : ''}>Home</a></li>
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
<li><a href="/stats">Stats</a></li>
|
<li><a href="/stats" class={$page.url.pathname === '/stats' ? 'active' : ''}>Stats</a></li>
|
||||||
<li><a href="/calendar">Calendar</a></li>
|
<li><a href="/calendar" class={$page.url.pathname === '/calendar' ? 'active' : ''}>Calendar</a></li>
|
||||||
{/if}
|
{/if}
|
||||||
<li>
|
<li>
|
||||||
<a href="/signin">
|
<a href="/signin" class={$page.url.pathname === '/signin' ? 'active' : ''}>
|
||||||
{#if $currentUser}
|
{#if $currentUser}
|
||||||
<strong>( {$currentUser.username} )</strong>
|
( {$currentUser.username} )
|
||||||
{:else}
|
{:else}
|
||||||
'Sign In'
|
'Sign In'
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
// Adjust currentWeekStart to the beginning of the week
|
// Adjust currentWeekStart to the beginning of the week
|
||||||
currentWeekStart.setDate(currentWeekStart.getDate() - currentWeekStart.getDay());
|
currentWeekStart.setDate(currentWeekStart.getDate() - currentWeekStart.getDay());
|
||||||
|
currentWeekStart.setHours(0, 0, 0, 0);
|
||||||
const oldestDataDate: Date = new Date('2024-01-22');
|
const oldestDataDate: Date = new Date('2024-01-22');
|
||||||
|
|
||||||
// Reactive store to hold actions
|
// Reactive store to hold actions
|
||||||
|
|
@ -62,7 +63,7 @@
|
||||||
|
|
||||||
// Function to get the day of the week from a date string
|
// Function to get the day of the week from a date string
|
||||||
const getDayOfWeek = (dateString: string) => {
|
const getDayOfWeek = (dateString: string) => {
|
||||||
return new Date(dateString).getDay();
|
return new Date(dateString).getDay(); // 0 = Sunday, 1 = Monday, etc.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Function to get the hour of the day from a date string
|
// Function to get the hour of the day from a date string
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { pb } from '$lib/pocketbase';
|
import { pb } from '$lib/pocketbase';
|
||||||
|
import { parse } from 'svelte/compiler';
|
||||||
|
import { convertMinutes } from '$lib/utils';
|
||||||
|
|
||||||
type Action = {
|
type Action = {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -78,7 +80,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
const totalNaps = Array.from(daysMap.values()).reduce((acc, naps) => acc + naps, 0);
|
const totalNaps = Array.from(daysMap.values()).reduce((acc, naps) => acc + naps, 0);
|
||||||
return daysMap.size > 0 ? totalNaps / daysMap.size : 0;
|
return daysMap.size > 0 ? parseFloat((totalNaps / daysMap.size).toFixed(1)) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateAvgSleepPerDay(actions: Action[]): number {
|
function calculateAvgSleepPerDay(actions: Action[]): number {
|
||||||
|
|
@ -161,7 +163,7 @@
|
||||||
totalDays += interval;
|
totalDays += interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseFloat((totalDays / (poopDates.length - 1)).toFixed(2));
|
return parseFloat((totalDays / (poopDates.length - 1)).toFixed(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateAvgMealsPerDay(actions: any[]): number {
|
function calculateAvgMealsPerDay(actions: any[]): number {
|
||||||
|
|
@ -194,18 +196,77 @@
|
||||||
// Return the average to one decimal place
|
// Return the average to one decimal place
|
||||||
return parseFloat(average.toFixed(1));
|
return parseFloat(average.toFixed(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateAgeFormatted(birthdate: Date): string {
|
||||||
|
const now = new Date();
|
||||||
|
const birthDate = new Date(birthdate);
|
||||||
|
const diffInMilliseconds = now.getTime() - birthDate.getTime();
|
||||||
|
const diffInDays = diffInMilliseconds / (1000 * 3600 * 24);
|
||||||
|
const diffInWeeks = diffInDays / 7;
|
||||||
|
const diffInMonths = diffInDays / 30.4375; // Average days per month in a year
|
||||||
|
const diffInYears = diffInDays / 365.25; // Average, accounting for leap years
|
||||||
|
|
||||||
|
// Determine the largest unit (years, months, weeks, days) for the age
|
||||||
|
if (diffInYears >= 1) {
|
||||||
|
return formatAge(diffInYears, 'year');
|
||||||
|
} else if (diffInMonths >= 1) {
|
||||||
|
return formatAge(diffInMonths, 'month');
|
||||||
|
} else if (diffInWeeks >= 1) {
|
||||||
|
return formatAge(diffInWeeks, 'week');
|
||||||
|
} else {
|
||||||
|
return formatAge(diffInDays, 'day');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAge(value: number, unit: string): string {
|
||||||
|
// Round to nearest quarter
|
||||||
|
const nearestQuarter = Math.round(value * 4) / 4;
|
||||||
|
const integerPart = Math.floor(nearestQuarter);
|
||||||
|
const fractionPart = nearestQuarter - integerPart;
|
||||||
|
let fractionString = '';
|
||||||
|
|
||||||
|
switch (fractionPart) {
|
||||||
|
case 0.25:
|
||||||
|
fractionString = '¼';
|
||||||
|
break;
|
||||||
|
case 0.5:
|
||||||
|
fractionString = '½';
|
||||||
|
break;
|
||||||
|
case 0.75:
|
||||||
|
fractionString = '¾';
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
fractionString = '';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// This case should not happen with rounding to nearest quarter
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ageString = `${integerPart}${fractionString} ${unit}${integerPart > 1 || integerPart === 0 ? 's' : ''}`;
|
||||||
|
return ageString;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<h2>Stats</h2>
|
<h2>Stats</h2>
|
||||||
<p>(diapers and meals should be correct now)</p>
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>{stats.avgSleepPerNap} min / nap</li>
|
<li>{calculateAgeFormatted(new Date('2023-08-30'))} old</li>
|
||||||
<li>{stats.avgNapsPerDay} naps / day</li>
|
</ul>
|
||||||
<!-- <li>{stats.avgSleepPerDay} min of sleep / day</li> -->
|
<h3>Input</h3>
|
||||||
<li>{stats.avgSleepPerNap * stats.avgNapsPerDay} min of sleep / day</li>
|
<ul>
|
||||||
<li>{stats.avgDiaperChanges} diapers / day</li>
|
|
||||||
<li>{stats.avgPoops} days between poops</li>
|
|
||||||
<li>{stats.avgEatingTimes} meals / day</li>
|
<li>{stats.avgEatingTimes} meals / day</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<h3>Output</h3>
|
||||||
|
<ul>
|
||||||
|
<li>{stats.avgDiaperChanges} diapers / day</li>
|
||||||
|
<li>{stats.avgPoops} days between poops</li>
|
||||||
|
</ul>
|
||||||
|
<h3>Downtime</h3>
|
||||||
|
<ul>
|
||||||
|
<li>{convertMinutes(stats.avgSleepPerNap)} / nap</li>
|
||||||
|
<li>{stats.avgNapsPerDay} naps / day</li>
|
||||||
|
<!-- <li>{stats.avgSleepPerDay} min of sleep / day</li> -->
|
||||||
|
<li>{convertMinutes(Math.round(stats.avgSleepPerNap * stats.avgNapsPerDay))} of sleep / day</li>
|
||||||
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ nav ul li {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav ul li a.active{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
|
@ -98,6 +102,11 @@ header {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
margin: 0 -2rem;
|
margin: 0 -2rem;
|
||||||
padding: 2rem 2rem 0 2rem;
|
padding: 2rem 2rem 0 2rem;
|
||||||
|
border-radius: 3rem 3rem 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a {
|
||||||
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* aside {
|
/* aside {
|
||||||
|
|
@ -139,4 +148,16 @@ section {
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
min-width: 10ch;
|
min-width: 10ch;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
main.container {
|
||||||
|
max-width: 80rem;
|
||||||
|
background: var(--c-bkg);
|
||||||
|
border-radius: 3rem;
|
||||||
|
margin: 3rem auto;
|
||||||
|
padding-bottom: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #303443;
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue