all repos — weimar @ 8d72190a752e3e1eaff992da40d75e01c6149993

Unnamed repository; edit this file 'description' to name the repository.

web/src/routes/settings/+page.svelte (view raw)

  1<script lang="ts">
  2	import { uploadAvatar } from '$lib/api';
  3
  4	let file = $state<File | null>(null);
  5	let previewUrl = $state('');
  6	let error = $state('');
  7	let success = $state('');
  8	let loading = $state(false);
  9
 10	function handleFileSelect(e: Event) {
 11		const f = (e.target as HTMLInputElement).files?.[0];
 12		if (!f) return;
 13		file = f;
 14		previewUrl = URL.createObjectURL(f);
 15		error = '';
 16		success = '';
 17	}
 18
 19	async function handleSubmit(e: Event) {
 20		e.preventDefault();
 21		if (!file) return;
 22		error = '';
 23		success = '';
 24		loading = true;
 25		try {
 26			const result = await uploadAvatar(file);
 27			success = 'Avatar updated.';
 28			previewUrl = result.avatar_url;
 29			file = null;
 30		} catch (err) {
 31			error = err instanceof Error ? err.message : 'Upload failed';
 32		} finally {
 33			loading = false;
 34		}
 35	}
 36</script>
 37
 38<div class="page-head">
 39	<h1>SETTINGS</h1>
 40	<div class="head-line"></div>
 41</div>
 42
 43<form onsubmit={handleSubmit}>
 44	{#if error}
 45		<p class="error">{error}</p>
 46	{/if}
 47	{#if success}
 48		<p class="success">{success}</p>
 49	{/if}
 50
 51	<div class="avatar-section">
 52		<div class="avatar-preview">
 53			{#if previewUrl}
 54				<img src={previewUrl} alt="Avatar preview" class="avatar-img" />
 55			{:else}
 56				<div class="avatar-placeholder">NO AVATAR</div>
 57			{/if}
 58		</div>
 59
 60		<label>
 61			<span class="label-text">AVATAR IMAGE</span>
 62			<div class="file-wrap">
 63				<input type="file" id="avatar-input" accept="image/*"
 64					onchange={handleFileSelect}
 65					disabled={loading}
 66				/>
 67				<label for="avatar-input" class="file-label">
 68					{file ? file.name : 'CHOOSE IMAGE'}
 69				</label>
 70			</div>
 71		</label>
 72	</div>
 73
 74	<button type="submit" class="btn" disabled={loading || !file}>
 75		{loading ? 'UPLOADING...' : 'UPLOAD AVATAR'}
 76	</button>
 77</form>
 78
 79<style>
 80	.page-head {
 81		margin-bottom: 1.5rem;
 82	}
 83	.page-head h1 {
 84		margin: 0;
 85	}
 86	.head-line {
 87		height: 3px;
 88		width: 3rem;
 89		background: #c62828;
 90		margin-top: 0.4rem;
 91	}
 92
 93	form {
 94		display: flex;
 95		flex-direction: column;
 96		gap: 1rem;
 97		max-width: 400px;
 98	}
 99
100	.error {
101		color: #c62828;
102		font-family: 'Inter', sans-serif;
103		font-size: 0.85rem;
104		font-weight: 600;
105	}
106	.success {
107		color: #2e7d32;
108		font-family: 'Inter', sans-serif;
109		font-size: 0.85rem;
110		font-weight: 600;
111	}
112
113	.avatar-section {
114		display: flex;
115		gap: 1.5rem;
116		align-items: flex-start;
117	}
118	.avatar-preview {
119		width: 100px;
120		height: 100px;
121		background: #1a1a1a;
122		flex-shrink: 0;
123		display: flex;
124		align-items: center;
125		justify-content: center;
126		overflow: hidden;
127	}
128	.avatar-img {
129		width: 100%;
130		height: 100%;
131		object-fit: cover;
132	}
133	.avatar-placeholder {
134		font-family: 'Oswald', sans-serif;
135		font-weight: 500;
136		font-size: 0.7rem;
137		letter-spacing: 0.12em;
138		color: #555;
139	}
140
141	.label-text {
142		font-family: 'Oswald', sans-serif;
143		font-weight: 500;
144		font-size: 0.75rem;
145		letter-spacing: 0.15em;
146		color: #888;
147	}
148	.file-wrap {
149		position: relative;
150		margin-top: 0.25rem;
151	}
152	#avatar-input {
153		position: absolute;
154		width: 1px;
155		height: 1px;
156		opacity: 0;
157		pointer-events: none;
158	}
159	.file-label {
160		display: flex;
161		align-items: center;
162		padding: 0.55rem 0.75rem;
163		border: 2px solid #1a1a1a;
164		font-family: 'Inter', sans-serif;
165		font-size: 0.85rem;
166		font-weight: 500;
167		color: #888;
168		background: #fff;
169		cursor: pointer;
170		transition: border-color 0.15s;
171	}
172	.file-label:hover {
173		border-color: #c62828;
174	}
175
176	.btn {
177		display: inline-flex;
178		align-items: center;
179		justify-content: center;
180		padding: 0.6rem 1.5rem;
181		background: #1a1a1a;
182		color: #fff;
183		border: none;
184		font-family: 'Oswald', sans-serif;
185		font-weight: 500;
186		font-size: 0.85rem;
187		letter-spacing: 0.1em;
188		cursor: pointer;
189		text-transform: uppercase;
190		transition: background 0.15s;
191		align-self: flex-start;
192	}
193	.btn:hover {
194		background: #c62828;
195	}
196	.btn:disabled {
197		opacity: 0.4;
198		cursor: default;
199	}
200</style>