feat(ui): sidebar icon refresh + unified detail-page headers
Sidebar (user-approved design, docs/superpowers/specs/2026-06-12-sidebar- icons-design.md): all 31 menu items get unique contextual stroke glyphs in rounded-square tiles tinted per section (Docházka blue, Kniha jízd teal, Administrativa amber, Sklad violet, Systém slate; new teal/violet/slate palette entries with channel tokens in both schemes). Replaces the old set where 13 items shared a glyph (3x people, 3x sliders, ...) and Faktury was a dollar sign. Odin stays the only solid + animated tile. Uniqueness pinned by navdata-icons.test.ts (tsconfig.test.json gains jsx for the .tsx import). Headers: ProjectDetail and OrderDetail now match the Offer/Invoice shell — number rendered in secondary color at regular weight next to the bold entity word, and flexWrap on both left header boxes so the title drops below Zpět on phones instead of overflowing the viewport (the reported mobile bug on received orders). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
52
docs/superpowers/specs/2026-06-12-sidebar-icons-design.md
Normal file
52
docs/superpowers/specs/2026-06-12-sidebar-icons-design.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Sidebar icon refresh — design
|
||||
|
||||
**Date:** 2026-06-12 · **Status:** approved (visual companion session, 3 screens)
|
||||
|
||||
## Problem
|
||||
|
||||
All 31 sidebar items have icons, but 13 share a glyph with another item
|
||||
(3× "people", 3× "sliders", 2× grid/bar-chart/box/history-clock) and Faktury
|
||||
uses a dollar sign in an app that bills in Kč/EUR. Duplicated glyphs defeat
|
||||
the purpose of icons as unique visual anchors.
|
||||
|
||||
## Decisions (user-approved)
|
||||
|
||||
1. **Full refresh** of all items (option B over targeted fix).
|
||||
2. **Tile language (option B of 3 rendered directions):** every item's glyph
|
||||
sits in a rounded-square tile (border-radius 32%, echoing OdinMark), as a
|
||||
**soft color wash — one muted hue per menu section**:
|
||||
Přehled `primary` (red) · Docházka `info` (blue) · Kniha jízd `teal` ·
|
||||
Administrativa `warning` (amber) · Sklad `violet` · Systém `slate`.
|
||||
3. **Odin stays unique:** the only **solid** (gradient) tile and the only
|
||||
animated icon. No animation anywhere else. Red stays reserved for
|
||||
Odin/primary.
|
||||
4. **Glyph set** approved on the full-set screen: receipt for Faktury,
|
||||
business card for Zákazníci, truck for Dodavatelé, stamp for Schvalování,
|
||||
ledger ± for Správa bilancí, route/pin/car for Kniha jízd, barcode for
|
||||
Inventura, shelf rack for Lokace, arrows into/out of a box for
|
||||
Příjmy/Výdeje, warehouse hall for Sklad přehled, tag/cart/folder for
|
||||
Nabídky/Objednávky/Projekty, gauge for Přehled, paper plane for Žádosti,
|
||||
timesheet for Moje historie (docházka), shapes for Kategorie, chart frame
|
||||
for Reporty, magnifier-over-list for Audit log. Kept concepts (now unique
|
||||
in the set): clock (Záznam docházky), calendar (Plán prací), people
|
||||
(Uživatelé), gear (Nastavení), box (Položky).
|
||||
|
||||
## Implementation
|
||||
|
||||
- `theme.ts`: add `teal`, `violet`, `slate` palette entries (light + dark
|
||||
mains, full main/light/dark/contrastText so cssVariables emits `*Channel`
|
||||
tokens) + TS module augmentation.
|
||||
- `navData.tsx`: `MenuSection.hue` (palette key), new glyph SVGs
|
||||
(stroke, 24-viewBox, width 2, round caps), `MenuItem.rawIcon` flag for
|
||||
Odin (its icon ships its own tile).
|
||||
- `SidebarNav.tsx`: renders the tile (26 px, radius 32%, `bgcolor:
|
||||
rgba(<hue>.mainChannel / 0.12)`, glyph color `<hue>.main`, svg 16 px)
|
||||
around every non-`rawIcon` item. No hardcoded hex; washes are
|
||||
channel-alpha so both schemes work.
|
||||
- Pinning test `navdata-icons.test.ts`: every non-raw icon renders to unique
|
||||
static markup (no two items may ever share a glyph again).
|
||||
|
||||
## Out of scope
|
||||
|
||||
Animation for non-Odin icons (explicitly rejected), page-level icons,
|
||||
mobile drawer changes (it reuses SidebarNav).
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "app-ts",
|
||||
"version": "2.4.31",
|
||||
"version": "2.4.32",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "app-ts",
|
||||
"version": "2.4.31",
|
||||
"version": "2.4.32",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/sdk": "^0.102.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-ts",
|
||||
"version": "2.4.31",
|
||||
"version": "2.4.32",
|
||||
"description": "",
|
||||
"main": "dist/server.js",
|
||||
"scripts": {
|
||||
|
||||
51
src/__tests__/navdata-icons.test.ts
Normal file
51
src/__tests__/navdata-icons.test.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { isValidElement, type ReactElement } from "react";
|
||||
import { menuSections, type MenuItem } from "../admin/ui/navData";
|
||||
|
||||
/**
|
||||
* Pins the sidebar icon-refresh contract (2026-06-12 design): every menu
|
||||
* item has a glyph, and no two items share one — duplicated glyphs were the
|
||||
* whole problem the refresh fixed (3× "people", 3× "sliders", …). Odin's
|
||||
* rawIcon (OdinMark, MUI-styled + animated) is exempt from rendering here
|
||||
* but still must exist and stay the ONLY rawIcon.
|
||||
*/
|
||||
|
||||
const allItems: Array<MenuItem & { section: string }> = menuSections.flatMap(
|
||||
(s) => s.items.map((item) => ({ section: s.label, ...item })),
|
||||
);
|
||||
|
||||
describe("sidebar nav icons", () => {
|
||||
it("every item has an icon and a section hue", () => {
|
||||
for (const section of menuSections) {
|
||||
expect(section.hue, `section ${section.label}`).toBeTruthy();
|
||||
}
|
||||
for (const item of allItems) {
|
||||
expect(isValidElement(item.icon), `${item.section}/${item.label}`).toBe(
|
||||
true,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("Odin is the only rawIcon (its own tile + animation stay unique)", () => {
|
||||
const raw = allItems.filter((i) => i.rawIcon);
|
||||
expect(raw.map((i) => i.label)).toEqual(["Odin"]);
|
||||
});
|
||||
|
||||
it("no two items share a glyph", () => {
|
||||
const rendered = allItems
|
||||
.filter((i) => !i.rawIcon)
|
||||
.map((item) => ({
|
||||
key: `${item.section}/${item.label}`,
|
||||
markup: renderToStaticMarkup(item.icon as ReactElement),
|
||||
}));
|
||||
|
||||
const seen = new Map<string, string>();
|
||||
for (const { key, markup } of rendered) {
|
||||
const dupe = seen.get(markup);
|
||||
expect(dupe, `${key} shares its glyph with ${dupe}`).toBeUndefined();
|
||||
seen.set(markup, key);
|
||||
}
|
||||
expect(seen.size).toBe(rendered.length);
|
||||
});
|
||||
});
|
||||
@@ -389,7 +389,17 @@ export default function OrderDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: the title must drop below the Zpět button on phones
|
||||
instead of overflowing the viewport (same as Offer/Invoice
|
||||
detail headers). */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/orders"
|
||||
@@ -399,9 +409,22 @@ export default function OrderDetail() {
|
||||
>
|
||||
Zpět
|
||||
</Button>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 1.5,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4">
|
||||
Objednávka {order.order_number}
|
||||
Objednávka
|
||||
<Box
|
||||
component="span"
|
||||
sx={{ color: "text.secondary", ml: 1, fontWeight: 400 }}
|
||||
>
|
||||
{order.order_number}
|
||||
</Box>
|
||||
</Typography>
|
||||
<StatusChip
|
||||
label={statusLabel(ORDER_STATUS, order.status)}
|
||||
|
||||
@@ -298,7 +298,17 @@ export default function ProjectDetail() {
|
||||
mb: 3,
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 2 }}>
|
||||
{/* flexWrap: the title must drop below the Zpět button on phones
|
||||
instead of being squeezed beside it (mirrors Offer/Invoice
|
||||
detail headers). */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 2,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
component={RouterLink}
|
||||
to="/projects"
|
||||
@@ -308,9 +318,22 @@ export default function ProjectDetail() {
|
||||
>
|
||||
Zpět
|
||||
</Button>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: 1.5 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: 1.5,
|
||||
flexWrap: "wrap",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h4">
|
||||
Projekt {project.project_number}
|
||||
Projekt
|
||||
<Box
|
||||
component="span"
|
||||
sx={{ color: "text.secondary", ml: 1, fontWeight: 400 }}
|
||||
>
|
||||
{project.project_number}
|
||||
</Box>
|
||||
</Typography>
|
||||
<StatusChip
|
||||
label={STATUS_LABELS[project.status] || project.status}
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { createTheme, type Theme } from "@mui/material/styles";
|
||||
import {
|
||||
createTheme,
|
||||
type Theme,
|
||||
type PaletteColor,
|
||||
type PaletteColorChannel,
|
||||
} from "@mui/material/styles";
|
||||
|
||||
const FONT_BODY = "'Plus Jakarta Sans', system-ui, sans-serif";
|
||||
const FONT_HEADING = "'Urbanist', sans-serif";
|
||||
@@ -33,6 +38,26 @@ export const theme = createTheme({
|
||||
warning: { main: "#b45309", contrastText: "#fff" },
|
||||
error: { main: "#b91c1c", contrastText: "#fff" },
|
||||
info: { main: "#1d4ed8", contrastText: "#fff" },
|
||||
// Sidebar section hues (full objects so cssVariables emits *Channel
|
||||
// tokens — the nav tiles use channel-alpha washes).
|
||||
teal: {
|
||||
main: "#0e8a7c",
|
||||
light: "#3aa99c",
|
||||
dark: "#0a675d",
|
||||
contrastText: "#fff",
|
||||
},
|
||||
violet: {
|
||||
main: "#6a4cb4",
|
||||
light: "#8a6fd0",
|
||||
dark: "#54399a",
|
||||
contrastText: "#fff",
|
||||
},
|
||||
slate: {
|
||||
main: "#5c6470",
|
||||
light: "#7d8694",
|
||||
dark: "#454c56",
|
||||
contrastText: "#fff",
|
||||
},
|
||||
background: { default: "#f4f3f1", paper: "#ffffff" },
|
||||
text: { primary: "#1a1a1a", secondary: "#555555" },
|
||||
divider: "rgba(0,0,0,0.1)",
|
||||
@@ -46,6 +71,25 @@ export const theme = createTheme({
|
||||
warning: { main: "#f59e0b", contrastText: "#1a1a1a" },
|
||||
error: { main: "#ef4444", contrastText: "#1a1a1a" },
|
||||
info: { main: "#3b82f6", contrastText: "#1a1a1a" },
|
||||
// Sidebar section hues — brighter mains so the glyphs read on dark.
|
||||
teal: {
|
||||
main: "#2dd4bf",
|
||||
light: "#5eead4",
|
||||
dark: "#14b8a6",
|
||||
contrastText: "#1a1a1a",
|
||||
},
|
||||
violet: {
|
||||
main: "#a78bfa",
|
||||
light: "#c4b5fd",
|
||||
dark: "#8b5cf6",
|
||||
contrastText: "#1a1a1a",
|
||||
},
|
||||
slate: {
|
||||
main: "#94a3b8",
|
||||
light: "#cbd5e1",
|
||||
dark: "#64748b",
|
||||
contrastText: "#1a1a1a",
|
||||
},
|
||||
background: { default: "#0f0f0f", paper: "#1a1a1a" },
|
||||
text: { primary: "#ffffff", secondary: "#a0a0a0" },
|
||||
divider: "rgba(255,255,255,0.08)",
|
||||
@@ -239,6 +283,24 @@ export const fonts = {
|
||||
mono: FONT_MONO,
|
||||
};
|
||||
|
||||
// Custom sidebar-section palette entries (teal/violet/slate above). The
|
||||
// channel intersection matters: theme.vars.palette.<hue>.mainChannel is how
|
||||
// the nav tiles build their alpha washes.
|
||||
type NavPaletteColor = PaletteColor & PaletteColorChannel;
|
||||
|
||||
declare module "@mui/material/styles" {
|
||||
interface Palette {
|
||||
teal: NavPaletteColor;
|
||||
violet: NavPaletteColor;
|
||||
slate: NavPaletteColor;
|
||||
}
|
||||
interface PaletteOptions {
|
||||
teal?: PaletteOptions["primary"];
|
||||
violet?: PaletteOptions["primary"];
|
||||
slate?: PaletteOptions["primary"];
|
||||
}
|
||||
}
|
||||
|
||||
export type IconBadgeColor =
|
||||
| "default"
|
||||
| "primary"
|
||||
|
||||
@@ -88,9 +88,8 @@ export default function SidebarNav({ onNavigate, onLogout }: SidebarNavProps) {
|
||||
sx={{
|
||||
borderRadius: 2,
|
||||
mb: 0.25,
|
||||
py: 0.6,
|
||||
py: 0.45,
|
||||
color: "text.secondary",
|
||||
"& svg": { width: 18, height: 18 },
|
||||
"&.Mui-selected, &.Mui-selected:hover": {
|
||||
bgcolor: "background.paper",
|
||||
color: "primary.main",
|
||||
@@ -99,8 +98,29 @@ export default function SidebarNav({ onNavigate, onLogout }: SidebarNavProps) {
|
||||
},
|
||||
}}
|
||||
>
|
||||
<ListItemIcon sx={{ minWidth: 32, color: "inherit" }}>
|
||||
{item.icon}
|
||||
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
|
||||
{item.rawIcon ? (
|
||||
item.icon
|
||||
) : (
|
||||
<Box
|
||||
sx={(t) => ({
|
||||
width: 26,
|
||||
height: 26,
|
||||
// Echoes OdinMark's rounded-square tile; only Odin
|
||||
// is solid + animated, these are quiet washes.
|
||||
borderRadius: "32%",
|
||||
flexShrink: 0,
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
bgcolor: `rgba(${t.vars!.palette[section.hue].mainChannel} / 0.12)`,
|
||||
color: t.vars!.palette[section.hue].main,
|
||||
"& svg": { width: 16, height: 16 },
|
||||
})}
|
||||
>
|
||||
{item.icon}
|
||||
</Box>
|
||||
)}
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary={item.label}
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
import { type ReactNode } from "react";
|
||||
import OdinMark from "../components/odin/OdinMark";
|
||||
|
||||
/** Palette key driving a section's tile wash + glyph color (see theme.ts). */
|
||||
export type NavHue =
|
||||
| "primary"
|
||||
| "info"
|
||||
| "teal"
|
||||
| "warning"
|
||||
| "violet"
|
||||
| "slate";
|
||||
|
||||
export interface MenuItem {
|
||||
path: string;
|
||||
label: string;
|
||||
@@ -10,33 +19,51 @@ export interface MenuItem {
|
||||
matchAlso?: string[];
|
||||
matchExclude?: string[];
|
||||
icon: ReactNode;
|
||||
/** Icon ships its own container (OdinMark) — SidebarNav skips the tile. */
|
||||
rawIcon?: boolean;
|
||||
}
|
||||
|
||||
export interface MenuSection {
|
||||
label: string;
|
||||
hue: NavHue;
|
||||
items: MenuItem[];
|
||||
}
|
||||
|
||||
/**
|
||||
* One stroke glyph per item, every glyph unique within the set (pinned by
|
||||
* navdata-icons.test.ts). 24-viewBox, stroke 2, round caps — rendered at
|
||||
* 16px inside SidebarNav's 26px section-hue tile.
|
||||
*/
|
||||
function glyph(children: ReactNode) {
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
{children}
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export const menuSections: MenuSection[] = [
|
||||
{
|
||||
label: "Přehled",
|
||||
hue: "primary",
|
||||
items: [
|
||||
{
|
||||
path: "/",
|
||||
label: "Přehled",
|
||||
end: true,
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||
</svg>
|
||||
// tachometr — dashboard
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="12" cy="13" r="8.5" />
|
||||
<path d="M12 13l4-4.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -44,78 +71,66 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Odin",
|
||||
permission: "ai.use",
|
||||
end: true,
|
||||
icon: <OdinMark size={22} />,
|
||||
icon: <OdinMark size={26} />,
|
||||
rawIcon: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Docházka",
|
||||
hue: "info",
|
||||
items: [
|
||||
{
|
||||
path: "/attendance",
|
||||
label: "Záznam",
|
||||
permission: "attendance.record",
|
||||
end: true,
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
// píchačky — hodiny
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
<polyline points="12 7 12 12 15 15" />
|
||||
</svg>
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/attendance/history",
|
||||
label: "Moje historie",
|
||||
permission: "attendance.history",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<polyline points="12 8 12 12 14 14" />
|
||||
<path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" />
|
||||
</svg>
|
||||
// výkaz s hodinami — timesheet
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M14 3H5a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h6" />
|
||||
<line x1="7" y1="8" x2="13" y2="8" />
|
||||
<line x1="7" y1="12" x2="11" y2="12" />
|
||||
<circle cx="16.5" cy="16" r="4.5" />
|
||||
<polyline points="16.5 14 16.5 16 18 17" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/attendance/requests",
|
||||
label: "Žádosti",
|
||||
permission: "attendance.record",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="12" y1="18" x2="12" y2="12" />
|
||||
<line x1="9" y1="15" x2="15" y2="15" />
|
||||
</svg>
|
||||
// odeslaná žádost — vlaštovka
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M22 2L11 13" />
|
||||
<path d="M22 2l-7 20-4-9-9-4z" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/attendance/approval",
|
||||
label: "Schvalování",
|
||||
permission: "attendance.approve",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M9 12l2 2 4-4" />
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
</svg>
|
||||
// razítko
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M9.5 12V9.8C9.5 8.4 8 7.8 8 6a4 4 0 0 1 8 0c0 1.8-1.5 2.4-1.5 3.8V12" />
|
||||
<path d="M5 16a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v2H5z" />
|
||||
<line x1="5" y1="21" x2="19" y2="21" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -124,147 +139,108 @@ export const menuSections: MenuSection[] = [
|
||||
permission: "attendance.manage",
|
||||
matchPrefix: "/attendance/admin",
|
||||
matchAlso: ["/attendance/create", "/attendance/location"],
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="4" y1="21" x2="4" y2="14" />
|
||||
<line x1="4" y1="10" x2="4" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12" y2="3" />
|
||||
<line x1="20" y1="21" x2="20" y2="16" />
|
||||
<line x1="20" y1="12" x2="20" y2="3" />
|
||||
<line x1="1" y1="14" x2="7" y2="14" />
|
||||
<line x1="9" y1="8" x2="15" y2="8" />
|
||||
<line x1="17" y1="16" x2="23" y2="16" />
|
||||
</svg>
|
||||
// osoba + fajfka
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="9" cy="8" r="3.5" />
|
||||
<path d="M3 20a6 6 0 0 1 12 0" />
|
||||
<path d="M15.5 11.5l2 2 4-4" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/attendance/balances",
|
||||
label: "Správa bilancí",
|
||||
permission: "attendance.balances",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="18" y1="20" x2="18" y2="10" />
|
||||
<line x1="12" y1="20" x2="12" y2="4" />
|
||||
<line x1="6" y1="20" x2="6" y2="14" />
|
||||
</svg>
|
||||
// kniha plus/mínus — ruční korekce bilancí
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="4" y="3" width="16" height="18" rx="2" />
|
||||
<line x1="9" y1="8.5" x2="15" y2="8.5" />
|
||||
<line x1="12" y1="5.5" x2="12" y2="11.5" />
|
||||
<line x1="9" y1="16" x2="15" y2="16" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/plan-work",
|
||||
label: "Plán prací",
|
||||
permission: ["attendance.manage", "attendance.record"],
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" />
|
||||
// kalendář s plánovacím blokem
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="3" y="4" width="18" height="17" rx="2" />
|
||||
<line x1="16" y1="2" x2="16" y2="6" />
|
||||
<line x1="8" y1="2" x2="8" y2="6" />
|
||||
<line x1="3" y1="10" x2="21" y2="10" />
|
||||
</svg>
|
||||
<line x1="3" y1="9.5" x2="21" y2="9.5" />
|
||||
<path d="M8 14h3v3H8z" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Kniha jízd",
|
||||
hue: "teal",
|
||||
items: [
|
||||
{
|
||||
path: "/trips",
|
||||
label: "Záznam",
|
||||
permission: "trips.record",
|
||||
end: true,
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<circle cx="5" cy="18" r="3" />
|
||||
<circle cx="19" cy="18" r="3" />
|
||||
<path d="M5 18V12L8 5h8l3 7v6" />
|
||||
<path d="M10 18h4" />
|
||||
</svg>
|
||||
// trasa A→B
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="5" cy="19" r="2" />
|
||||
<circle cx="19" cy="5" r="2" />
|
||||
<path d="M5 17C5 11 19 13 19 7" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/trips/history",
|
||||
label: "Moje historie",
|
||||
permission: "trips.history",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<polyline points="12 8 12 12 14 14" />
|
||||
<path d="M3.05 11a9 9 0 1 1 .5 4m-.5 5v-5h5" />
|
||||
</svg>
|
||||
// navštívená místa — pin
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M12 21s-6-5.2-6-10a6 6 0 0 1 12 0c0 4.8-6 10-6 10z" />
|
||||
<circle cx="12" cy="11" r="2.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/trips/admin",
|
||||
label: "Správa",
|
||||
permission: "trips.manage",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="4" y1="21" x2="4" y2="14" />
|
||||
<line x1="4" y1="10" x2="4" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12" y2="3" />
|
||||
<line x1="20" y1="21" x2="20" y2="16" />
|
||||
<line x1="20" y1="12" x2="20" y2="3" />
|
||||
<line x1="1" y1="14" x2="7" y2="14" />
|
||||
<line x1="9" y1="8" x2="15" y2="8" />
|
||||
<line x1="17" y1="16" x2="23" y2="16" />
|
||||
</svg>
|
||||
// schránka s fajfkou — kniha jízd
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="5" y="4" width="14" height="17" rx="2" />
|
||||
<path d="M9 2h6v4H9z" />
|
||||
<path d="M9 13l2 2 4-4" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/vehicles",
|
||||
label: "Vozidla",
|
||||
permission: "vehicles.manage",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<rect x="1" y="3" width="15" height="13" rx="2" />
|
||||
<path d="M16 8h4l3 3v5h-7V8z" />
|
||||
<circle cx="5.5" cy="18.5" r="2.5" />
|
||||
<circle cx="18.5" cy="18.5" r="2.5" />
|
||||
</svg>
|
||||
// auto
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M5 17H3v-4l2-5h11l3 5h2v4h-2" />
|
||||
<circle cx="7.5" cy="17" r="2" />
|
||||
<circle cx="16.5" cy="17" r="2" />
|
||||
<line x1="9.5" y1="17" x2="14.5" y2="17" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Administrativa",
|
||||
hue: "warning",
|
||||
items: [
|
||||
{
|
||||
path: "/offers",
|
||||
@@ -272,16 +248,12 @@ export const menuSections: MenuSection[] = [
|
||||
permission: "offers.view",
|
||||
matchPrefix: "/offers",
|
||||
matchExclude: ["/offers/customers", "/offers/templates"],
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
</svg>
|
||||
// cenovka
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M20.6 13.4L11 3H4v7l9.6 10.4a2 2 0 0 0 2.8 0l4.2-4.2a2 2 0 0 0 0-2.8z" />
|
||||
<circle cx="8" cy="8" r="1.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -289,17 +261,13 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Objednávky",
|
||||
permission: "orders.view",
|
||||
matchPrefix: "/orders",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M6 2L3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4z" />
|
||||
<line x1="3" y1="6" x2="21" y2="6" />
|
||||
<path d="M16 10a4 4 0 0 1-8 0" />
|
||||
</svg>
|
||||
// košík
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="9" cy="20" r="1.6" />
|
||||
<circle cx="17" cy="20" r="1.6" />
|
||||
<path d="M2 3h3l2.7 12h10.6l2.7-9H6" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -307,16 +275,13 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Faktury",
|
||||
permission: "invoices.view",
|
||||
matchPrefix: "/invoices",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="12" y1="1" x2="12" y2="23" />
|
||||
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" />
|
||||
</svg>
|
||||
// účtenka
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M6 2h12v20l-3-2-3 2-3-2-3 2z" />
|
||||
<line x1="9" y1="7" x2="15" y2="7" />
|
||||
<line x1="9" y1="11" x2="15" y2="11" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -324,60 +289,44 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Projekty",
|
||||
permission: "projects.view",
|
||||
matchPrefix: "/projects",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" />
|
||||
<line x1="8" y1="21" x2="16" y2="21" />
|
||||
<line x1="12" y1="17" x2="12" y2="21" />
|
||||
</svg>
|
||||
// složka (NAS projektové složky)
|
||||
icon: glyph(
|
||||
<path d="M3 7a2 2 0 0 1 2-2h4l2 2.5h8a2 2 0 0 1 2 2V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/offers/customers",
|
||||
label: "Zákazníci",
|
||||
permission: "customers.view",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||||
</svg>
|
||||
// vizitka
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" />
|
||||
<circle cx="8.5" cy="10.5" r="2.2" />
|
||||
<path d="M5 16.5a3.5 3.5 0 0 1 7 0" />
|
||||
<line x1="14.5" y1="9.5" x2="19" y2="9.5" />
|
||||
<line x1="14.5" y1="13.5" x2="19" y2="13.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Sklad",
|
||||
hue: "violet",
|
||||
items: [
|
||||
{
|
||||
path: "/warehouse",
|
||||
label: "Přehled",
|
||||
permission: "warehouse.view",
|
||||
end: true,
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
||||
<line x1="12" y1="22.08" x2="12" y2="12" />
|
||||
</svg>
|
||||
// skladová hala
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M3 21V9l9-5 9 5v12" />
|
||||
<path d="M7 21v-7h10v7" />
|
||||
<line x1="7" y1="17.5" x2="17" y2="17.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -385,17 +334,13 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Položky",
|
||||
permission: "warehouse.view",
|
||||
matchPrefix: "/warehouse/items",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
// krabice
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z" />
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96" />
|
||||
<line x1="12" y1="22.08" x2="12" y2="12" />
|
||||
</svg>
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -403,17 +348,13 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Příjmy",
|
||||
permission: "warehouse.operate",
|
||||
matchPrefix: "/warehouse/receipts",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<polyline points="17 11 12 6 7 11" />
|
||||
<line x1="12" y1="6" x2="12" y2="18" />
|
||||
<path d="M5 19h14" />
|
||||
</svg>
|
||||
// šipka do krabice
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="4" y="12" width="16" height="9" rx="1" />
|
||||
<polyline points="8 6 12 10 16 6" />
|
||||
<line x1="12" y1="2" x2="12" y2="10" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -421,17 +362,13 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Výdeje",
|
||||
permission: "warehouse.operate",
|
||||
matchPrefix: "/warehouse/issues",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<polyline points="7 13 12 18 17 13" />
|
||||
<line x1="12" y1="18" x2="12" y2="6" />
|
||||
<path d="M5 5h14" />
|
||||
</svg>
|
||||
// šipka z krabice ven
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="4" y="13" width="16" height="8" rx="1" />
|
||||
<polyline points="8 6 12 2 16 6" />
|
||||
<line x1="12" y1="2" x2="12" y2="11" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -439,18 +376,12 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Rezervace",
|
||||
permission: "warehouse.operate",
|
||||
matchPrefix: "/warehouse/reservations",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<line x1="19" y1="8" x2="19" y2="14" />
|
||||
<line x1="22" y1="11" x2="16" y2="11" />
|
||||
</svg>
|
||||
// krabice se záložkou
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="3" y="8" width="13" height="12" rx="1" />
|
||||
<path d="M16 3h5v9l-2.5-2L16 12z" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -458,16 +389,15 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Inventura",
|
||||
permission: "warehouse.inventory",
|
||||
matchPrefix: "/warehouse/inventory",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M9 11l3 3L22 4" />
|
||||
<path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" />
|
||||
</svg>
|
||||
// čárový kód — fyzický sken zásob
|
||||
icon: glyph(
|
||||
<>
|
||||
<line x1="4" y1="6" x2="4" y2="18" />
|
||||
<line x1="8" y1="6" x2="8" y2="18" />
|
||||
<line x1="12" y1="6" x2="12" y2="18" />
|
||||
<line x1="17" y1="6" x2="17" y2="18" />
|
||||
<line x1="20" y1="6" x2="20" y2="18" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -475,17 +405,12 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Reporty",
|
||||
permission: "warehouse.view",
|
||||
end: true,
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="18" y1="20" x2="18" y2="10" />
|
||||
<line x1="12" y1="20" x2="12" y2="4" />
|
||||
<line x1="6" y1="20" x2="6" y2="14" />
|
||||
</svg>
|
||||
// graf v rámu
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="3" y="3" width="18" height="18" rx="2" />
|
||||
<polyline points="7 14 11 10 14 13 17 8" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -493,23 +418,14 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Kategorie",
|
||||
permission: "warehouse.manage",
|
||||
matchPrefix: "/warehouse/categories",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<line x1="4" y1="21" x2="4" y2="14" />
|
||||
<line x1="4" y1="10" x2="4" y2="3" />
|
||||
<line x1="12" y1="21" x2="12" y2="12" />
|
||||
<line x1="12" y1="8" x2="12" y2="3" />
|
||||
<line x1="20" y1="21" x2="20" y2="16" />
|
||||
<line x1="20" y1="12" x2="20" y2="3" />
|
||||
<line x1="1" y1="14" x2="7" y2="14" />
|
||||
<line x1="9" y1="8" x2="15" y2="8" />
|
||||
<line x1="17" y1="16" x2="23" y2="16" />
|
||||
</svg>
|
||||
// tvary — třídění do kategorií
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="7.5" cy="7.5" r="3.5" />
|
||||
<rect x="13.5" y="4" width="7" height="7" rx="1" />
|
||||
<path d="M7.5 13.5l3.5 7h-7z" />
|
||||
<rect x="13.5" y="13.5" width="7" height="7" rx="3.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -517,18 +433,16 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Lokace",
|
||||
permission: "warehouse.manage",
|
||||
matchPrefix: "/warehouse/locations",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||
</svg>
|
||||
// regál s krabicemi
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M3 21V4" />
|
||||
<path d="M21 21V4" />
|
||||
<line x1="3" y1="12" x2="21" y2="12" />
|
||||
<line x1="3" y1="21" x2="21" y2="21" />
|
||||
<rect x="7" y="8" width="4" height="4" />
|
||||
<rect x="13" y="17" width="4" height="4" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -536,41 +450,34 @@ export const menuSections: MenuSection[] = [
|
||||
label: "Dodavatelé",
|
||||
permission: "warehouse.manage",
|
||||
matchPrefix: "/warehouse/suppliers",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||||
</svg>
|
||||
// kamion
|
||||
icon: glyph(
|
||||
<>
|
||||
<rect x="1" y="6" width="13" height="10" rx="1" />
|
||||
<path d="M14 9h4l3 4v3h-7" />
|
||||
<circle cx="6" cy="18.5" r="2" />
|
||||
<circle cx="17.5" cy="18.5" r="2" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Systém",
|
||||
hue: "slate",
|
||||
items: [
|
||||
{
|
||||
path: "/users",
|
||||
label: "Uživatelé",
|
||||
permission: "users.view",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
// lidé — uživatelské účty
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||||
</svg>
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -583,35 +490,27 @@ export const menuSections: MenuSection[] = [
|
||||
"settings.system",
|
||||
"settings.templates",
|
||||
],
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
// ozubené kolo
|
||||
icon: glyph(
|
||||
<>
|
||||
<circle cx="12" cy="12" r="3" />
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
||||
</svg>
|
||||
</>,
|
||||
),
|
||||
},
|
||||
{
|
||||
path: "/audit-log",
|
||||
label: "Audit log",
|
||||
permission: "settings.audit",
|
||||
icon: (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
>
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
||||
<polyline points="14 2 14 8 20 8" />
|
||||
<line x1="16" y1="13" x2="8" y2="13" />
|
||||
<line x1="16" y1="17" x2="8" y2="17" />
|
||||
<polyline points="10 9 9 9 8 9" />
|
||||
</svg>
|
||||
// lupa nad záznamy
|
||||
icon: glyph(
|
||||
<>
|
||||
<path d="M4 5h12" />
|
||||
<path d="M4 10h6" />
|
||||
<path d="M4 15h5" />
|
||||
<circle cx="15" cy="14" r="4.5" />
|
||||
<line x1="18.2" y1="17.2" x2="21.5" y2="20.5" />
|
||||
</>,
|
||||
),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2022"],
|
||||
"types": ["node", "vitest/globals"],
|
||||
"jsx": "react-jsx",
|
||||
"moduleResolution": "node",
|
||||
"noEmit": true,
|
||||
"strict": true,
|
||||
|
||||
Reference in New Issue
Block a user