M1.5 frontend: admin Settings UI + config store + register gating
CI & Build / Python lint (push) Successful in 3s
CI & Build / Python tests (push) Successful in 8s
CI & Build / TypeScript typecheck (push) Successful in 10s
CI & Build / Build & push image (push) Successful in 31s

- config store (public /api/config: site_name, allow_registration, version),
  loaded in the router guard; site name drives the board header.
- session User gains is_admin.
- /settings route with requiresAdmin guard + admin-only gear link in the header;
  SettingsView renders grouped typed fields (text/number/toggle), saves via
  PATCH /api/settings with saved/error feedback, refreshes public config.
- Register link hidden + /register route blocked when registration is closed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-19 18:28:56 -04:00
co-authored by Claude Opus 4.8
parent b46bda38ee
commit 68f7b260de
7 changed files with 208 additions and 3 deletions
+12 -1
View File
@@ -2,6 +2,7 @@
import { computed, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useSessionStore } from "../stores/session";
import { useConfigStore } from "../stores/config";
import { useNotesStore, type Note, type NoteView } from "../stores/notes";
import QuickAdd from "../components/QuickAdd.vue";
import NoteCard from "../components/NoteCard.vue";
@@ -10,6 +11,7 @@ import BaseButton from "../components/BaseButton.vue";
import Icon from "../components/Icon.vue";
const session = useSessionStore();
const config = useConfigStore();
const notes = useNotesStore();
const route = useRoute();
const router = useRouter();
@@ -69,7 +71,7 @@ async function signOut() {
<div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand text-sm font-black text-neutral-900">
TS
</div>
<span class="hidden font-semibold sm:inline">ThoughtSync</span>
<span class="hidden font-semibold sm:inline">{{ config.siteName }}</span>
</div>
<nav class="flex items-center gap-1">
@@ -89,6 +91,15 @@ async function signOut() {
</nav>
<div class="flex items-center gap-3">
<RouterLink
v-if="session.user?.is_admin"
to="/settings"
class="icon-btn"
title="Settings"
aria-label="Settings"
>
<Icon name="settings" />
</RouterLink>
<span class="hidden text-sm text-neutral-500 sm:inline dark:text-neutral-400">{{
session.user?.display_name
}}</span>