feat(ai): move the assistant to its own "Odin" sidebar page
Promote the AI chat from a dashboard widget to a dedicated page: - new sidebar item "Odin" under the Přehled section, gated on ai.use, → /odin - new /odin route + Odin page (reuses the assistant component, with a light ai.use guard) - remove the widget from the dashboard - rebrand the assistant header "Asistent" → "Odin" and give the thread more height now that it owns the page Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import { LoadingState } from "./ui";
|
|||||||
import Login from "./pages/Login";
|
import Login from "./pages/Login";
|
||||||
import Dashboard from "./pages/Dashboard";
|
import Dashboard from "./pages/Dashboard";
|
||||||
|
|
||||||
|
const Odin = lazy(() => import("./pages/Odin"));
|
||||||
const Users = lazy(() => import("./pages/Users"));
|
const Users = lazy(() => import("./pages/Users"));
|
||||||
const Attendance = lazy(() => import("./pages/Attendance"));
|
const Attendance = lazy(() => import("./pages/Attendance"));
|
||||||
const AttendanceHistory = lazy(() => import("./pages/AttendanceHistory"));
|
const AttendanceHistory = lazy(() => import("./pages/AttendanceHistory"));
|
||||||
@@ -82,6 +83,7 @@ export default function AdminApp() {
|
|||||||
)}
|
)}
|
||||||
<Route element={<AppShell />}>
|
<Route element={<AppShell />}>
|
||||||
<Route index element={<Dashboard />} />
|
<Route index element={<Dashboard />} />
|
||||||
|
<Route path="odin" element={<Odin />} />
|
||||||
<Route path="users" element={<Users />} />
|
<Route path="users" element={<Users />} />
|
||||||
<Route path="attendance" element={<Attendance />} />
|
<Route path="attendance" element={<Attendance />} />
|
||||||
<Route
|
<Route
|
||||||
|
|||||||
@@ -361,7 +361,7 @@ export default function DashAssistant() {
|
|||||||
AI
|
AI
|
||||||
</Box>
|
</Box>
|
||||||
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
|
||||||
Asistent
|
Odin
|
||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
|
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
|
||||||
@@ -393,8 +393,8 @@ export default function DashAssistant() {
|
|||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: 1,
|
gap: 1,
|
||||||
minHeight: 120,
|
minHeight: 200,
|
||||||
maxHeight: 360,
|
maxHeight: 480,
|
||||||
overflowY: "auto",
|
overflowY: "auto",
|
||||||
mb: 1.5,
|
mb: 1.5,
|
||||||
p: 1,
|
p: 1,
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import DashSessions from "../components/dashboard/DashSessions";
|
|||||||
import DashTodayPlan, {
|
import DashTodayPlan, {
|
||||||
type TodayPlan,
|
type TodayPlan,
|
||||||
} from "../components/dashboard/DashTodayPlan";
|
} from "../components/dashboard/DashTodayPlan";
|
||||||
import DashAssistant from "../components/dashboard/DashAssistant";
|
|
||||||
|
|
||||||
const API_BASE = "/api/admin";
|
const API_BASE = "/api/admin";
|
||||||
|
|
||||||
@@ -237,9 +236,6 @@ export default function Dashboard() {
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* AI Assistant widget */}
|
|
||||||
{hasPermission("ai.use") && <DashAssistant />}
|
|
||||||
|
|
||||||
{/* 2FA Required Banner */}
|
{/* 2FA Required Banner */}
|
||||||
{user?.require2FA && !user?.totpEnabled && (
|
{user?.require2FA && !user?.totpEnabled && (
|
||||||
<Card
|
<Card
|
||||||
|
|||||||
27
src/admin/pages/Odin.tsx
Normal file
27
src/admin/pages/Odin.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import { PageEnter } from "../ui";
|
||||||
|
import { useAuth } from "../context/AuthContext";
|
||||||
|
import DashAssistant from "../components/dashboard/DashAssistant";
|
||||||
|
|
||||||
|
export default function Odin() {
|
||||||
|
const { hasPermission } = useAuth();
|
||||||
|
|
||||||
|
if (!hasPermission("ai.use")) {
|
||||||
|
return (
|
||||||
|
<PageEnter>
|
||||||
|
<Typography variant="body1" color="text.secondary">
|
||||||
|
Nemáte oprávnění pro AI asistenta.
|
||||||
|
</Typography>
|
||||||
|
</PageEnter>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageEnter>
|
||||||
|
<Box sx={{ maxWidth: 920, mx: "auto" }}>
|
||||||
|
<DashAssistant />
|
||||||
|
</Box>
|
||||||
|
</PageEnter>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -38,6 +38,22 @@ export const menuSections: MenuSection[] = [
|
|||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/odin",
|
||||||
|
label: "Odin",
|
||||||
|
permission: "ai.use",
|
||||||
|
end: true,
|
||||||
|
icon: (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
>
|
||||||
|
<path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user