11<script lang =" ts" >
2- import { Lock , ExternalLink , Link , Download , Search , X } from ' @lucide/svelte' ;
3- import relativeDate from ' tiny-relative-date' ;
2+ import EndMessage from ' $lib/components/EndMessage.svelte' ;
3+ import { Lock , ExternalLink , Link , Download , Search , X } from ' @lucide/svelte' ;
4+ import relativeDate from ' tiny-relative-date' ;
45
56 export let data: PageData ;
67
78 const { projects, s3PublicUrl } = data ;
89
910 // ── Filter definitions ──────────────────────────────────────────
1011 const FILTERS = [
11- { key: ' all' , label: ' All' },
12- { key: ' building' , label: ' Building' },
13- { key: ' submitted' , label: ' Submitted' },
12+ { key: ' all' , label: ' All' },
13+ { key: ' building' , label: ' Building' },
14+ { key: ' submitted' , label: ' Submitted' },
1415 { key: ' t1_approved' , label: ' Approved / Queue' },
15- { key: ' printing' , label: ' Being Printed' },
16- { key: ' printed' , label: ' Printed' },
17- { key: ' finalized' , label: ' Finalized' },
16+ { key: ' printing' , label: ' Being Printed' },
17+ { key: ' printed' , label: ' Printed' },
18+ { key: ' finalized' , label: ' Finalized' },
19+ { key: ' rejected' , label: ' Rejected' },
20+ { key: ' rejected_locked' , label: ' Rejected (locked)' }
1821 ] as const ;
1922
2023 type FilterKey = (typeof FILTERS )[number ][' key' ];
@@ -36,17 +39,23 @@ import relativeDate from 'tiny-relative-date';
3639
3740 // ── Helpers (kept from original page) ──────────────────────────
3841 const isLocked = (project : (typeof projects )[0 ]) =>
39- [' printed' , ' finalized' , ' printing' , ' submitted' , ' t1_approved' ].includes (project .status );
42+ [' printed' , ' finalized' , ' printing' , ' submitted' , ' t1_approved' , ' rejected_locked' ].includes (
43+ project .status
44+ );
4045
4146 function formatStatus(status : string ) {
42- return {
43- building: ' Building' ,
44- submitted: ' Submitted' ,
45- t1_approved: ' On print queue' ,
46- printing: ' Being printed' ,
47- printed: ' Printed' ,
48- finalized: ' Finalized' ,
49- }[status ] ?? status ;
47+ return (
48+ {
49+ building : ' Building' ,
50+ submitted : ' Submitted' ,
51+ t1_approved : ' On print queue' ,
52+ printing : ' Being printed' ,
53+ printed : ' Printed' ,
54+ finalized : ' Finalized' ,
55+ rejected : ' Rejected' ,
56+ rejected_locked : ' Rejected (locked)'
57+ }[status ] ?? status
58+ );
5059 }
5160
5261 function formatTime(minutes : number | string ) {
@@ -61,21 +70,23 @@ import relativeDate from 'tiny-relative-date';
6170
6271<div class =" flex h-full flex-col" >
6372 <!-- ── Header ──────────────────────────────────────────────────── -->
64- <div class =" flex justify-between items-center" >
73+ <div class =" flex items-center justify-between " >
6574 <div >
6675 <h1 class =" mt-5 mb-2 font-hero text-3xl font-medium" >Projects</h1 >
67- <h2 class =" text-md text-[#72685e] font-medium " >
76+ <h2 class =" text-md font-medium text-[#72685e]" >
6877 {data .totalHours }h total ∙ {data .finalHours }h finalized
6978 </h2 >
7079 </div >
7180 <a
7281 href =" /dashboard/projects/create"
73- class =" offset block button md lg:inline-block text-center bg-primary-800 hover:ring -primary-50 hover:ring-2 hover:bg -primary-700 -mt-14 "
82+ class =" offset button md -mt-14 block bg-primary-800 text-center hover:bg -primary-700 hover:ring-2 hover:ring -primary-50 lg:inline-block "
7483 >
7584 Create project
7685 </a >
7786 </div >
7887
88+ <EndMessage />
89+
7990 <!-- ── Search bar — uses themed-input from app.css ──────── -->
8091 <div class =" relative mt-2 flex items-center" >
8192 <Search size ={15 } class =" pointer-events-none absolute left-3 text-primary-700" />
@@ -85,7 +96,7 @@ import relativeDate from 'tiny-relative-date';
8596 placeholder =" Search projects..."
8697 autocomplete =" off"
8798 spellcheck =" false"
88- class =" themed-input w-full py-2.5 pl -9 pr -9 text-sm"
99+ class =" themed-input w-full py-2.5 pr -9 pl -9 text-sm"
89100 />
90101 {#if searchQuery }
91102 <button
@@ -106,8 +117,8 @@ import relativeDate from 'tiny-relative-date';
106117 class ="
107118 rounded-lg border-2 px-2 py-1 text-xs font-semibold transition-colors
108119 {activeFilter === filter.key
109- ? 'border-primary-600 bg-primary-700 text-primary-50'
110- : 'border-primary-900 bg-primary-950 text-primary-700 hover:border-primary-700 hover:bg-primary-900 hover:text-primary-400'}
120+ ? 'border-primary-600 bg-primary-700 text-primary-50'
121+ : 'border-primary-900 bg-primary-950 text-primary-700 hover:border-primary-700 hover:bg-primary-900 hover:text-primary-400'}
111122 "
112123 >
113124 {filter .label }
@@ -116,23 +127,32 @@ import relativeDate from 'tiny-relative-date';
116127 </div >
117128
118129 <!-- ── Count ─────────────────────────────────────────────────── -->
119- <p class =" mt-2 mb-0 text-sm text-[#72685e] font-medium " >
130+ <p class =" mt-2 mb-0 text-sm font-medium text-[#72685e]" >
120131 {filteredProjects .length } project{filteredProjects .length !== 1 ? ' s' : ' ' }
121132 </p >
122133
123134 <!-- ── Project grid ──────────────────────────────────────────── -->
124135 {#if filteredProjects .length > 0 }
125- <div class =" grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 gap-5 mt-2 " >
136+ <div class =" mt-2 grid grid-cols-1 gap-5 lg:grid-cols-2 2xl:grid-cols-3" >
126137 {#each filteredProjects as project (project .id )}
127- <div class =" themed-box relative flex flex-col p-3 shadow-lg/20 transition-all hover:scale-102" >
138+ <div
139+ class =" themed-box relative flex flex-col p-3 shadow-lg/20 transition-all hover:scale-102"
140+ >
128141 <!-- Clickable overlay -->
129- <a class ="absolute inset-0 z-1" href ="/dashboard/projects/ {project .id }" aria-label =" project" ></a >
142+ <a
143+ class =" absolute inset-0 z-1"
144+ href ="/dashboard/projects/ {project .id }"
145+ aria-label =" project"
146+ ></a >
130147
131148 <!-- Title + lock -->
132149 <h1 class =" flex flex-row gap-1 text-xl font-semibold" >
133150 <span class ="grow truncate" >{project .name }</span >
134151 {#if isLocked (project )}
135- <span title =" This project is currently locked as it has been shipped" class =" relative z-2" >
152+ <span
153+ title =" This project is currently locked as it has been shipped"
154+ class =" relative z-2"
155+ >
136156 <Lock size ={24 } />
137157 </span >
138158 {/if }
@@ -153,13 +173,21 @@ import relativeDate from 'tiny-relative-date';
153173 {/if }
154174 {#if project .editorFileType === ' upload' && project .uploadedFileUrl }
155175 <div class =" flex" >
156- <a class ="button sm primary relative z-2" href =" {s3PublicUrl }/ {project .uploadedFileUrl }" target =" _blank" >
176+ <a
177+ class =" button sm primary relative z-2"
178+ href =" {s3PublicUrl }/ {project .uploadedFileUrl }"
179+ target =" _blank"
180+ >
157181 <Download size ={20 } /> Project file
158182 </a >
159183 </div >
160184 {:else if project .editorFileType === ' url' && project .editorUrl }
161185 <div class =" flex" >
162- <a class ="button sm primary relative z-2" href ={project .editorUrl } target =" _blank" >
186+ <a
187+ class =" button sm primary relative z-2"
188+ href ={project .editorUrl }
189+ target =" _blank"
190+ >
163191 <Link size ={20 } /> Project link
164192 </a >
165193 </div >
@@ -171,8 +199,9 @@ import relativeDate from 'tiny-relative-date';
171199 <div class =" flex flex-row gap-4" >
172200 <p class =" grow text-sm" >
173201 Created <abbr title ={project .createdAt .toUTCString ()} class =" relative z-2" >
174- {relativeDate (project .createdAt )}
175- </abbr > ∙ {formatTime (project .timeSpent ?? 0 )}
202+ {relativeDate (project .createdAt )}
203+ </abbr >
204+ ∙ {formatTime (project .timeSpent ?? 0 )}
176205 </p >
177206 <p class ="text-sm" >{formatStatus (project .status )}</p >
178207 </div >
@@ -194,7 +223,10 @@ import relativeDate from 'tiny-relative-date';
194223 {/if }
195224 </p >
196225 <button
197- on:click ={() => { searchQuery = ' ' ; activeFilter = ' all' ; }}
226+ on:click ={() => {
227+ searchQuery = ' ' ;
228+ activeFilter = ' all' ;
229+ }}
198230 class =" button sm mt-2 bg-primary-800 hover:bg-primary-700"
199231 >
200232 Clear filters
0 commit comments