colorThemes #1

Merged
curo merged 7 commits from colorThemes into main 2026-04-18 11:05:41 +02:00
4 changed files with 31 additions and 51 deletions
Showing only changes of commit 003fbee20f - Show all commits
+1 -1
View File
@@ -169,7 +169,7 @@ docker compose up --build -d
│ │ └── useTheme.ts ← Theme toggle │ │ └── useTheme.ts ← Theme toggle
│ ├── components/ │ ├── components/
│ │ ├── AppShell.tsx ← Layout: Sidebar + scrollable main │ │ ├── AppShell.tsx ← Layout: Sidebar + scrollable main
│ │ ├── Sidebar.tsx ← Collapsible nav; "Extensions" section auto-populated from /api/plugins │ │ ├── Sidebar.tsx ← Collapsible nav (icons ↔ icons+labels)
│ │ ├── ThemeToggle.tsx ← Light/dark mode toggle │ │ ├── ThemeToggle.tsx ← Light/dark mode toggle
│ │ ├── PluginSchemaForm.tsx ← JSON Schema → React form (boolean/string/number/readOnly) │ │ ├── PluginSchemaForm.tsx ← JSON Schema → React form (boolean/string/number/readOnly)
│ │ └── ui/ ← shadcn/ui components (Button, Input, …) │ │ └── ui/ ← shadcn/ui components (Button, Input, …)
+5 -5
View File
@@ -61,11 +61,11 @@ Cards are rendered dynamically from `GET /api/services` (polled every 30 s via T
- Sections auto-open when navigating to their route - Sections auto-open when navigating to their route
- In collapsed (icons-only) mode, clicking the Apps icon navigates to `/apps` - In collapsed (icons-only) mode, clicking the Apps icon navigates to `/apps`
**Extensions** section (dynamic): **App cards — Extension button:**
- Populated from `GET /api/plugins` (polled via TanStack Query, `retry: false`) - `GET /api/plugins` is queried on the Apps page (already user-filtered by backend)
- Only shown when the user has access to at least one plugin - If an app's `id` matches a plugin `id`, an "Extension" button is shown on that card
- Each entry links to `/settings/plugins/:id` - Button links to `/settings/plugins/:id` alongside the existing admin "Settings" button
- No code changes needed to add future plugin-enabled feature containers - Only users with plugin access see the button (backend filters `GET /api/plugins`)
### Documents page (`/apps/documents`) ### Documents page (`/apps/documents`)
+1 -44
View File
@@ -16,12 +16,11 @@ import {
Users, Users,
UsersRound, UsersRound,
Palette, Palette,
Puzzle,
} from "lucide-react"; } from "lucide-react";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import ThemeToggle from "@/components/ThemeToggle"; import ThemeToggle from "@/components/ThemeToggle";
import { useAuth } from "@/hooks/useAuth"; import { useAuth } from "@/hooks/useAuth";
import { getMe, getPlugins, listCategories } from "@/api/client"; import { getMe, listCategories } from "@/api/client";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
export default function Sidebar() { export default function Sidebar() {
@@ -57,14 +56,6 @@ export default function Sidebar() {
enabled: appsOpen && docsOpen && !!user, enabled: appsOpen && docsOpen && !!user,
}); });
const { data: plugins = [] } = useQuery({
queryKey: ["plugins"],
queryFn: getPlugins,
enabled: !!user,
// Empty array on 404/error — regular users simply see no plugins
retry: false,
});
const navItemClass = (isActive: boolean) => const navItemClass = (isActive: boolean) =>
cn( cn(
"flex items-center rounded-lg transition-colors", "flex items-center rounded-lg transition-colors",
@@ -218,40 +209,6 @@ export default function Sidebar() {
)} )}
</NavLink> </NavLink>
{/* Extensions — visible only when the user has accessible plugins */}
{plugins.length > 0 && (
<div>
{sidebarExpanded ? (
<>
<div className="px-3 py-1.5">
<span className="text-xs font-semibold uppercase tracking-wider text-muted">
Extensions
</span>
</div>
<div className="space-y-0.5">
{plugins.map((plugin) => (
<NavLink
key={plugin.id}
to={`/settings/plugins/${plugin.id}`}
className={({ isActive }) => subItemClass(isActive)}
>
<Puzzle className="h-4 w-4 shrink-0" />
<span className="whitespace-nowrap truncate">{plugin.name}</span>
</NavLink>
))}
</div>
</>
) : (
<NavLink
to={`/settings/plugins/${plugins[0].id}`}
className={({ isActive }) => navItemClass(isActive)}
>
<Puzzle className="h-5 w-5 shrink-0" />
</NavLink>
)}
</div>
)}
{/* Admin — expandable */} {/* Admin — expandable */}
{user?.is_admin && ( {user?.is_admin && (
<div> <div>
+24 -1
View File
@@ -1,6 +1,6 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { getMe, getServices } from "../api/client"; import { getMe, getPlugins, getServices } from "../api/client";
const cardBase: React.CSSProperties = { const cardBase: React.CSSProperties = {
backgroundColor: "rgb(var(--color-surface))", backgroundColor: "rgb(var(--color-surface))",
@@ -34,6 +34,12 @@ export default function AppsPage() {
refetchInterval: 30_000, refetchInterval: 30_000,
refetchIntervalInBackground: true, refetchIntervalInBackground: true,
}); });
const { data: plugins = [] } = useQuery({
queryKey: ["plugins"],
queryFn: getPlugins,
retry: false,
});
const pluginIds = new Set(plugins.map((p) => p.id));
return ( return (
<div style={{ padding: 32, maxWidth: 900, margin: "0 auto" }}> <div style={{ padding: 32, maxWidth: 900, margin: "0 auto" }}>
@@ -93,6 +99,23 @@ export default function AppsPage() {
Settings Settings
</Link> </Link>
)} )}
{pluginIds.has(svc.id) && (
<Link
to={`/settings/plugins/${svc.id}`}
onClick={(e) => e.stopPropagation()}
style={{
padding: "6px 14px",
border: "1px solid #ccc",
borderRadius: 4,
textDecoration: "none",
fontSize: 14,
color: "#333",
}}
title="Extension settings"
>
Extension
</Link>
)}
</div> </div>
</CardWrapper> </CardWrapper>
); );