source: Klonkt/src/views/pages/admin-seo.ejs@ 3b4095f

main
Last change on this file since 3b4095f was 3b4095f, checked in by roboburr <roboburr@…>, 2 months ago

feat(seo): og:image light/dark override setting

The auto-generated share card followed the site's default theme (1.2.0); Admin →
SEO now has an explicit override: Auto (follow site theme) / Light / Dark.

  • src/config/database.js — sites.og_theme column (NULL = auto).
  • src/services/OgImageService.js — og_theme wins over theme_override when set.
  • src/routes/og.js — select og_theme for the card route.
  • src/routes/admin-seo.js — save og_theme (validated to light/dark, else auto).
  • src/views/pages/admin-seo.ejs — select under the share-image field.
  • src/services/i18n.js — aseo.og_theme* (nl/en/de).
  • CHANGELOG(.nl/.de).md — under Added.

Closes prutfolio-src-krm.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 8.5 KB
Line 
1<div class="container admin-site-edit-page">
2 <p><a href="/admin" class="btn">&larr; <%= t('aseo.back') %></a></p>
3 <h1><%= t('aseo.title') %></h1>
4 <p class="admin-tagline"><%= t('aseo.tagline_pre') %> <strong><%= site.title %></strong> <%= t('aseo.tagline_post') %></p>
5
6 <% if (typeof success !== 'undefined' && success) { %><div class="audio-flash audio-flash--ok"><%= success %></div><% } %>
7 <% if (typeof error !== 'undefined' && error) { %><div class="audio-flash audio-flash--err"><%= error %></div><% } %>
8
9 <form method="post" action="/admin/seo" class="site-form">
10
11 <fieldset>
12 <legend><%= t('aseo.index_legend') %></legend>
13 <label class="cb">
14 <input type="checkbox" name="robots_index" value="1" <%= site.robots_index ? 'checked' : '' %>>
15 <span><%= t('aseo.index_label') %> <small class="form-hint-inline">— <%= t('aseo.index_hint_pre') %> <code>noindex</code> <%= t('aseo.index_hint_post') %></small></span>
16 </label>
17 </fieldset>
18
19 <fieldset>
20 <legend><%= t('aseo.title_legend') %></legend>
21 <label>
22 <span><%= t('aseo.title_template') %> <small class="form-hint-inline">— <%= t('aseo.title_template_hint_pre') %> <code>{title}</code> <%= t('aseo.title_template_hint_and') %> <code>{site}</code></small></span>
23 <input type="text" name="title_template" value="<%= site.title_template || '{title} — {site}' %>" maxlength="200" placeholder="{title} — {site}">
24 </label>
25 <label>
26 <span><%= t('aseo.default_desc') %> <small class="form-hint-inline">— <%= t('aseo.default_desc_hint') %></small></span>
27 <textarea name="default_description" rows="2" maxlength="500" placeholder="<%= t('aseo.default_desc_ph') %>"><%= site.default_description || '' %></textarea>
28 </label>
29 <label>
30 <span><%= t('aseo.canonical') %> <small class="form-hint-inline">— <%= t('aseo.canonical_hint') %></small></span>
31 <input type="text" name="canonical" value="<%= site.canonical || '' %>" placeholder="https://yourdomain.com" maxlength="200">
32 </label>
33 <label>
34 <span><%= t('aseo.author') %> <small class="form-hint-inline">— <%= t('aseo.author_hint') %></small></span>
35 <input type="text" name="author" value="<%= site.author || '' %>" maxlength="120" placeholder="<%= t('aseo.author_ph') %>">
36 </label>
37 </fieldset>
38
39 <fieldset>
40 <legend><%= t('aseo.social_legend') %></legend>
41 <label>
42 <span><%= t('aseo.og_image') %> <small class="form-hint-inline">— <%= t('aseo.og_image_hint') %></small></span>
43 <input type="text" name="og_image_default" value="<%= site.og_image_default || '' %>" placeholder="https://yourdomain.com/share.jpg" maxlength="500">
44 </label>
45 <label>
46 <span><%= t('aseo.og_theme') %> <small class="form-hint-inline">— <%= t('aseo.og_theme_hint') %></small></span>
47 <select name="og_theme">
48 <option value="" <%= !site.og_theme ? 'selected' : '' %>><%= t('aseo.og_theme_auto') %></option>
49 <option value="light" <%= site.og_theme === 'light' ? 'selected' : '' %>><%= t('aseo.og_theme_light') %></option>
50 <option value="dark" <%= site.og_theme === 'dark' ? 'selected' : '' %>><%= t('aseo.og_theme_dark') %></option>
51 </select>
52 </label>
53 <label>
54 <span><%= t('aseo.og_locale') %> <small class="form-hint-inline">— <%= t('aseo.og_locale_hint_pre') %> <code>nl_NL</code> <%= t('aseo.og_locale_hint_or') %> <code>en_US</code></small></span>
55 <input type="text" name="og_locale" value="<%= site.og_locale || '' %>" placeholder="nl_NL" maxlength="32">
56 </label>
57 <label>
58 <span><%= t('aseo.twitter') %> <small class="form-hint-inline">— <%= t('aseo.twitter_hint') %></small></span>
59 <input type="text" name="twitter" value="<%= site.twitter || '' %>" placeholder="@jouwnaam" maxlength="64">
60 </label>
61 <label>
62 <span><%= t('aseo.fb_app') %> <small class="form-hint-inline">— <%= t('aseo.fb_app_hint') %></small></span>
63 <input type="text" name="facebook_app_id" value="<%= site.facebook_app_id || '' %>" maxlength="64">
64 </label>
65 </fieldset>
66
67 <fieldset>
68 <legend><%= t('aseo.publisher_legend') %></legend>
69 <label>
70 <span><%= t('aseo.type') %></span>
71 <select name="schema_type">
72 <option value="Person" <%= site.schema_type === 'Organization' ? '' : 'selected' %>><%= t('aseo.type_person') %></option>
73 <option value="Organization" <%= site.schema_type === 'Organization' ? 'selected' : '' %>><%= t('aseo.type_org') %></option>
74 </select>
75 </label>
76 <label>
77 <span><%= t('aseo.publisher_name') %> <small class="form-hint-inline">— <%= t('aseo.publisher_name_hint') %></small></span>
78 <input type="text" name="publisher_name" value="<%= site.publisher_name || '' %>" maxlength="200">
79 </label>
80 <label>
81 <span><%= t('aseo.publisher_url') %></span>
82 <input type="text" name="publisher_url" value="<%= site.publisher_url || '' %>" placeholder="https://yourdomain.com" maxlength="200">
83 </label>
84 <label>
85 <span><%= t('aseo.publisher_logo') %></span>
86 <input type="text" name="publisher_logo" value="<%= site.publisher_logo || '' %>" placeholder="https://yourdomain.com/logo.png" maxlength="500">
87 </label>
88 </fieldset>
89
90 <fieldset>
91 <legend><%= t('aseo.verify_legend') %></legend>
92 <label>
93 <span><%= t('aseo.verify_google') %></span>
94 <input type="text" name="google_verification" value="<%= site.google_verification || '' %>" maxlength="200">
95 </label>
96 <label>
97 <span><%= t('aseo.verify_bing') %> <small class="form-hint-inline">— <%= t('aseo.verify_bing_hint') %></small></span>
98 <input type="text" name="bing_verification" value="<%= site.bing_verification || '' %>" maxlength="200">
99 </label>
100 <label>
101 <span><%= t('aseo.verify_pinterest') %> <small class="form-hint-inline">— <%= t('aseo.verify_pinterest_hint') %></small></span>
102 <input type="text" name="pinterest_verification" value="<%= site.pinterest_verification || '' %>" maxlength="200">
103 </label>
104 <label>
105 <span><%= t('aseo.verify_yandex') %></span>
106 <input type="text" name="yandex_verification" value="<%= site.yandex_verification || '' %>" maxlength="200">
107 </label>
108 </fieldset>
109
110 <button type="submit" class="btn btn-primary"><%= t('aseo.save') %></button>
111 </form>
112</div>
113
114<style>
115.admin-site-edit-page { max-width: 760px; margin: 3rem auto; padding: 0 1rem; }
116.admin-site-edit-page h1 { font-family: var(--font-display, serif); font-size: 1.8rem; margin: 0 0 0.4rem; }
117.admin-site-edit-page .admin-tagline { color: var(--ink-muted); margin: 0 0 1.75rem; }
118
119.site-form { display: flex; flex-direction: column; gap: 1.5rem; min-width: 0; }
120/* <fieldset> does not shrink because the UA sets min-inline-size:min-content → mobile overflow.
121 min-width:0 forces it to shrink. */
122.site-form fieldset { min-width: 0; }
123.site-form input, .site-form textarea, .site-form select { min-width: 0; }
124.site-form fieldset {
125 border: 1px solid var(--rule);
126 border-radius: 8px;
127 padding: 1rem 1.25rem;
128 background: var(--paper-2);
129 display: flex;
130 flex-direction: column;
131 gap: 0.85rem;
132}
133.site-form fieldset legend { font-family: var(--font-display, serif); font-size: 1rem; padding: 0 0.4rem; }
134.site-form label { display: flex; flex-direction: column; gap: 0.3rem; font-size: 0.85rem; color: var(--ink-muted, var(--ink-soft)); }
135.site-form label.cb { flex-direction: row; align-items: flex-start; gap: 0.55rem; font-size: 0.9rem; color: var(--ink); }
136.site-form label.cb input { margin-top: 0.15rem; }
137.site-form label > span { line-height: 1.35; }
138.form-hint-inline { font-weight: 400; color: var(--ink-muted); font-size: 0.82em; }
139.site-form code { background: var(--paper); padding: 0.05rem 0.35rem; border-radius: 3px; font-size: 0.85em; }
140.site-form input[type="text"], .site-form textarea, .site-form select {
141 padding: 0.55rem 0.85rem;
142 border: 1px solid var(--rule);
143 border-radius: 5px;
144 background: var(--paper);
145 color: var(--ink);
146 font-family: inherit;
147 font-size: 0.95rem;
148 width: 100%;
149 box-sizing: border-box;
150}
151.site-form textarea { resize: vertical; min-height: 2.6rem; }
152.site-form select { max-width: 320px; }
153.site-form input:focus, .site-form textarea:focus, .site-form select:focus {
154 outline: none; border-color: var(--accent);
155}
156.seo-pointer p { margin: 0; font-size: 0.9rem; color: var(--ink-muted); }
157.seo-pointer a { color: var(--accent); font-weight: 600; }
158.site-form > button[type="submit"] { align-self: flex-start; }
159</style>
Note: See TracBrowser for help on using the repository browser.