mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2024-12-26 13:29:05 +01:00
fix: Responsive Navbar
Missing Burger menu icon
This commit is contained in:
parent
b7aacd1b33
commit
80be78604f
20
web/frontend/package-lock.json
generated
20
web/frontend/package-lock.json
generated
@ -14,7 +14,7 @@
|
|||||||
"chart.js": "^4.3.3",
|
"chart.js": "^4.3.3",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"svelte-chartjs": "^3.1.2",
|
"svelte-chartjs": "^3.1.2",
|
||||||
"sveltestrap": "^5.10.0",
|
"sveltestrap": "^5.11.1",
|
||||||
"uplot": "^1.6.24",
|
"uplot": "^1.6.24",
|
||||||
"wonka": "^6.3.2"
|
"wonka": "^6.3.2"
|
||||||
},
|
},
|
||||||
@ -138,9 +138,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rollup/plugin-node-resolve": {
|
"node_modules/@rollup/plugin-node-resolve": {
|
||||||
"version": "15.2.0",
|
"version": "15.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-15.2.1.tgz",
|
||||||
"integrity": "sha512-mKur03xNGT8O9ODO6FtT43ITGqHWZbKPdVJHZb+iV9QYcdlhUUB0wgknvA4KCUmC5oHJF6O2W1EgmyOQyVUI4Q==",
|
"integrity": "sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rollup/pluginutils": "^5.0.1",
|
"@rollup/pluginutils": "^5.0.1",
|
||||||
@ -346,9 +346,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
@ -547,9 +547,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/rollup": {
|
"node_modules/rollup": {
|
||||||
"version": "3.28.0",
|
"version": "3.28.1",
|
||||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/rollup/-/rollup-3.28.1.tgz",
|
||||||
"integrity": "sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==",
|
"integrity": "sha512-R9OMQmIHJm9znrU3m3cpE8uhN0fGdXiawME7aZIpQqvpS/85+Vt1Hq1/yVIcYfOmaQiHjvXkQAoJukvLpau6Yw==",
|
||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"rollup": "dist/bin/rollup"
|
"rollup": "dist/bin/rollup"
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
"chart.js": "^4.3.3",
|
"chart.js": "^4.3.3",
|
||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"svelte-chartjs": "^3.1.2",
|
"svelte-chartjs": "^3.1.2",
|
||||||
"sveltestrap": "^5.10.0",
|
"sveltestrap": "^5.11.1",
|
||||||
"uplot": "^1.6.24",
|
"uplot": "^1.6.24",
|
||||||
"wonka": "^6.3.2"
|
"wonka": "^6.3.2"
|
||||||
}
|
}
|
||||||
|
@ -1,110 +1,293 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Icon, Button, InputGroup, Input, Collapse,
|
import {
|
||||||
Navbar, NavbarBrand, Nav, NavItem, NavLink, NavbarToggler,
|
Icon,
|
||||||
Dropdown, DropdownToggle, DropdownMenu, DropdownItem, InputGroupText } from 'sveltestrap'
|
Button,
|
||||||
|
InputGroup,
|
||||||
|
Input,
|
||||||
|
Collapse,
|
||||||
|
Navbar,
|
||||||
|
NavbarBrand,
|
||||||
|
Nav,
|
||||||
|
NavItem,
|
||||||
|
NavLink,
|
||||||
|
NavbarToggler,
|
||||||
|
Dropdown,
|
||||||
|
DropdownToggle,
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownItem,
|
||||||
|
InputGroupText,
|
||||||
|
} from "sveltestrap";
|
||||||
|
|
||||||
export let username // empty string if auth. is disabled, otherwise the username as string
|
export let username; // empty string if auth. is disabled, otherwise the username as string
|
||||||
export let authlevel // Integer
|
export let authlevel; // Integer
|
||||||
export let clusters // array of names
|
export let clusters; // array of names
|
||||||
export let roles // Role Enum-Like
|
export let roles; // Role Enum-Like
|
||||||
|
|
||||||
let isOpen = false
|
let isOpen = false;
|
||||||
|
let screenSize;
|
||||||
|
|
||||||
const userviews = [
|
const jobsTitle = new Map();
|
||||||
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' },
|
jobsTitle.set(2, "Job Search");
|
||||||
{ title: `Job Search`, href: '/monitoring/jobs/', icon: 'card-list' },
|
jobsTitle.set(3, "Managed Jobs");
|
||||||
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' }
|
jobsTitle.set(4, "Jobs");
|
||||||
]
|
jobsTitle.set(5, "Jobs");
|
||||||
|
const usersTitle = new Map();
|
||||||
|
usersTitle.set(3, "Managed Users");
|
||||||
|
usersTitle.set(4, "Users");
|
||||||
|
usersTitle.set(5, "Users");
|
||||||
|
|
||||||
const managerviews = [
|
const views = [
|
||||||
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' },
|
{
|
||||||
{ title: `Managed Jobs`, href: '/monitoring/jobs/', icon: 'card-list' },
|
title: "My Jobs",
|
||||||
{ title: `Managed Users`, href: '/monitoring/users/', icon: 'people-fill' },
|
requiredRole: roles.user,
|
||||||
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' }
|
href: `/monitoring/user/${username}`,
|
||||||
]
|
icon: "bar-chart-line-fill",
|
||||||
|
perCluster: false,
|
||||||
const supportviews = [
|
menu: "none",
|
||||||
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' },
|
},
|
||||||
{ title: 'Jobs', href: '/monitoring/jobs/', icon: 'card-list' },
|
{
|
||||||
{ title: 'Users', href: '/monitoring/users/', icon: 'people-fill' },
|
title: jobsTitle.get(authlevel),
|
||||||
{ title: 'Projects', href: '/monitoring/projects/', icon: 'folder' },
|
requiredRole: roles.user,
|
||||||
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' }
|
href: `/monitoring/jobs`,
|
||||||
]
|
icon: "card-list",
|
||||||
|
perCluster: false,
|
||||||
const adminviews = [
|
menu: "none",
|
||||||
{ title: 'My Jobs', href: `/monitoring/user/${username}`, icon: 'bar-chart-line-fill' },
|
},
|
||||||
{ title: 'Jobs', href: '/monitoring/jobs/', icon: 'card-list' },
|
{
|
||||||
{ title: 'Users', href: '/monitoring/users/', icon: 'people-fill' },
|
title: usersTitle.get(authlevel),
|
||||||
{ title: 'Projects', href: '/monitoring/projects/', icon: 'folder' },
|
requiredRole: roles.manager,
|
||||||
{ title: 'Tags', href: '/monitoring/tags/', icon: 'tags' }
|
href: "/monitoring/users/",
|
||||||
]
|
icon: "people-fill",
|
||||||
|
perCluster: false,
|
||||||
const viewsPerCluster = [
|
menu: "Groups",
|
||||||
{ title: 'Analysis', requiredRole: roles.support, href: '/monitoring/analysis/', icon: 'graph-up' },
|
},
|
||||||
{ title: 'Systems', requiredRole: roles.admin, href: '/monitoring/systems/', icon: 'cpu' },
|
{
|
||||||
{ title: 'Status', requiredRole: roles.admin, href: '/monitoring/status/', icon: 'cpu' },
|
title: "Projects",
|
||||||
]
|
requiredRole: roles.support,
|
||||||
|
href: "/monitoring/projects/",
|
||||||
|
icon: "folder",
|
||||||
|
perCluster: false,
|
||||||
|
menu: "Groups",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Tags",
|
||||||
|
requiredRole: roles.user,
|
||||||
|
href: "/monitoring/tags/",
|
||||||
|
icon: "tags",
|
||||||
|
perCluster: false,
|
||||||
|
menu: "Groups",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Analysis",
|
||||||
|
requiredRole: roles.support,
|
||||||
|
href: "/monitoring/analysis/",
|
||||||
|
icon: "graph-up",
|
||||||
|
perCluster: true,
|
||||||
|
menu: "Stats",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Nodes",
|
||||||
|
requiredRole: roles.admin,
|
||||||
|
href: "/monitoring/systems/",
|
||||||
|
icon: "cpu",
|
||||||
|
perCluster: true,
|
||||||
|
menu: "Groups",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Status",
|
||||||
|
requiredRole: roles.admin,
|
||||||
|
href: "/monitoring/status/",
|
||||||
|
icon: "cpu",
|
||||||
|
perCluster: true,
|
||||||
|
menu: "Stats",
|
||||||
|
},
|
||||||
|
];
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Navbar color="light" light expand="lg" fixed="top">
|
<svelte:window bind:innerWidth={screenSize} />
|
||||||
|
<Navbar color="light" light expand="sm" fixed="top">
|
||||||
<NavbarBrand href="/">
|
<NavbarBrand href="/">
|
||||||
<img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem">
|
<img alt="ClusterCockpit Logo" src="/img/logo.png" height="25rem" />
|
||||||
</NavbarBrand>
|
</NavbarBrand>
|
||||||
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
|
<NavbarToggler on:click={() => (isOpen = !isOpen)} />
|
||||||
<Collapse {isOpen} navbar expand="lg" on:update={({ detail }) => (isOpen = detail.isOpen)}>
|
<Collapse
|
||||||
<Nav pills>
|
{isOpen}
|
||||||
{#if authlevel == roles.admin}
|
navbar
|
||||||
{#each adminviews as item}
|
expand="sm"
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
on:update={({ detail }) => (isOpen = detail.isOpen)}
|
||||||
|
>
|
||||||
|
<Nav navbar>
|
||||||
|
{#if screenSize > 1500}
|
||||||
|
{#each views.filter((item) => item.requiredRole <= authlevel) as item}
|
||||||
|
{#if !item.perCluster}
|
||||||
|
<NavLink
|
||||||
|
href={item.href}
|
||||||
|
active={window.location.pathname == item.href}
|
||||||
|
><Icon name={item.icon} /> {item.title}</NavLink
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<Dropdown nav inNavbar>
|
||||||
|
<DropdownToggle nav caret>
|
||||||
|
<Icon name={item.icon} />
|
||||||
|
{item.title}
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
|
{#each clusters as cluster}
|
||||||
|
<DropdownItem
|
||||||
|
href={item.href + cluster.name}
|
||||||
|
active={window.location.pathname ==
|
||||||
|
item.href + cluster.name}
|
||||||
|
>
|
||||||
|
{cluster.name}
|
||||||
|
</DropdownItem>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == roles.support}
|
{:else if screenSize > 1300}
|
||||||
{#each supportviews as item}
|
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu != "Stats") as item}
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
{#if !item.perCluster}
|
||||||
|
<NavLink
|
||||||
|
href={item.href}
|
||||||
|
active={window.location.pathname == item.href}
|
||||||
|
><Icon name={item.icon} /> {item.title}</NavLink
|
||||||
|
>
|
||||||
|
{:else}
|
||||||
|
<Dropdown nav inNavbar>
|
||||||
|
<DropdownToggle nav caret>
|
||||||
|
<Icon name={item.icon} />
|
||||||
|
{item.title}
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
|
{#each clusters as cluster}
|
||||||
|
<DropdownItem
|
||||||
|
href={item.href + cluster.name}
|
||||||
|
active={window.location.pathname ==
|
||||||
|
item.href + cluster.name}
|
||||||
|
>
|
||||||
|
{cluster.name}
|
||||||
|
</DropdownItem>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
{:else if authlevel == roles.manager}
|
<Dropdown nav>
|
||||||
{#each managerviews as item}
|
<DropdownToggle nav caret>
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
<Icon name="graph-up" />
|
||||||
|
Stats
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
|
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == "Stats") as item}
|
||||||
|
<Dropdown nav inNavbar>
|
||||||
|
<DropdownToggle nav caret>
|
||||||
|
<Icon name={item.icon} />
|
||||||
|
{item.title}
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
|
{#each clusters as cluster}
|
||||||
|
<DropdownItem
|
||||||
|
href={item.href + cluster.name}
|
||||||
|
active={window.location.pathname ==
|
||||||
|
item.href + cluster.name}
|
||||||
|
>
|
||||||
|
{cluster.name}
|
||||||
|
</DropdownItem>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
|
{:else}
|
||||||
|
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == "none") as item}
|
||||||
|
<NavLink
|
||||||
|
href={item.href}
|
||||||
|
active={window.location.pathname == item.href}
|
||||||
|
><Icon name={item.icon} /> {item.title}</NavLink
|
||||||
|
>
|
||||||
{/each}
|
{/each}
|
||||||
{:else} <!-- Compatibility: Handle "user role" or "no role" as identical-->
|
{#each Array("Groups", "Stats") as menu}
|
||||||
{#each userviews as item}
|
<Dropdown nav>
|
||||||
<NavLink href={item.href} active={window.location.pathname == item.href}><Icon name={item.icon}/> {item.title}</NavLink>
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
{#each viewsPerCluster.filter(item => item.requiredRole <= authlevel) as item}
|
|
||||||
<NavItem>
|
|
||||||
<Dropdown nav inNavbar>
|
|
||||||
<DropdownToggle nav caret>
|
<DropdownToggle nav caret>
|
||||||
<Icon name={item.icon}/> {item.title}
|
{menu}
|
||||||
</DropdownToggle>
|
</DropdownToggle>
|
||||||
<DropdownMenu>
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
{#each clusters as cluster}
|
{#each views.filter((item) => item.requiredRole <= authlevel && item.menu == menu) as item}
|
||||||
<DropdownItem href={item.href + cluster.name} active={window.location.pathname == item.href + cluster.name}>
|
<Dropdown nav inNavbar>
|
||||||
{cluster.name}
|
<DropdownToggle nav caret>
|
||||||
</DropdownItem>
|
<Icon name={item.icon} />
|
||||||
|
{item.title}
|
||||||
|
</DropdownToggle>
|
||||||
|
<DropdownMenu class="dropdown-menu-lg-end">
|
||||||
|
{#each clusters as cluster}
|
||||||
|
<DropdownItem
|
||||||
|
href={item.href + cluster.name}
|
||||||
|
active={window.location
|
||||||
|
.pathname ==
|
||||||
|
item.href + cluster.name}
|
||||||
|
>
|
||||||
|
{cluster.name}
|
||||||
|
</DropdownItem>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
{/each}
|
{/each}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</NavItem>
|
{/each}
|
||||||
{/each}
|
{/if}
|
||||||
</Nav>
|
</Nav>
|
||||||
</Collapse>
|
</Collapse>
|
||||||
<div class="d-flex">
|
<Nav>
|
||||||
<form method="GET" action="/search">
|
<NavItem>
|
||||||
<InputGroup>
|
<form method="GET" action="/search">
|
||||||
<Input type="text" placeholder="Search 'type:<query>' ..." name="searchId"/>
|
<InputGroup>
|
||||||
<Button outline type="submit"><Icon name="search"/></Button>
|
<Input
|
||||||
<InputGroupText style="cursor:help;" title={(authlevel >= roles.support) ? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | arrayJobId | username | name" : "Example: 'jobName:myjob', Types are jobId | jobName | projectId | arrayJobId "}><Icon name="info-circle"/></InputGroupText>
|
type="text"
|
||||||
</InputGroup>
|
placeholder="Search 'type:<query>' ..."
|
||||||
</form>
|
name="searchId"
|
||||||
{#if username}
|
/>
|
||||||
<form method="POST" action="/logout">
|
<Button outline type="submit"><Icon name="search" /></Button
|
||||||
<Button outline color="success" type="submit" style="margin-left: 10px;">
|
>
|
||||||
<Icon name="box-arrow-right"/> Logout {username}
|
<InputGroupText
|
||||||
</Button>
|
style="cursor:help;"
|
||||||
|
title={authlevel >= roles.support
|
||||||
|
? "Example: 'projectId:a100cd', Types are: jobId | jobName | projectId | arrayJobId | username | name"
|
||||||
|
: "Example: 'jobName:myjob', Types are jobId | jobName | projectId | arrayJobId "}
|
||||||
|
><Icon name="info-circle" /></InputGroupText
|
||||||
|
>
|
||||||
|
</InputGroup>
|
||||||
</form>
|
</form>
|
||||||
|
</NavItem>
|
||||||
|
{#if username}
|
||||||
|
<NavItem>
|
||||||
|
<form method="POST" action="/logout">
|
||||||
|
<Button
|
||||||
|
outline
|
||||||
|
color="success"
|
||||||
|
type="submit"
|
||||||
|
style="margin-left: 10px;"
|
||||||
|
>
|
||||||
|
{#if screenSize > 1630}
|
||||||
|
<Icon name="box-arrow-right" /> Logout {username}
|
||||||
|
{:else}
|
||||||
|
<Icon name="box-arrow-right" />
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</NavItem>
|
||||||
{/if}
|
{/if}
|
||||||
<Button outline on:click={() => window.location.href = '/config'} style="margin-left: 10px;">
|
<NavItem>
|
||||||
<Icon name="gear"/>
|
<Button
|
||||||
</Button>
|
outline
|
||||||
</div>
|
on:click={() => (window.location.href = "/config")}
|
||||||
|
style="margin-left: 10px;"
|
||||||
|
>
|
||||||
|
<Icon name="gear" />
|
||||||
|
</Button>
|
||||||
|
</NavItem>
|
||||||
|
</Nav>
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user