From b5bedcdbe527ec896544b0508ba43279dd2f90f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danko=20Lu=C4=8Di=C4=87?= Date: Sun, 8 Jun 2025 17:54:45 +0200 Subject: [PATCH 1/2] change favicon according to current tracker state - stop button icon if workday not started - working button icon when user is working - break button if on break --- index.html | 1 - public/favicon/initial.ico | Bin 0 -> 15406 bytes public/favicon/on-break.ico | Bin 0 -> 15406 bytes public/favicon/working.ico | Bin 0 -> 15406 bytes src/App.svelte | 10 ++++++++++ src/ui/WorkdayForm.svelte | 12 ++++++++++++ tests/e2e.spec.ts | 24 ++++++++++++++++++++++++ 7 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 public/favicon/initial.ico create mode 100644 public/favicon/on-break.ico create mode 100644 public/favicon/working.ico diff --git a/index.html b/index.html index f2c5ab4..7d5b322 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,6 @@ - Work Hours Tracker diff --git a/public/favicon/initial.ico b/public/favicon/initial.ico new file mode 100644 index 0000000000000000000000000000000000000000..d62c8f9dfa7b6f8903ded31b59cc1dc686eb0d99 GIT binary patch literal 15406 zcmeHNPixdb6d$qRwI00hL9}=(D4Rb$=x5L`An2hNKSRMkN${+oAfg{ZMSBtKCX3fr zi=ZB>t=ik_MQAm?-)4tdY|Q2*A+u}Wz+)zpdGr3vZzeM@qtp>~RL##T$~$U#PN}X^ zs?*7y7mh3Skm?o}NAFK5_4#_UL zyt#`4qY~>(hv)(k)~BMB+;T1^q5prc|b=gme4edXC%WItp#mC%>SKe!hX z|Nndb3I1gM$@(w5YPzmy|IxIpy#6xz{$Ad@HtL4w51czI zo?mM7k?b{`0WFnlfUx_ejd%&>D32(vLAu3(_c#t52Rf5!>eb5mxnAgACEd4c&zsiftG^4KVZ+On z?ire=yX1GPF$0e8GF{1rn{Jolf%iGQ)6}*fdXETGpQcGCjxNAFjfI66w)Z*Ok5%)-qW_2ILHc!bwN?7KdOJ5ZIX+fp{lByRw3!1tqZjyVT-Us^=+AH{ zv#I#sd}9RvOz=>1!H?jFj~c@v_!0aVH1JD;AHfeFHHJg*Blt0B;FknHf*(F=42R%H z@MF-xFA07GKYY{}4#AJX&--ULQpAATB*cvKVxHr5W#gaYJkwMb{SC%!j0^lv|M0Bc zldo$t{lD9v_5Y_y=-#dC2V4BOuhGAL?)2zC@J!twfX=vcg8LguXlw`9#=qC`-tAb!F0r4{wMYFkUK_Q4%`~V^ff?%^7 zL=UBD=|Qj6s_iZHQfPI3Z#R={CbK&`JCaSzTX@XhH}B2c-+R0Jb{IRtj(%D` zEQE5i@Npj0m)Cju@xeuZe{Uxstcz_v4@(*Ur=?3izpyU0iNANR|3~`;cxnIi_kp_j ziL_PsKRT|eqy2y~`vCGb0{<;*ufchxK|!Mv<4ghQ0uaWhq|3Wsy6idjI?!Qf@0u|u zeT)ia*c%O}bOm(^WY_H9s(bS9$3Z6Fcg8qrtP|MW1)CeMlTrUXW#gU^YkBQmNB(O4 zn%~~u?4#BC)$X%ZH6FLewEJ%@-{gj-?iG`Hu|Xl{VRDqZr!x_i{m%# z9}W5Tf$2W7@(14^_}!WD`^A_KWUt^iU{2qkD4Tx+yEVJz)yhM-7xn?l;8$IN>ww)K!^oFljL;*mBNJU`WN zta;FV+t_c0=CAfH>Wq7Mw^%#_e!2^G&oyVDW4lmS*uy=m2>HOrI=rJax9OCh0;ax> zl1@6hAm-s-Ynr73Yq#?zoq{-*ZHJ>b56Y|JL)0zq9#PO6xCfb#sv1rn>Vv zUs7pe|Ie@PbaSR+<;&~pfd0Dkll10eq`zqSuub+)_k1Ay5Pr}CGJoMQCaEO+5Pk?h z>0+(uN4oC=y7NVOvNriI`7ik|`LE~&viAXsKZ7`-_$eYMY!iM4u|m(^ew*mV6W#eT zdy9SKzvRDY0a?5fu_9|vGJe*U7yR#QFQtUJTG3v*^F@AH+r<8|^@iO?`u9H*@~}Vr z!^`cQyy<@7|J~uZ|3CE{Ysu6OdGX_Ujpp5Rr#t_F$Ljn5>WpVj@cf2Xvu*_Kp`E`l t_50rW4Sog%@54&fdIGnz literal 0 HcmV?d00001 diff --git a/public/favicon/working.ico b/public/favicon/working.ico new file mode 100644 index 0000000000000000000000000000000000000000..1e4fc167d0d25f862c63f16ba0e37b418c034095 GIT binary patch literal 15406 zcmeHNTS!zv7@kPz)gF5HAxe4*3U~KNB-BHXQLhDdfrRR*9=bq6lxM@D2!%qi3-rG@znvfP~+qiN1|LX{8UqeVa+yf?HVYfpy@0F^H)+Ei! zF(`SyV*F;dR=kpm6vP#=V>ZZ_{f%{RYc1`2c9Ev)t32^3w*UI#D#a^3`jhrOb??W? zzR-z>e%$Qc`tfS-)K5_MPVscZx9#d3$J5E)&5miN*uJ;*42-)o9b|iNgGiareP&=S>P6jhJW3pj1J@{It+zosaCYOrr-%ZrXRwoZ~%5*%-##gc;e5 zoC?@+W6JB;$h!|PncsqW(l-}~c@kpoyB|URIIod;@b|e+i(r2 zY5j@VYz>@@8{Jks7Qk2-S;S)43+)Rmh5z9`32f1lYA9~|B#)1-U1gb(O-VD>4BGt> z#C{3p2)+XQ8o(`C7rT|Li$st!xJF*pZY@ce(SyK!(D%FvY`z*p$QgZjUR$vN=IIc` zUF)6!PwZ^2;KR9S1?&f2=dlgby(u$x6EyWLOq_VQpypvLH0&{oeSazTZ8394Uv$6c z6vvbI_fq2D)lyBnZy%!VS9U1d_f#4G&rjksGd@CxKfa_N9-XGIFKyTE2akS`e{S+G z&FAwpmzkhr-+Sr%2Pdg@VT(3DMEJA)C#>^RIXd#Co%Y;0LfbC~uPeX&nJ?M&A3EF% zx_TI_u4M5qjKv&Xod`r%GWoNyDEKm?tHVllrHViEC7V&JE7kl}>naHTY%J#LN>08C znm_YJR=(23UzVME@KCnrYful_Fj?(XpgpW|WUpG}W* zy+?(GKfcEfe@f7<8~b5hvCZ@QxK94Kj<9vo2|78ppiZ3DWxDwD=9#eY7uAVQ{xWo; zi~p=nPAOUMtJ43hPWEx-6Q?*kwaetMQYWhU^FDW+`0%#-jek%&aq>%qzYd+q;$PT5 z)UeMZgMUZU9@^2oS7|`N{K;d^_=P&%u``Pmw>9GI+ literal 0 HcmV?d00001 diff --git a/src/App.svelte b/src/App.svelte index ddf613f..1b5d869 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -30,6 +30,16 @@ } + + {#if !user} + + {/if} + +

Welcome {user ? user.settings.username : "to Work Hours Tracker"}

diff --git a/src/ui/WorkdayForm.svelte b/src/ui/WorkdayForm.svelte index 24c592f..418bac0 100644 --- a/src/ui/WorkdayForm.svelte +++ b/src/ui/WorkdayForm.svelte @@ -26,6 +26,18 @@ } + + + + {#if tracker.hasWorkdayStarted()} {/if} diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index d6d906e..74f848b 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -4,6 +4,10 @@ test("first visit, full workday", async ({ page }) => { await page.goto("/"); await expect(page).toHaveTitle("Work Hours Tracker"); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/initial.ico", + ); await expect( page.getByRole("heading", { name: "Welcome to Work Hours Tracker" }), ).toBeVisible(); @@ -21,6 +25,10 @@ test("first visit, full workday", async ({ page }) => { await expect(page.getByLabel("Username")).not.toBeVisible(); await expect(page.getByLabel("Daily paid break")).not.toBeVisible(); await expect(page.getByText("Start tracking!")).not.toBeVisible(); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/initial.ico", + ); // User work hours tracking interface is shown await expect( @@ -45,6 +53,10 @@ test("first visit, full workday", async ({ page }) => { ).toBeDisabled(); await expect(page.getByRole("button", { name: "Start Break" })).toBeEnabled(); await expect(page.getByRole("button", { name: "End Workday" })).toBeEnabled(); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/working.ico", + ); // Take a break at 8:35:00 await page.clock.setFixedTime(new Date(2025, 2, 2, 8, 35, 0)); @@ -60,6 +72,10 @@ test("first visit, full workday", async ({ page }) => { page.getByRole("button", { name: "Start Break" }), ).not.toBeVisible(); await expect(page.getByRole("button", { name: "End Break" })).toBeVisible(); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/on-break.ico", + ); // End the break at 9:05:00 await page.clock.setFixedTime(new Date(2025, 2, 2, 9, 5, 0)); @@ -89,6 +105,10 @@ test("first visit, full workday", async ({ page }) => { page.getByRole("button", { name: "Yes, I'm done for today" }), ).toBeVisible(); await expect(page.getByRole("button", { name: "Cancel" })).toBeVisible(); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/working.ico", + ); await page.getByRole("button", { name: "Yes, I'm done for today" }).click(); @@ -115,6 +135,10 @@ test("first visit, full workday", async ({ page }) => { await expect( page.getByRole("button", { name: "End Workday" }), ).toBeDisabled(); + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/initial.ico", + ); }); test("User data persists through reloads", async ({ page }) => { From d65e3a2015df24f9f0afa2d902cfd6408e4a079f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Danko=20Lu=C4=8Di=C4=87?= Date: Sun, 8 Jun 2025 22:03:16 +0200 Subject: [PATCH 2/2] display current status above work duration --- src/App.svelte | 7 ++----- src/ui/Favicon.svelte | 9 +++++++++ src/ui/Status.svelte | 38 +++++++++++++++++++++++++++++++++++++ src/ui/WorkdayEvents.svelte | 2 +- src/ui/WorkdayForm.svelte | 16 +++++++++------- tests/e2e.spec.ts | 31 ++++++++++++++++++++++++++---- 6 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 src/ui/Favicon.svelte create mode 100644 src/ui/Status.svelte diff --git a/src/App.svelte b/src/App.svelte index 1b5d869..9da338b 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -4,6 +4,7 @@ import { type User } from "./lib/tracker"; import WorkdayForm from "./ui/WorkdayForm.svelte"; import { getDatabase } from "./lib/database"; + import Favicon from "./ui/Favicon.svelte"; let user: User | null = $state(null); @@ -32,11 +33,7 @@ {#if !user} - + {/if} diff --git a/src/ui/Favicon.svelte b/src/ui/Favicon.svelte new file mode 100644 index 0000000..29cd2e0 --- /dev/null +++ b/src/ui/Favicon.svelte @@ -0,0 +1,9 @@ + + + diff --git a/src/ui/Status.svelte b/src/ui/Status.svelte new file mode 100644 index 0000000..8a74adb --- /dev/null +++ b/src/ui/Status.svelte @@ -0,0 +1,38 @@ + + +
{mapping[getStatusClass()]}
+ + diff --git a/src/ui/WorkdayEvents.svelte b/src/ui/WorkdayEvents.svelte index 18a47fd..fc14508 100644 --- a/src/ui/WorkdayEvents.svelte +++ b/src/ui/WorkdayEvents.svelte @@ -21,7 +21,7 @@ {#if type === "start-workday"} Workday Started {:else if type === "break-ongoing"} - On Break + Break started {:else if type === "break"} Break {:else if type === "end-workday"} diff --git a/src/ui/WorkdayForm.svelte b/src/ui/WorkdayForm.svelte index 418bac0..d2b18b2 100644 --- a/src/ui/WorkdayForm.svelte +++ b/src/ui/WorkdayForm.svelte @@ -2,6 +2,8 @@ import { createTracker, type User } from "../lib/tracker"; import Button from "./Button.svelte"; import ConfirmationModal from "./ConfirmationModal.svelte"; + import Favicon from "./Favicon.svelte"; + import Status from "./Status.svelte"; import WorkdayEvents from "./WorkdayEvents.svelte"; import WorkDuration from "./WorkDuration.svelte"; @@ -27,17 +29,17 @@ - + + {#if tracker.hasWorkdayStarted()} {/if} diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index 74f848b..dcab8bf 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -25,10 +25,6 @@ test("first visit, full workday", async ({ page }) => { await expect(page.getByLabel("Username")).not.toBeVisible(); await expect(page.getByLabel("Daily paid break")).not.toBeVisible(); await expect(page.getByText("Start tracking!")).not.toBeVisible(); - await expect(page.getByTestId("favicon")).toHaveAttribute( - "href", - "/work-hours-tracker/favicon/initial.ico", - ); // User work hours tracking interface is shown await expect( @@ -44,6 +40,18 @@ test("first visit, full workday", async ({ page }) => { page.getByRole("button", { name: "End Workday" }), ).toBeDisabled(); + await expect(page.getByText("Not working")).toBeVisible(); + await expect(page.getByText("Not working")).toHaveCSS( + "color", + "rgb(255, 0, 0)", + ); + + // Favicon updated + await expect(page.getByTestId("favicon")).toHaveAttribute( + "href", + "/work-hours-tracker/favicon/initial.ico", + ); + // Workday starts at 8:05:00 await page.clock.setFixedTime(new Date(2025, 2, 2, 8, 5, 0)); await page.getByRole("button", { name: "Start Workday" }).click(); @@ -53,6 +61,11 @@ test("first visit, full workday", async ({ page }) => { ).toBeDisabled(); await expect(page.getByRole("button", { name: "Start Break" })).toBeEnabled(); await expect(page.getByRole("button", { name: "End Workday" })).toBeEnabled(); + await expect(page.getByText("Working")).toBeVisible(); + await expect(page.getByText("Working")).toHaveCSS( + "color", + "rgb(127, 255, 0)", + ); await expect(page.getByTestId("favicon")).toHaveAttribute( "href", "/work-hours-tracker/favicon/working.ico", @@ -72,6 +85,11 @@ test("first visit, full workday", async ({ page }) => { page.getByRole("button", { name: "Start Break" }), ).not.toBeVisible(); await expect(page.getByRole("button", { name: "End Break" })).toBeVisible(); + await expect(page.getByText("On break")).toBeVisible(); + await expect(page.getByText("On break")).toHaveCSS( + "color", + "rgb(255, 234, 0)", + ); await expect(page.getByTestId("favicon")).toHaveAttribute( "href", "/work-hours-tracker/favicon/on-break.ico", @@ -135,6 +153,11 @@ test("first visit, full workday", async ({ page }) => { await expect( page.getByRole("button", { name: "End Workday" }), ).toBeDisabled(); + await expect(page.getByText("Not working")).toBeVisible(); + await expect(page.getByText("Not working")).toHaveCSS( + "color", + "rgb(255, 0, 0)", + ); await expect(page.getByTestId("favicon")).toHaveAttribute( "href", "/work-hours-tracker/favicon/initial.ico",