Separate API token settings tab

This commit is contained in:
Rafael Foster
2026-04-28 20:31:53 -03:00
parent 69897666fb
commit d22f3c1034
17 changed files with 55 additions and 36 deletions
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "التسوق",
"tabCalendar": "التقويم",
"tabFamily": "إدارة العائلة",
"tabApiTokens": "رموز API",
"tabAccount": "الحساب",
"tabsAriaLabel": "أقسام الإعدادات",
"sectionDesign": "التصميم",
+1
View File
@@ -576,6 +576,7 @@
"tabShopping": "Einkauf",
"tabCalendar": "Kalender",
"tabFamily": "Familienverwaltung",
"tabApiTokens": "API-Tokens",
"tabAccount": "Konto",
"tabsAriaLabel": "Einstellungsbereiche",
"sectionDesign": "Design",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Αγορές",
"tabCalendar": "Ημερολόγιο",
"tabFamily": "Διαχείριση οικογένειας",
"tabApiTokens": "API Tokens",
"tabAccount": "Λογαριασμός",
"tabsAriaLabel": "Τμήματα ρυθμίσεων",
"sectionDesign": "Εμφάνιση",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Shopping",
"tabCalendar": "Calendar",
"tabFamily": "Family Management",
"tabApiTokens": "API Tokens",
"tabAccount": "Account",
"tabsAriaLabel": "Settings sections",
"sectionDesign": "Appearance",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Compras",
"tabCalendar": "Calendario",
"tabFamily": "Gestión familiar",
"tabApiTokens": "API Tokens",
"tabAccount": "Cuenta",
"tabsAriaLabel": "Secciones de configuración",
"sectionDesign": "Diseño",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Courses",
"tabCalendar": "Calendrier",
"tabFamily": "Gestion familiale",
"tabApiTokens": "API Tokens",
"tabAccount": "Compte",
"tabsAriaLabel": "Sections des paramètres",
"sectionDesign": "Apparence",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "खरीदारी",
"tabCalendar": "कैलेंडर",
"tabFamily": "परिवार प्रबंधन",
"tabApiTokens": "API टोकन",
"tabAccount": "खाता",
"tabsAriaLabel": "सेटिंग्स अनुभाग",
"sectionDesign": "डिज़ाइन",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Spesa",
"tabCalendar": "Calendario",
"tabFamily": "Gestione famiglia",
"tabApiTokens": "API Tokens",
"tabAccount": "Account",
"tabsAriaLabel": "Sezioni impostazioni",
"sectionDesign": "Aspetto",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "買い物",
"tabCalendar": "カレンダー",
"tabFamily": "家族管理",
"tabApiTokens": "APIトークン",
"tabAccount": "アカウント",
"tabsAriaLabel": "設定カテゴリー",
"sectionDesign": "デザイン",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Compras",
"tabCalendar": "Calendário",
"tabFamily": "Gestão da família",
"tabApiTokens": "API Tokens",
"tabAccount": "Conta",
"tabsAriaLabel": "Seções de configurações",
"sectionDesign": "Design",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Покупки",
"tabCalendar": "Календарь",
"tabFamily": "Управление семьей",
"tabApiTokens": "API-токены",
"tabAccount": "Аккаунт",
"tabsAriaLabel": "Разделы настроек",
"sectionDesign": "Внешний вид",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Inköp",
"tabCalendar": "Kalender",
"tabFamily": "Familjehantering",
"tabApiTokens": "API Tokens",
"tabAccount": "Konto",
"tabsAriaLabel": "Inställningsavsnitt",
"sectionDesign": "Utseende",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Alışveriş",
"tabCalendar": "Takvim",
"tabFamily": "Aile Yönetimi",
"tabApiTokens": "API Tokenları",
"tabAccount": "Hesap",
"tabsAriaLabel": "Ayar bölümleri",
"sectionDesign": "Görünüm",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "Покупки",
"tabCalendar": "Календар",
"tabFamily": "Керування родиною",
"tabApiTokens": "API-токени",
"tabAccount": "Обліковий запис",
"tabsAriaLabel": "Розділи налаштувань",
"sectionDesign": "Зовнішній вигляд",
+1
View File
@@ -551,6 +551,7 @@
"tabShopping": "购物",
"tabCalendar": "日历",
"tabFamily": "家庭管理",
"tabApiTokens": "API 令牌",
"tabAccount": "账户",
"tabsAriaLabel": "设置类别",
"sectionDesign": "外观",
+36 -32
View File
@@ -220,7 +220,7 @@ export async function render(container, { user }) {
const allowedTabs = [
'general', 'meals', 'budget', 'shopping', 'calendar',
...(user?.role === 'admin' ? ['family'] : []),
...(user?.role === 'admin' ? ['family', 'api-tokens'] : []),
'account',
];
const storedTab = sessionStorage.getItem(SETTINGS_TAB_KEY) ?? 'general';
@@ -248,6 +248,7 @@ export async function render(container, { user }) {
<button class="${btnClass('shopping')}" role="tab" data-tab="shopping" aria-selected="${btnAria('shopping')}">${t('settings.tabShopping')}</button>
<button class="${btnClass('calendar')}" role="tab" data-tab="calendar" aria-selected="${btnAria('calendar')}">${t('settings.tabCalendar')}</button>
${user?.role === 'admin' ? `<button class="${btnClass('family')}" role="tab" data-tab="family" aria-selected="${btnAria('family')}">${t('settings.tabFamily')}</button>` : ''}
${user?.role === 'admin' ? `<button class="${btnClass('api-tokens')}" role="tab" data-tab="api-tokens" aria-selected="${btnAria('api-tokens')}">${t('settings.tabApiTokens')}</button>` : ''}
<button class="${btnClass('account')}" role="tab" data-tab="account" aria-selected="${btnAria('account')}">${t('settings.tabAccount')}</button>
</nav>
@@ -573,6 +574,40 @@ export async function render(container, { user }) {
</div>
` : ''}
${user?.role === 'admin' ? `
<!-- Panel: API Tokens -->
<div class="settings-tab-panel" data-panel="api-tokens" role="tabpanel"${panelHidden('api-tokens')}>
<section class="settings-section">
<h2 class="settings-section__title">${t('settings.apiTokensTitle')}</h2>
<div class="settings-card">
<h3 class="settings-card__title">${t('settings.apiTokensCardTitle')}</h3>
<p class="form-hint" style="margin-bottom:var(--space-3)">${t('settings.apiTokensHint')}</p>
<ul class="settings-members" id="api-token-list">
${apiTokens.map(apiTokenHtml).join('')}
</ul>
<form id="api-token-form" class="settings-form" autocomplete="off">
<div class="form-group">
<label class="form-label" for="api-token-name">${t('settings.apiTokenNameLabel')}</label>
<input class="form-input" type="text" id="api-token-name" maxlength="100" required />
</div>
<div class="form-group">
<label class="form-label" for="api-token-expires">${t('settings.apiTokenExpiresLabel')}</label>
<input class="form-input" type="datetime-local" id="api-token-expires" />
<p class="form-hint">${t('settings.apiTokenExpiresHint')}</p>
</div>
<div id="api-token-created" class="settings-token-output" hidden>
<label class="form-label" for="api-token-created-value">${t('settings.apiTokenCreatedLabel')}</label>
<input class="form-input" id="api-token-created-value" type="text" readonly />
<p class="form-hint">${t('settings.apiTokenCreatedHint')}</p>
</div>
<div id="api-token-error" class="form-error" hidden></div>
<button type="submit" class="btn btn--primary">${t('settings.apiTokenCreate')}</button>
</form>
</div>
</section>
</div>
` : ''}
<!-- Panel: Konto -->
<div class="settings-tab-panel" data-panel="account" role="tabpanel"${panelHidden('account')}>
<section class="settings-section">
@@ -628,37 +663,6 @@ export async function render(container, { user }) {
</div>
</section>
${user?.role === 'admin' ? `
<section class="settings-section">
<h2 class="settings-section__title">${t('settings.apiTokensTitle')}</h2>
<div class="settings-card">
<h3 class="settings-card__title">${t('settings.apiTokensCardTitle')}</h3>
<p class="form-hint" style="margin-bottom:var(--space-3)">${t('settings.apiTokensHint')}</p>
<ul class="settings-members" id="api-token-list">
${apiTokens.map(apiTokenHtml).join('')}
</ul>
<form id="api-token-form" class="settings-form" autocomplete="off">
<div class="form-group">
<label class="form-label" for="api-token-name">${t('settings.apiTokenNameLabel')}</label>
<input class="form-input" type="text" id="api-token-name" maxlength="100" required />
</div>
<div class="form-group">
<label class="form-label" for="api-token-expires">${t('settings.apiTokenExpiresLabel')}</label>
<input class="form-input" type="datetime-local" id="api-token-expires" />
<p class="form-hint">${t('settings.apiTokenExpiresHint')}</p>
</div>
<div id="api-token-created" class="settings-token-output" hidden>
<label class="form-label" for="api-token-created-value">${t('settings.apiTokenCreatedLabel')}</label>
<input class="form-input" id="api-token-created-value" type="text" readonly />
<p class="form-hint">${t('settings.apiTokenCreatedHint')}</p>
</div>
<div id="api-token-error" class="form-error" hidden></div>
<button type="submit" class="btn btn--primary">${t('settings.apiTokenCreate')}</button>
</form>
</div>
</section>
` : ''}
<section class="settings-section">
<button class="btn btn--danger-outline settings-logout-btn" id="logout-btn">${t('settings.logout')}</button>
</section>
+4 -4
View File
@@ -13,10 +13,10 @@
* → bypassCacheUntil (in-memory + Cache API für SW-Restart-Robustheit)
*/
const SHELL_CACHE = 'oikos-shell-v61';
const PAGES_CACHE = 'oikos-pages-v56';
const LOCALES_CACHE = 'oikos-locales-v7';
const ASSETS_CACHE = 'oikos-assets-v56';
const SHELL_CACHE = 'oikos-shell-v62';
const PAGES_CACHE = 'oikos-pages-v57';
const LOCALES_CACHE = 'oikos-locales-v8';
const ASSETS_CACHE = 'oikos-assets-v57';
const BYPASS_CACHE = 'oikos-bypass-flag';
const ALL_CACHES = [SHELL_CACHE, PAGES_CACHE, LOCALES_CACHE, ASSETS_CACHE];