浏览代码

Display currencies

Lee Morgan 2 周之前
父节点
当前提交
8284803559

+ 6 - 1
src/routes/currency/+page.server.js

@@ -17,7 +17,12 @@ export async function load() {
 		const groups = new Map();
 		for (const doc of docs) {
 			const name = doc.location?.trim() || 'Unknown';
-			const group = groups.get(name) ?? { name, count: 0, image: '' };
+			const group = groups.get(name) ?? {
+				name,
+				slug: encodeURIComponent(name),
+				count: 0,
+				image: ''
+			};
 			group.count += 1;
 			if (!group.image) group.image = currencyImageSrc(doc.frontImage);
 			groups.set(name, group);

+ 9 - 2
src/routes/currency/+page.svelte

@@ -13,7 +13,7 @@
 		<ul class="places">
 			{#each data.locations as place}
 				<li>
-					<div class="card place">
+					<a class="card place" href={`/currency/${place.slug}`}>
 						{#if place.image}
 							<img src={place.image} alt="" />
 						{/if}
@@ -21,7 +21,7 @@
 							<h2>{place.name}</h2>
 							<p>{place.count} {place.count === 1 ? 'note' : 'notes'}</p>
 						</div>
-					</div>
+					</a>
 				</li>
 			{/each}
 		</ul>
@@ -63,6 +63,13 @@
 		width: 100%;
 		overflow: hidden;
 		padding: 0;
+		color: inherit;
+		text-decoration: none;
+	}
+
+	.place:hover {
+		color: inherit;
+		text-decoration: none;
 	}
 
 	.place img {

+ 43 - 0
src/routes/currency/[location]/+page.server.js

@@ -0,0 +1,43 @@
+import { error } from '@sveltejs/kit';
+import { currencyImageSrc } from '#lib/currency.js';
+import { getDb } from '#lib/server/mongo.js';
+
+export const prerender = false;
+
+export async function load({ params }) {
+	const location = params.location?.trim();
+	if (!location) error(404, 'Location not found');
+
+	const db = await getDb();
+	const docs = await db
+		.collection('currencies')
+		.find({ location })
+		.project({
+			name: 1,
+			value: 1,
+			type: 1,
+			year: 1,
+			comment: 1,
+			frontImage: 1,
+			backImage: 1,
+			currencyCode: 1
+		})
+		.sort({ year: 1, name: 1 })
+		.toArray();
+
+	if (!docs.length) error(404, 'Location not found');
+
+	const notes = docs.map((doc) => ({
+		id: String(doc._id),
+		name: doc.name ?? '',
+		value: doc.value ?? '',
+		type: doc.type ?? '',
+		year: doc.year ?? '',
+		comment: doc.comment ?? '',
+		currencyCode: doc.currencyCode ?? '',
+		frontImage: currencyImageSrc(doc.frontImage),
+		backImage: currencyImageSrc(doc.backImage)
+	}));
+
+	return { location, notes };
+}

+ 119 - 0
src/routes/currency/[location]/+page.svelte

@@ -0,0 +1,119 @@
+<script>
+	let { data } = $props();
+</script>
+
+<svelte:head>
+	<title>{data.location} — Currency — Lee Morgan</title>
+	<meta name="description" content={`Currency from ${data.location}.`} />
+</svelte:head>
+
+<section class="notes-page">
+	<p class="back"><a href="/currency">Currency</a></p>
+	<h1>{data.location}</h1>
+	<ul class="notes">
+		{#each data.notes as note}
+			<li class="card note">
+				<div class="faces">
+					{#if note.frontImage}
+						<a href={`/currency/${encodeURIComponent(data.location)}/${note.id}/front`}>
+							<img src={note.frontImage} alt="Front of {note.name}" />
+						</a>
+					{/if}
+					{#if note.backImage}
+						<a href={`/currency/${encodeURIComponent(data.location)}/${note.id}/back`}>
+							<img src={note.backImage} alt="Back of {note.name}" />
+						</a>
+					{/if}
+				</div>
+				<div class="copy">
+					<h2>{note.name}{#if note.year} <span>{note.year}</span>{/if}</h2>
+					<p class="meta">
+						{#if note.currencyCode}{note.currencyCode}{/if}
+						{#if note.value} · {note.value}{/if}
+						{#if note.type} · {note.type}{/if}
+					</p>
+					{#if note.comment}
+						<p class="comment">{note.comment}</p>
+					{/if}
+				</div>
+			</li>
+		{/each}
+	</ul>
+</section>
+
+<style>
+	.notes-page {
+		max-width: 52rem;
+	}
+
+	.back {
+		margin: 0 0 1rem;
+		font-size: 0.875rem;
+	}
+
+	h1 {
+		font-size: 1.25rem;
+		font-weight: 500;
+		letter-spacing: 0.02em;
+		margin-bottom: 1.5rem;
+	}
+
+	.notes {
+		list-style: none;
+		margin: 0;
+		padding: 0;
+		display: flex;
+		flex-direction: column;
+		gap: 0.85rem;
+	}
+
+	.note {
+		padding: 0;
+		overflow: hidden;
+	}
+
+	.faces {
+		display: grid;
+		grid-template-columns: 1fr 1fr;
+		gap: 1px;
+		background: var(--border);
+	}
+
+	.faces a {
+		display: block;
+		color: inherit;
+	}
+
+	.faces img {
+		display: block;
+		width: 100%;
+		aspect-ratio: 16 / 9;
+		object-fit: cover;
+		background: var(--bg);
+	}
+
+	.copy {
+		padding: 1.15rem 1.4rem 1.3rem;
+	}
+
+	.note h2 {
+		font-size: 1.125rem;
+		font-weight: 600;
+		letter-spacing: 0.02em;
+	}
+
+	.note h2 span {
+		font-weight: 500;
+		color: var(--muted);
+	}
+
+	.meta {
+		margin-top: 0.3rem;
+		font-size: 0.875rem;
+		color: var(--muted);
+	}
+
+	.comment {
+		margin-top: 0.7rem;
+	}
+</style>

+ 31 - 0
src/routes/currency/[location]/[id]/+page.server.js

@@ -0,0 +1,31 @@
+import { error } from '@sveltejs/kit';
+import { ObjectId } from 'mongodb';
+import { currencyImageSrc } from '#lib/currency.js';
+import { getDb } from '#lib/server/mongo.js';
+
+export const prerender = false;
+
+export async function load({ params }) {
+	if (!ObjectId.isValid(params.id)) error(404, 'Currency not found');
+
+	const db = await getDb();
+	const doc = await db.collection('currencies').findOne({
+		_id: new ObjectId(params.id),
+		location: params.location
+	});
+
+	if (!doc) error(404, 'Currency not found');
+
+	return {
+		id: String(doc._id),
+		location: doc.location ?? params.location,
+		name: doc.name ?? '',
+		value: doc.value ?? '',
+		type: doc.type ?? '',
+		year: doc.year ?? '',
+		comment: doc.comment ?? '',
+		currencyCode: doc.currencyCode ?? '',
+		frontImage: currencyImageSrc(doc.frontImage),
+		backImage: currencyImageSrc(doc.backImage)
+	};
+}

+ 111 - 0
src/routes/currency/[location]/[id]/+page.svelte

@@ -0,0 +1,111 @@
+<script>
+	let { data } = $props();
+</script>
+
+<svelte:head>
+	<title>{data.name} — {data.location} — Lee Morgan</title>
+</svelte:head>
+
+<div class="viewer">
+	<a class="btn btn-ghost back" href={`/currency/${encodeURIComponent(data.location)}`}
+		>Back to {data.location}</a
+	>
+	<div class="content">
+		<div class="faces">
+			{#if data.frontImage}
+				<a href={`/currency/${encodeURIComponent(data.location)}/${data.id}/front`}>
+					<img src={data.frontImage} alt="Front of {data.name}" />
+				</a>
+			{/if}
+			{#if data.backImage}
+				<a href={`/currency/${encodeURIComponent(data.location)}/${data.id}/back`}>
+					<img src={data.backImage} alt="Back of {data.name}" />
+				</a>
+			{/if}
+		</div>
+		<h1>{data.name}{#if data.year} <span>{data.year}</span>{/if}</h1>
+		<p class="meta">
+			{#if data.currencyCode}{data.currencyCode}{/if}
+			{#if data.value} · {data.value}{/if}
+			{#if data.type} · {data.type}{/if}
+		</p>
+		{#if data.comment}
+			<p class="comment">{data.comment}</p>
+		{/if}
+	</div>
+</div>
+
+<style>
+	.viewer {
+		position: fixed;
+		inset: 0;
+		z-index: 30;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding: 4.5rem 1.5rem 1.5rem;
+		background: var(--bg);
+		overflow: auto;
+	}
+
+	.back {
+		position: absolute;
+		top: 1.25rem;
+		left: 1.25rem;
+		z-index: 1;
+	}
+
+	.content {
+		width: min(72rem, 100%);
+		margin: auto;
+	}
+
+	.faces {
+		display: grid;
+		grid-template-columns: 1fr;
+		gap: 0.85rem;
+	}
+
+	.faces a {
+		display: block;
+		color: inherit;
+	}
+
+	.faces img {
+		display: block;
+		width: 100%;
+		max-height: 70svh;
+		object-fit: contain;
+		background: var(--surface);
+		border: 1px solid var(--border);
+		border-radius: 10px;
+	}
+
+	h1 {
+		margin-top: 1.25rem;
+		font-size: 1.25rem;
+		font-weight: 600;
+		letter-spacing: 0.02em;
+	}
+
+	h1 span {
+		font-weight: 500;
+		color: var(--muted);
+	}
+
+	.meta {
+		margin-top: 0.35rem;
+		color: var(--muted);
+	}
+
+	.comment {
+		margin-top: 0.75rem;
+		max-width: 40rem;
+	}
+
+	@media (min-width: 800px) {
+		.faces {
+			grid-template-columns: 1fr 1fr;
+		}
+	}
+</style>

+ 31 - 0
src/routes/currency/[location]/[id]/[side]/+page.server.js

@@ -0,0 +1,31 @@
+import { error } from '@sveltejs/kit';
+import { ObjectId } from 'mongodb';
+import { currencyImageSrc } from '#lib/currency.js';
+import { getDb } from '#lib/server/mongo.js';
+
+export const prerender = false;
+
+export async function load({ params }) {
+	if (params.side !== 'front' && params.side !== 'back') error(404, 'Photo not found');
+	if (!ObjectId.isValid(params.id)) error(404, 'Currency not found');
+
+	const db = await getDb();
+	const doc = await db.collection('currencies').findOne({
+		_id: new ObjectId(params.id),
+		location: params.location
+	});
+
+	if (!doc) error(404, 'Currency not found');
+
+	const src =
+		params.side === 'front' ? currencyImageSrc(doc.frontImage) : currencyImageSrc(doc.backImage);
+	if (!src) error(404, 'Photo not found');
+
+	return {
+		id: String(doc._id),
+		location: doc.location ?? params.location,
+		name: doc.name ?? '',
+		side: params.side,
+		src
+	};
+}

+ 40 - 0
src/routes/currency/[location]/[id]/[side]/+page.svelte

@@ -0,0 +1,40 @@
+<script>
+	let { data } = $props();
+</script>
+
+<svelte:head>
+	<title>{data.side === 'front' ? 'Front' : 'Back'} — {data.name} — Lee Morgan</title>
+</svelte:head>
+
+<div class="viewer">
+	<a class="btn btn-ghost back" href={`/currency/${encodeURIComponent(data.location)}`}
+		>Close</a
+	>
+	<img src={data.src} alt="{data.side === 'front' ? 'Front' : 'Back'} of {data.name}" />
+</div>
+
+<style>
+	.viewer {
+		position: fixed;
+		inset: 0;
+		z-index: 30;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		background: var(--bg);
+	}
+
+	.back {
+		position: absolute;
+		top: 1.25rem;
+		left: 1.25rem;
+		z-index: 1;
+	}
+
+	img {
+		display: block;
+		max-width: 100%;
+		max-height: 100%;
+		object-fit: contain;
+	}
+</style>