Преглед изворни кода

Display user workouts on their page

Lee Morgan пре 4 дана
родитељ
комит
e2f455bc00
3 измењених фајлова са 145 додато и 2 уклоњено
  1. 35 0
      src/lib/server/workouts.js
  2. 3 1
      src/routes/workout/+page.server.js
  3. 107 1
      src/routes/workout/+page.svelte

+ 35 - 0
src/lib/server/workouts.js

@@ -98,3 +98,38 @@ export async function saveWorkout(db, userId, input) {
 		exercises: validated.exercises
 	});
 }
+
+/**
+ * @typedef {object} WorkoutListItem
+ * @property {string} id
+ * @property {string} title
+ * @property {Array<{ name: string, type: string }>} exercises
+ */
+
+/**
+ * List all workouts owned by a user.
+ * @param {import('mongodb').Db} db
+ * @param {string} userId
+ * @returns {Promise<WorkoutListItem[]>}
+ */
+export async function listWorkoutsForUser(db, userId) {
+	const ownerId = String(userId ?? '').trim();
+	if (!ownerId) return [];
+
+	const docs = await db
+		.collection(WORKOUTS)
+		.find({ userId: ownerId })
+		.sort({ _id: -1 })
+		.toArray();
+
+	return docs.map((doc) => ({
+		id: String(doc._id),
+		title: String(doc.title ?? ''),
+		exercises: Array.isArray(doc.exercises)
+			? doc.exercises.map((exercise) => ({
+					name: String(exercise?.name ?? ''),
+					type: String(exercise?.type ?? '')
+				}))
+			: []
+	}));
+}

+ 3 - 1
src/routes/workout/+page.server.js

@@ -1,7 +1,9 @@
 import { requireUser } from '$lib/server/auth.js';
+import { listWorkoutsForUser } from '$lib/server/workouts.js';
 
 /** @type {import('./$types').PageServerLoad} */
 export async function load(event) {
 	const user = await requireUser(event);
-	return { user };
+	const workouts = await listWorkoutsForUser(event.locals.db, user.id);
+	return { user, workouts };
 }

+ 107 - 1
src/routes/workout/+page.svelte

@@ -22,10 +22,25 @@
 			<div class="panel-head">
 				<div class="panel-copy">
 					<h1>Workouts</h1>
-					<p class="lede">Signed in as {data.user.name}. Your sessions will show up here.</p>
+					<p class="lede">Signed in as {data.user.name}.</p>
 				</div>
 				<a class="btn btn-primary new-workout" href="/workout/new">New workout</a>
 			</div>
+
+			{#if data.workouts.length === 0}
+				<p class="empty">No workouts yet. Create one to get started.</p>
+			{:else}
+				<ul class="workout-list" role="list">
+					{#each data.workouts as workout (workout.id)}
+						<li class="workout-item">
+							<a class="workout-btn" href="/workout/{workout.id}">
+								<span class="workout-btn-label">{workout.title}</span>
+								<span class="workout-btn-arrow" aria-hidden="true">→</span>
+							</a>
+						</li>
+					{/each}
+				</ul>
+			{/if}
 		</section>
 	</main>
 </div>
@@ -84,6 +99,7 @@
 		flex-direction: column;
 		align-items: flex-start;
 		gap: 16px;
+		margin-bottom: 20px;
 	}
 
 	.panel-copy {
@@ -108,6 +124,96 @@
 		line-height: 1.45;
 	}
 
+	.empty {
+		margin: 0;
+		padding: 18px;
+		border-radius: var(--radius);
+		border: 1px dashed var(--border);
+		background: var(--surface-2);
+		color: var(--text-muted);
+		line-height: 1.45;
+	}
+
+	.workout-list {
+		list-style: none;
+		margin: 0;
+		padding: 0;
+		display: flex;
+		flex-direction: column;
+		gap: 10px;
+		width: 100%;
+	}
+
+	.workout-item {
+		width: 100%;
+	}
+
+	.workout-btn {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		gap: 16px;
+		box-sizing: border-box;
+		width: 100%;
+		margin: 0;
+		padding: 16px 18px;
+		border: 1px solid var(--graphite);
+		border-radius: 10px;
+		background: var(--plate);
+		color: var(--bone);
+		font: inherit;
+		font-size: 15px;
+		font-weight: 650;
+		line-height: 1.2;
+		text-align: left;
+		text-decoration: none;
+		cursor: pointer;
+	}
+
+	.workout-btn-label {
+		flex: 1;
+		min-width: 0;
+		overflow: hidden;
+		text-overflow: ellipsis;
+		white-space: nowrap;
+		text-align: left;
+	}
+
+	.workout-btn-arrow {
+		flex-shrink: 0;
+		display: inline-flex;
+		align-items: center;
+		justify-content: center;
+		width: 1.25em;
+		font-size: 2.75rem;
+		font-weight: 700;
+		line-height: 1;
+		color: var(--ember);
+	}
+
+	.workout-btn:hover {
+		background: var(--steel);
+	}
+
+	.workout-btn:hover .workout-btn-arrow {
+		color: var(--heat);
+	}
+
+	.workout-btn:focus-visible {
+		outline: 2px solid var(--focus);
+		outline-offset: 2px;
+	}
+
+	.workout-btn:active {
+		background: var(--forge);
+		color: var(--ember-ink);
+		border-color: var(--forge);
+	}
+
+	.workout-btn:active .workout-btn-arrow {
+		color: var(--ember-ink);
+	}
+
 	@media (min-width: 720px) {
 		.top {
 			padding: 16px 28px;