source: Klonkt/src/views/pages/account.ejs@ 00550c0

main
Last change on this file since 00550c0 was 7bc636b, checked in by Robin <robin@…>, 4 months ago

Initial commit — PrutFolio v1 source (pulled from Hetzner /srv/prutfolio)

  • Property mode set to 100644
File size: 9.6 KB
Line 
1<div class="container ax-page">
2
3 <header class="ax-header">
4 <a href="/" class="ax-back" aria-label="Terug naar home">←</a>
5 <div>
6 <h1>Account</h1>
7 <p class="ax-tagline">Profiel en wachtwoord.</p>
8 </div>
9 </header>
10
11 <% if (success) { %><div class="ax-flash ax-flash-ok"><%= success %></div><% } %>
12 <% if (error) { %><div class="ax-flash ax-flash-err"><%= error %></div><% } %>
13
14 <%# ── PROFILE ────────────────────────────────────────────── %>
15 <section class="ax-card">
16 <div class="ax-card-title">Profiel</div>
17
18 <div class="ax-profile-id">
19 <div class="ax-profile-avatar">
20 <% if (account.avatar_url) { %>
21 <img src="<%= account.avatar_url %>" alt="">
22 <% } else { %>
23 <span><%= account.username.charAt(0).toUpperCase() %></span>
24 <% } %>
25 </div>
26 <div class="ax-profile-meta">
27 <div class="ax-profile-name"><%= account.username %></div>
28 <div class="ax-profile-role"><%= account.role %> · <%= account.email %></div>
29 <% if (account.created_at) { %>
30 <div class="ax-profile-joined">Lid sinds <%= formatDate(account.created_at) %></div>
31 <% } %>
32 </div>
33 </div>
34
35 <form action="/account/avatar" method="post" enctype="multipart/form-data" class="ax-form">
36 <label class="ax-field ax-file">
37 <span>Avatar uploaden <small>(jpg/png/webp/gif, max 5 MB)</small></span>
38 <span class="ax-file-control">
39 <span class="ax-btn ax-btn-secondary ax-file-btn">📷 Kies bestand</span>
40 <span class="ax-file-name" data-empty>Geen bestand gekozen</span>
41 <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp,image/gif" required>
42 </span>
43 </label>
44 <div class="ax-form-actions">
45 <button type="submit" class="ax-btn ax-btn-primary">⬆ Uploaden</button>
46 <% if (account.avatar_url) { %>
47 <%# Separate <form> block kept inline since it submits to a different endpoint. %>
48 <button type="button" class="ax-btn" onclick="document.getElementById('avatar-remove-form').submit()">Avatar verwijderen</button>
49 <% } %>
50 </div>
51 </form>
52 <% if (account.avatar_url) { %>
53 <form action="/account/avatar/remove" method="post" id="avatar-remove-form" hidden></form>
54 <% } %>
55
56 <form action="/account/profile" method="post" class="ax-form ax-form-bio">
57 <label class="ax-field">
58 <span>Bio</span>
59 <textarea name="bio" rows="3" maxlength="500" placeholder="Een korte regel over jezelf"><%= account.bio || '' %></textarea>
60 </label>
61 <div class="ax-form-actions">
62 <button type="submit" class="ax-btn ax-btn-primary">💾 Bio opslaan</button>
63 </div>
64 </form>
65 </section>
66
67 <%# ── PASSWORD ──────────────────────────────────────────── %>
68 <section class="ax-card">
69 <div class="ax-card-title">Wachtwoord wijzigen</div>
70 <form action="/account/password" method="post" class="ax-form">
71 <label class="ax-field">
72 <span>Huidig wachtwoord</span>
73 <input type="password" name="current" required autocomplete="current-password">
74 </label>
75 <div class="ax-row ax-row-2">
76 <label class="ax-field">
77 <span>Nieuw wachtwoord <small>(min 8 tekens)</small></span>
78 <input type="password" name="new_password" required minlength="8" autocomplete="new-password">
79 </label>
80 <label class="ax-field">
81 <span>Bevestig nieuw wachtwoord</span>
82 <input type="password" name="confirm" required minlength="8" autocomplete="new-password">
83 </label>
84 </div>
85 <div class="ax-form-actions">
86 <button type="submit" class="ax-btn ax-btn-primary">🔒 Wachtwoord wijzigen</button>
87 </div>
88 </form>
89 </section>
90
91</div>
92
93<style>
94.ax-page { max-width: 720px; margin: 1.5rem auto 4rem; padding: 0 1rem; }
95.ax-header { display: flex; align-items: flex-start; gap: 0.75rem; margin-bottom: 1.5rem; }
96.ax-header h1 { font-family: var(--font-display, serif); font-size: 1.75rem; margin: 0 0 0.25rem; }
97.ax-tagline { color: var(--ink-muted, var(--ink-soft)); margin: 0; font-size: 0.9rem; }
98.ax-back {
99 display: inline-flex; align-items: center; justify-content: center;
100 width: 40px; height: 40px;
101 border: 1px solid var(--rule); border-radius: 8px;
102 background: var(--paper); color: var(--ink); text-decoration: none;
103 font-size: 1.1rem; flex-shrink: 0; transition: border-color 120ms;
104}
105.ax-back:hover { border-color: var(--accent); }
106
107.ax-flash { padding: 0.75rem 1rem; border-radius: 8px; margin-bottom: 1rem; font-size: 0.9rem; }
108.ax-flash-ok { background: rgba(40,160,90,.15); color: #2a9d5e; border: 1px solid rgba(40,160,90,.3); }
109.ax-flash-err { background: rgba(200,60,60,.15); color: #c33; border: 1px solid rgba(200,60,60,.3); }
110
111/* ─── Cards ────────────────────────────────────────────────────── */
112.ax-card {
113 background: var(--paper);
114 border: 1px solid var(--rule);
115 border-radius: 12px;
116 padding: 1.25rem;
117 margin-bottom: 1rem;
118}
119.ax-card-title {
120 font-family: var(--font-display, serif);
121 font-size: 1.15rem;
122 font-weight: 600;
123 margin-bottom: 1rem;
124}
125
126/* ─── Form fields ──────────────────────────────────────────────── */
127.ax-form { display: flex; flex-direction: column; gap: 0.85rem; }
128.ax-form-bio { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--rule); }
129.ax-row { display: grid; gap: 0.85rem; }
130.ax-row-2 { grid-template-columns: 1fr 1fr; }
131@media (max-width: 600px) { .ax-row-2 { grid-template-columns: 1fr; } }
132
133.ax-field { display: flex; flex-direction: column; gap: 0.35rem; min-width: 0; }
134.ax-field > span {
135 font-size: 0.8rem; font-weight: 600;
136 color: var(--ink-soft);
137 display: flex; align-items: baseline; gap: 0.4rem; flex-wrap: wrap;
138}
139.ax-field > span small { font-weight: 400; color: var(--ink-muted); font-size: 0.95em; }
140.ax-field input[type="text"],
141.ax-field input[type="email"],
142.ax-field input[type="password"],
143.ax-field input[type="url"],
144.ax-field select,
145.ax-field textarea {
146 width: 100%;
147 box-sizing: border-box;
148 padding: 0.6rem 0.75rem;
149 border: 1px solid var(--rule);
150 border-radius: 6px;
151 background: var(--paper-2);
152 color: var(--ink);
153 font-family: var(--font-ui, system-ui), sans-serif;
154 font-size: 0.95rem;
155 -webkit-appearance: none; appearance: none;
156 transition: border-color 120ms;
157}
158.ax-field textarea { resize: vertical; min-height: 70px; font-family: inherit; }
159.ax-field input:focus, .ax-field select:focus, .ax-field textarea:focus {
160 outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
161}
162
163/* ─── File upload ─────────────────────────────────────────────── */
164.ax-file-control {
165 display: flex; align-items: center; gap: 0.5rem;
166 flex-wrap: wrap;
167 position: relative;
168 min-height: 40px;
169}
170.ax-file-control input[type="file"] {
171 position: absolute; inset: 0;
172 opacity: 0; cursor: pointer;
173 z-index: 2;
174}
175.ax-file-btn { pointer-events: none; }
176.ax-file-name {
177 font-size: 0.85rem;
178 color: var(--ink);
179 word-break: break-all;
180 flex: 1; min-width: 0;
181}
182.ax-file-name[data-empty] { color: var(--ink-muted, var(--ink-soft)); }
183
184.ax-form-actions { display: flex; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.25rem; }
185
186/* ─── Buttons ──────────────────────────────────────────────────── */
187.ax-btn {
188 display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
189 padding: 0.55rem 1rem;
190 border: 1px solid var(--rule); background: var(--paper-2);
191 color: var(--ink);
192 font-family: var(--font-ui, system-ui), sans-serif;
193 font-size: 0.9rem; font-weight: 500;
194 text-decoration: none; border-radius: 6px;
195 cursor: pointer; min-height: 40px;
196 transition: background 120ms, border-color 120ms;
197 -webkit-tap-highlight-color: transparent;
198}
199.ax-btn:hover { border-color: var(--accent); }
200.ax-btn-secondary { background: var(--paper-2); }
201.ax-btn-primary { background: var(--accent); color: white; border-color: var(--accent); }
202.ax-btn-primary:hover { opacity: 0.92; border-color: var(--accent); }
203
204/* ─── Profile ID row ──────────────────────────────────────────── */
205.ax-profile-id {
206 display: flex; align-items: center; gap: 1rem;
207 padding-bottom: 1rem;
208 border-bottom: 1px solid var(--rule);
209 margin-bottom: 1rem;
210}
211.ax-profile-avatar {
212 width: 64px; height: 64px;
213 border-radius: 50%;
214 overflow: hidden;
215 background: var(--paper-2); /* neutral so transparent avatars blend */
216 color: var(--accent); /* accent only for the fallback initial */
217 display: inline-flex; align-items: center; justify-content: center;
218 font-family: var(--font-display, serif);
219 font-size: 1.6rem; font-weight: 600;
220 flex-shrink: 0;
221}
222.ax-profile-avatar img { width: 100%; height: 100%; object-fit: cover; }
223.ax-profile-meta { min-width: 0; }
224.ax-profile-name { font-weight: 600; font-size: 1.1rem; }
225.ax-profile-role,
226.ax-profile-joined {
227 color: var(--ink-muted, var(--ink-soft));
228 font-size: 0.85rem;
229}
230
231@media (max-width: 480px) {
232 .ax-profile-id { flex-direction: column; align-items: flex-start; gap: 0.75rem; text-align: left; }
233}
234</style>
Note: See TracBrowser for help on using the repository browser.