feat: Reinitialize frontend with SvelteKit and TypeScript

- Delete old Vite+Svelte frontend
- Initialize new SvelteKit project with TypeScript
- Configure Tailwind CSS v4 + DaisyUI
- Implement JWT authentication with auto-refresh
- Create login page with form validation (Zod)
- Add protected route guards
- Update Docker configuration for single-stage build
- Add E2E tests with Playwright (6/11 passing)
- Fix Svelte 5 reactivity with $state() runes

Known issues:
- 5 E2E tests failing (timing/async issues)
- Token refresh implementation needs debugging
- Validation error display timing
This commit is contained in:
2026-02-17 16:19:59 -05:00
parent 54df6018f5
commit de2d83092e
28274 changed files with 3816354 additions and 90 deletions

View File

@@ -0,0 +1 @@
import{a as m,f as b}from"../chunks/pJd4F_Tq.js";import{i as j}from"../chunks/BgHfHpED.js";import{A as N,J as P,p as k,b as A,s as u,c as r,r as s,t as R,ad as T,ae as U}from"../chunks/CCV2x70u.js";import{u as q,l as z,a as B}from"../chunks/CrZRXG6z.js";import{s as E,e as F}from"../chunks/Bx__7-vK.js";import{i as y}from"../chunks/CC5oASRR.js";import{s as G,a as I}from"../chunks/JkAhLmb1.js";import{g as K}from"../chunks/DBDqKY8A.js";function O(v,o,a,n,d){var c;N&&P();var e=(c=o.$$slots)==null?void 0:c[a],i=!1;e===!0&&(e=o.children,i=!0),e===void 0||e(v,i?()=>n:n)}var Q=b('<li><a href="/team-members">Team Members</a></li> <li><a href="/projects">Projects</a></li>',1),S=b('<div class="dropdown dropdown-end"><label tabindex="0" class="btn btn-ghost btn-circle avatar"><div class="w-10 rounded-full bg-primary"><span class="text-xl"> </span></div></label> <ul tabindex="0" class="mt-3 p-2 shadow menu menu-compact dropdown-content bg-base-100 rounded-box w-52"><li><a href="/dashboard" class="justify-between">Dashboard</a></li> <!> <li><a href="/reports">Reports</a></li> <div class="divider"></div> <li><button class="text-error">Logout</button></li></ul></div>'),V=b('<a href="/login" class="btn btn-primary btn-sm">Login</a>'),W=b('<nav class="navbar bg-base-100 shadow-lg"><div class="flex-1"><a href="/" class="btn btn-ghost normal-case text-xl">Headroom</a></div> <div class="flex-none gap-2"><!></div></nav>');function X(v,o){k(o,!1);const a=()=>I(q,"$user",n),[n,d]=G();async function e(){await z(),K("/login")}j();var i=W(),c=u(r(i),2),L=r(c);{var $=l=>{var f=S(),p=r(f),g=r(p),h=r(g),C=r(h,!0);s(h),s(g),s(p);var x=u(p,2),_=u(r(x),2);{var D=t=>{var J=Q();T(2),m(t,J)};y(_,t=>{(a().role==="superuser"||a().role==="manager")&&t(D)})}var w=u(_,6),H=r(w);s(w),s(x),s(f),R(t=>E(C,t),[()=>{var t;return(t=a().email)==null?void 0:t.charAt(0).toUpperCase()}]),F("click",H,e),m(l,f)},M=l=>{var f=V();m(l,f)};y(L,l=>{a()?l($):l(M,!1)})}s(c),s(i),m(v,i),A(),d()}var Y=b('<div class="min-h-screen bg-base-200"><!> <main class="container mx-auto px-4 py-6"><!></main></div>');function ia(v,o){k(o,!1),U(()=>{B()}),j();var a=Y(),n=r(a);X(n,{});var d=u(n,2),e=r(d);O(e,o,"default",{}),s(d),s(a),m(v,a),A()}export{ia as component};

View File

@@ -0,0 +1 @@
import{a as h,f as g}from"../chunks/pJd4F_Tq.js";import{i as l}from"../chunks/BgHfHpED.js";import{p as v,af as d,t as _,b as x,c as e,r as o,s as $}from"../chunks/CCV2x70u.js";import{s as p}from"../chunks/Bx__7-vK.js";import{s as b,p as m}from"../chunks/DBDqKY8A.js";const k={get error(){return m.error},get status(){return m.status}};b.updated.check;const f=k;var E=g("<h1> </h1> <p> </p>",1);function A(i,c){v(c,!1),l();var t=E(),r=d(t),n=e(r,!0);o(r);var s=$(r,2),u=e(s,!0);o(s),_(()=>{var a;p(n,f.status),p(u,(a=f.error)==null?void 0:a.message)}),h(i,t),x()}export{A as component};

View File

@@ -0,0 +1 @@
import{g as s}from"../chunks/DBDqKY8A.js";import{i}from"../chunks/CrZRXG6z.js";import{c,a as u}from"../chunks/pJd4F_Tq.js";import{z as l,K as f,af as m}from"../chunks/CCV2x70u.js";import{B as p}from"../chunks/BG30BmlR.js";function b(t,a,...e){var n=new p(t);l(()=>{const r=a()??null;n.ensure(r,r&&(o=>r(o,...e)))},f)}const d=async()=>{{let t=!1;if(i.subscribe(e=>{t=e})(),!t)return s("/login"),{authenticated:!1}}return{authenticated:!0}},T=Object.freeze(Object.defineProperty({__proto__:null,load:d},Symbol.toStringTag,{value:"Module"}));function A(t,a){var e=c(),n=m(e);b(n,()=>a.children),u(t,e)}export{A as component,T as universal};

View File

@@ -0,0 +1 @@
import{a as n,f as p}from"../chunks/pJd4F_Tq.js";import{i as m}from"../chunks/BgHfHpED.js";import{p as c,l as f,a as l,b as d}from"../chunks/CCV2x70u.js";import{s as g,a as _}from"../chunks/JkAhLmb1.js";import{g as e}from"../chunks/DBDqKY8A.js";import{i as u}from"../chunks/CrZRXG6z.js";var h=p('<div class="flex items-center justify-center min-h-screen"><div class="loading loading-spinner loading-lg text-primary"></div></div>');function j(s,a){c(a,!1);const t=()=>_(u,"$isAuthenticated",i),[i,o]=g();f(()=>(t(),e),()=>{t()?e("/dashboard"):e("/login")}),l(),m();var r=h();n(s,r),d(),o()}export{j as component};

View File

@@ -0,0 +1,2 @@
import{a as v,f as p}from"../chunks/pJd4F_Tq.js";import{i as k}from"../chunks/BgHfHpED.js";import{p as w,b as T,e as y,$ as A,c as s,s as r,r as t,t as L}from"../chunks/CCV2x70u.js";import{e as R,s as W}from"../chunks/Bx__7-vK.js";import{i as j}from"../chunks/CC5oASRR.js";import{h as F}from"../chunks/DhYTxIvM.js";import{s as H,a as J}from"../chunks/JkAhLmb1.js";import{u as M,l as q}from"../chunks/CrZRXG6z.js";import{g as z}from"../chunks/DBDqKY8A.js";var B=p('<div class="alert alert-info mb-4"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="stroke-current shrink-0 w-6 h-6"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg> <span> </span></div>'),C=p(`<div class="max-w-4xl mx-auto"><div class="card bg-base-100 shadow-xl"><div class="card-body"><h1 class="card-title text-3xl mb-4">Welcome to Headroom! 👋</h1> <!> <p class="text-lg mb-4">You have successfully authenticated. This is a protected dashboard page
that requires a valid JWT token to access.</p> <div class="divider"></div> <h2 class="text-xl font-bold mb-2">Authentication Features Implemented:</h2> <ul class="list-disc list-inside space-y-2 mb-6"><li>✅ JWT Token Authentication</li> <li>✅ Token Auto-refresh on 401</li> <li>✅ Protected Route Guards</li> <li>✅ Form Validation with Zod</li> <li>✅ Role-based Access Control</li> <li>✅ Redis Token Storage</li></ul> <div class="card-actions"><button class="btn btn-error">Logout</button></div></div></div></div>`);function K(u,h){w(h,!1);const o=()=>J(M,"$user",f),[f,b]=H();async function g(){await q(),z("/login")}k();var e=C();F("x1i5gj",a=>{y(()=>{A.title="Dashboard - Headroom"})});var l=s(e),n=s(l),d=r(s(n),2);{var x=a=>{var i=B(),m=r(s(i),2),$=s(m);t(m),t(i),L(()=>W($,`Logged in as ${o().email??""} (${o().role??""})`)),v(a,i)};j(d,a=>{o()&&a(x)})}var c=r(d,10),_=s(c);t(c),t(n),t(l),t(e),R("click",_,g),v(u,e),T(),b()}export{K as component};

File diff suppressed because one or more lines are too long