chore: small adjustments to upload behavior 3a1c2bd3
Steve · 2026-01-25 17:08 2 file(s) · +66 −118
src/routes/admin/+page.server.ts +1 −1
133 133
				)
134 134
				.run();
135 135
136 -
			throw redirect(302, `/photo/${slug}`);
136 +
			return { success: true };
137 137
		} catch (err) {
138 138
			if (isRedirect(err)) {
139 139
				throw err; // Re-throw redirects
src/routes/admin/+page.svelte +65 −117
131 131
		editingId = null;
132 132
		editingTitle = "";
133 133
	}
134 +
135 +
	function resetUploadForm() {
136 +
		selectedFile = null;
137 +
		if (previewUrl) {
138 +
			URL.revokeObjectURL(previewUrl);
139 +
			previewUrl = null;
140 +
		}
141 +
		thumbnailBlob = null;
142 +
		title = "";
143 +
		date = "";
144 +
		camera = "";
145 +
		lens = "";
146 +
		aperture = "";
147 +
		exposure = "";
148 +
		focalLength = "";
149 +
		iso = "";
150 +
		make = "";
151 +
		if (fileInput) {
152 +
			fileInput.value = "";
153 +
		}
154 +
	}
134 155
</script>
135 156
136 157
<svelte:head>
138 159
</svelte:head>
139 160
140 161
<div class="min-h-screen p-4 md:p-8">
141 -
	<div class="max-w-4xl mx-auto">
162 +
	<div class="max-w-4xl mx-auto space-y-4">
142 163
		<div class="flex items-center justify-between mb-8">
143 164
			<h1 class="text-2xl font-bold">Admin</h1>
144 165
			<a
151 172
152 173
		<!-- Upload Section -->
153 174
		<section>
154 -
			<h2 class="text-xl font-semibold mb-4">Upload New Photo</h2>
175 +
			<h2 class="text-lg font-semibold mb-3">Upload New Photo</h2>
155 176
156 177
			<form
157 178
				method="POST"
162 183
					if (thumbnailBlob) {
163 184
						formData.append("thumbnail", thumbnailBlob, "thumbnail.jpg");
164 185
					}
165 -
					return async ({ update }) => {
186 +
					return async ({ result, update }) => {
166 187
						isLoading = false;
188 +
						if (result.type === "success") {
189 +
							resetUploadForm();
190 +
						}
167 191
						await update();
168 192
					};
169 193
				}}
170 -
				class="space-y-6"
194 +
				class="space-y-4"
171 195
			>
172 196
				<div>
173 -
					<label for="file" class="block text-sm mb-2">Image</label>
174 197
					<input
175 198
						type="file"
176 199
						id="file"
179 202
						required
180 203
						bind:this={fileInput}
181 204
						onchange={handleFileSelect}
182 -
						class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500 file:mr-4 file:py-1 file:px-4 file:rounded file:border-0 file:bg-zinc-700 file:text-white file:cursor-pointer"
205 +
						class="w-full text-sm bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500 file:mr-3 file:py-1.5 file:px-3 file:rounded file:border-0 file:bg-zinc-700 file:text-white file:text-sm file:cursor-pointer"
183 206
					/>
184 207
				</div>
185 208
186 209
				{#if previewUrl}
187 -
					<div class="relative">
210 +
					<div class="flex gap-4">
188 211
						<img
189 212
							src={previewUrl}
190 213
							alt="Preview"
191 -
							class="w-full max-h-64 object-contain rounded bg-zinc-900"
214 +
							class="w-32 h-32 object-cover rounded bg-zinc-900 shrink-0"
192 215
						/>
193 -
					</div>
194 -
				{/if}
195 -
196 -
				<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
197 -
					<div>
198 -
						<label for="title" class="block text-sm mb-2">Title *</label>
199 -
						<input
200 -
							type="text"
201 -
							id="title"
202 -
							name="title"
203 -
							required
204 -
							bind:value={title}
205 -
							class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
206 -
						/>
207 -
					</div>
208 -
209 -
					<div>
210 -
						<label for="date" class="block text-sm mb-2">Date *</label>
211 -
						<input
212 -
							type="date"
213 -
							id="date"
214 -
							name="date"
215 -
							required
216 -
							bind:value={date}
217 -
							class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
218 -
						/>
219 -
					</div>
220 -
				</div>
221 -
222 -
				<div class="border-t border-zinc-800 pt-6">
223 -
					<h3 class="text-lg font-medium mb-4">EXIF Data</h3>
224 -
225 -
					<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
226 -
						<div>
227 -
							<label for="make" class="block text-sm mb-2">Make</label>
228 -
							<input
229 -
								type="text"
230 -
								id="make"
231 -
								name="make"
232 -
								bind:value={make}
233 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
234 -
							/>
235 -
						</div>
236 -
237 -
						<div>
238 -
							<label for="camera" class="block text-sm mb-2">Camera</label>
239 -
							<input
240 -
								type="text"
241 -
								id="camera"
242 -
								name="camera"
243 -
								bind:value={camera}
244 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
245 -
							/>
246 -
						</div>
216 +
						<div class="flex-1 space-y-3">
217 +
							<div>
218 +
								<label for="title" class="block text-xs text-zinc-400 mb-1">Title</label>
219 +
								<input
220 +
									type="text"
221 +
									id="title"
222 +
									name="title"
223 +
									required
224 +
									bind:value={title}
225 +
									class="w-full px-3 py-1.5 text-sm bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
226 +
								/>
227 +
							</div>
247 228
248 -
						<div>
249 -
							<label for="lens" class="block text-sm mb-2">Lens</label>
250 -
							<input
251 -
								type="text"
252 -
								id="lens"
253 -
								name="lens"
254 -
								bind:value={lens}
255 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
256 -
							/>
257 -
						</div>
229 +
							{#if date || camera || lens}
230 +
								<div class="text-xs text-zinc-500 space-y-0.5">
231 +
									{#if date}<p>{date}</p>{/if}
232 +
									{#if make || camera}<p>{[make, camera].filter(Boolean).join(" ")}</p>{/if}
233 +
									{#if lens}<p>{lens}</p>{/if}
234 +
									{#if aperture || exposure || focalLength || iso}
235 +
										<p>{[aperture, exposure, focalLength, iso ? `ISO ${iso}` : ""].filter(Boolean).join(" | ")}</p>
236 +
									{/if}
237 +
								</div>
238 +
							{/if}
258 239
259 -
						<div>
260 -
							<label for="aperture" class="block text-sm mb-2">Aperture</label>
261 -
							<input
262 -
								type="text"
263 -
								id="aperture"
264 -
								name="aperture"
265 -
								bind:value={aperture}
266 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
267 -
							/>
268 -
						</div>
269 -
270 -
						<div>
271 -
							<label for="exposure" class="block text-sm mb-2">Exposure</label>
272 -
							<input
273 -
								type="text"
274 -
								id="exposure"
275 -
								name="exposure"
276 -
								bind:value={exposure}
277 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
278 -
							/>
279 -
						</div>
280 -
281 -
						<div>
282 -
							<label for="focalLength" class="block text-sm mb-2">Focal Length</label>
283 -
							<input
284 -
								type="text"
285 -
								id="focalLength"
286 -
								name="focalLength"
287 -
								bind:value={focalLength}
288 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
289 -
							/>
290 -
						</div>
291 -
292 -
						<div>
293 -
							<label for="iso" class="block text-sm mb-2">ISO</label>
294 -
							<input
295 -
								type="text"
296 -
								id="iso"
297 -
								name="iso"
298 -
								bind:value={iso}
299 -
								class="w-full px-4 py-2 bg-zinc-900 border border-zinc-700 rounded focus:outline-none focus:border-zinc-500"
300 -
							/>
240 +
							<!-- Hidden inputs for form submission -->
241 +
							<input type="hidden" name="date" value={date} />
242 +
							<input type="hidden" name="make" value={make} />
243 +
							<input type="hidden" name="camera" value={camera} />
244 +
							<input type="hidden" name="lens" value={lens} />
245 +
							<input type="hidden" name="aperture" value={aperture} />
246 +
							<input type="hidden" name="exposure" value={exposure} />
247 +
							<input type="hidden" name="focalLength" value={focalLength} />
248 +
							<input type="hidden" name="iso" value={iso} />
301 249
						</div>
302 250
					</div>
303 -
				</div>
251 +
				{/if}
304 252
305 253
				{#if form?.error}
306 -
					<p class="text-red-500 text-sm">{form.error}</p>
254 +
					<p class="text-red-500 text-xs">{form.error}</p>
307 255
				{/if}
308 256
309 257
				<button
310 258
					type="submit"
311 259
					disabled={isLoading || !selectedFile}
312 -
					class="w-full py-3 bg-white text-black font-medium rounded hover:bg-zinc-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
260 +
					class="w-full py-2 text-sm bg-white text-black font-medium rounded hover:bg-zinc-200 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
313 261
				>
314 262
					{isLoading ? "Uploading..." : "Upload Photo"}
315 263
				</button>