implement dark mode
@ -10,4 +10,5 @@ def settings_and_registry(request):
|
||||
return {
|
||||
'settings': django_settings,
|
||||
'registry': registry,
|
||||
'preferences': request.user.config,
|
||||
}
|
||||
|
1
netbox/project-static/.prettierignore
Normal file
@ -0,0 +1 @@
|
||||
dist
|
@ -65,14 +65,14 @@ To bundle only CSS files, run:
|
||||
|
||||
```bash
|
||||
# netbox/project-static
|
||||
yarn bundle:css
|
||||
yarn bundle --styles
|
||||
```
|
||||
|
||||
To bundle only JS files, run:
|
||||
|
||||
```bash
|
||||
# netbox/project-static
|
||||
yarn bundle:js
|
||||
yarn bundle --scripts
|
||||
```
|
||||
|
||||
Or, to bundle both, run:
|
||||
|
55
netbox/project-static/bundle.js
Normal file
@ -0,0 +1,55 @@
|
||||
const Bundler = require('parcel-bundler');
|
||||
|
||||
const options = {
|
||||
watch: false,
|
||||
minify: true,
|
||||
outDir: './dist',
|
||||
publicUrl: '/static',
|
||||
logLevel: 2,
|
||||
cache: true,
|
||||
};
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.includes('--no-cache')) {
|
||||
options.cache = false;
|
||||
}
|
||||
|
||||
const styles = [
|
||||
['main.scss', 'netbox.css'],
|
||||
['rack_elevation.scss', 'rack_elevation.css'],
|
||||
];
|
||||
|
||||
const scripts = [
|
||||
['src/index.ts', 'netbox.js'],
|
||||
['src/jobs.ts', 'jobs.js'],
|
||||
['src/device/lldp.ts', 'lldp.js'],
|
||||
['src/device/config.ts', 'config.js'],
|
||||
['src/device/status.ts', 'status.js'],
|
||||
];
|
||||
|
||||
async function bundleStyles() {
|
||||
for (const [input, outFile] of styles) {
|
||||
const instance = new Bundler(input, { outFile, ...options });
|
||||
await instance.bundle();
|
||||
}
|
||||
}
|
||||
|
||||
async function bundleScripts() {
|
||||
for (const [input, outFile] of scripts) {
|
||||
const instance = new Bundler(input, { outFile, ...options });
|
||||
await instance.bundle();
|
||||
}
|
||||
}
|
||||
|
||||
async function bundleAll() {
|
||||
if (args.includes('--styles')) {
|
||||
return await bundleStyles();
|
||||
} else if (args.includes('--scripts')) {
|
||||
return await bundleScripts();
|
||||
}
|
||||
await bundleStyles();
|
||||
await bundleScripts();
|
||||
}
|
||||
|
||||
bundleAll();
|
@ -1,44 +0,0 @@
|
||||
$choices-font-size-lg: $form-select-font-size-lg;
|
||||
$choices-font-size-md: $form-select-font-size;
|
||||
$choices-font-size-sm: $form-select-font-size-sm;
|
||||
$choices-guttering: $form-select-padding-y;
|
||||
$choices-border-radius: $form-select-border-radius;
|
||||
$choices-bg-color: $form-select-bg;
|
||||
$choices-bg-color-disabled: $form-select-disabled-bg;
|
||||
$choices-bg-color-dropdown: $form-select-bg;
|
||||
$choices-text-color: $form-select-color;
|
||||
$choices-keyline-color: $form-select-border-color;
|
||||
$choices-primary-color: $primary;
|
||||
$choices-disabled-color: $form-select-disabled-color;
|
||||
$choices-highlight-color: $choices-primary-color;
|
||||
$choices-button-dimension: $form-select-bg-size;
|
||||
|
||||
.choices {
|
||||
.choices__list--dropdown .choices__item--selectable.is-highlighted[class] {
|
||||
background-color: $primary;
|
||||
color: white;
|
||||
}
|
||||
|
||||
// Floating-input adjusts the z-index of the label. This fixes an issue where if there are two
|
||||
// floating inputs on top of eachother, the label of an overlapping field is visible inside the
|
||||
// dropdown's dropdown list.
|
||||
&.is-open .choices__list--dropdown {
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
|
||||
.choices[data-type*='select-one'] select.choices__input {
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
select[data-ssid] {
|
||||
display: block !important;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
}
|
BIN
netbox/project-static/dist/config.js
vendored
BIN
netbox/project-static/dist/config.js.map
vendored
BIN
netbox/project-static/dist/jobs.js
vendored
BIN
netbox/project-static/dist/jobs.js.map
vendored
BIN
netbox/project-static/dist/lldp.js
vendored
BIN
netbox/project-static/dist/lldp.js.map
vendored
BIN
netbox/project-static/dist/materialdesignicons-webfont.e8effb94.woff2
vendored
Normal file
BIN
netbox/project-static/dist/netbox.css
vendored
2
netbox/project-static/dist/netbox.css.map
vendored
BIN
netbox/project-static/dist/netbox.js
vendored
BIN
netbox/project-static/dist/netbox.js.map
vendored
BIN
netbox/project-static/dist/rack_elevation.css
vendored
BIN
netbox/project-static/dist/status.js
vendored
BIN
netbox/project-static/dist/status.js.map
vendored
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 6.8 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 6.2 KiB |
@ -1,9 +1,21 @@
|
||||
@import './theme.scss';
|
||||
@import './theme-light.scss';
|
||||
@import './bootstrap.scss';
|
||||
|
||||
@import './node_modules/@mdi/font/css/materialdesignicons.min.css';
|
||||
@import '@mdi/font/css/materialdesignicons.min.css';
|
||||
|
||||
@import './select.scss';
|
||||
@import './node_modules/flatpickr/dist/flatpickr.min.css';
|
||||
@import 'flatpickr/dist/flatpickr.css';
|
||||
|
||||
@import './netbox.scss';
|
||||
|
||||
body[data-netbox-color-mode='dark'] {
|
||||
@import './theme-dark.scss';
|
||||
@import './bootstrap.scss';
|
||||
|
||||
@import '@mdi/font/css/materialdesignicons.min.css';
|
||||
|
||||
@import './select.scss';
|
||||
@import 'flatpickr/dist/flatpickr.css';
|
||||
|
||||
@import './netbox.scss';
|
||||
}
|
||||
|
@ -1,3 +1,101 @@
|
||||
:root {
|
||||
--nbx-logo-color-1: #9cc8f8;
|
||||
--nbx-logo-color-2: #1685fc;
|
||||
--nbx-sidebar-bg: #{$gray-100};
|
||||
--nbx-sidebar-link-color: #{$gray-800};
|
||||
--nbx-sidebar-link-hover-bg: #{$blue-100};
|
||||
--nbx-sidebar-title-color: #{$text-muted};
|
||||
--nbx-breadcrumb-bg: #{$light};
|
||||
--nbx-body-bg: #{$white};
|
||||
--nbx-body-color: #{$black};
|
||||
--nbx-pre-bg: #{$gray-100};
|
||||
--nbx-pre-border-color: #{$gray-600};
|
||||
--nbx-change-added: #{rgba($green, 0.4)};
|
||||
--nbx-change-removed: #{rgba($red, 0.4)};
|
||||
--nbx-cable-node-bg: #{$gray-100};
|
||||
--nbx-cable-node-border-color: #{$gray-200};
|
||||
--nbx-cable-termination-bg: #{$gray-200};
|
||||
--nbx-cable-termination-border-color: #{$gray-300};
|
||||
--nbx-elevation-slot-bg: #{$gray-100};
|
||||
|
||||
body[data-netbox-color-mode='dark'] {
|
||||
--nbx-logo-color-1: #{$white};
|
||||
--nbx-logo-color-2: #{$gray-200};
|
||||
--nbx-sidebar-bg: #{$gray-800};
|
||||
--nbx-sidebar-link-color: #{$gray-200};
|
||||
--nbx-sidebar-link-hover-bg: #{rgba($blue-300, 0.15)};
|
||||
--nbx-sidebar-title-color: #{$gray-300};
|
||||
--nbx-breadcrumb-bg: #{$gray-800};
|
||||
--nbx-body-bg: #{$gray-900};
|
||||
--nbx-body-color: #{$white};
|
||||
--nbx-pre-bg: #{$gray-700};
|
||||
--nbx-pre-border-color: #{$gray-600};
|
||||
--nbx-change-added: #{rgba($green-300, 0.4)};
|
||||
--nbx-change-removed: #{rgba($red-300, 0.4)};
|
||||
--nbx-cable-node-bg: #{$gray-700};
|
||||
--nbx-cable-node-border-color: #{$gray-600};
|
||||
--nbx-cable-termination-bg: #{$gray-800};
|
||||
--nbx-cable-termination-border-color: #{$gray-700};
|
||||
--nbx-elevation-slot-bg: #{$gray-700};
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
transition: background-color, color 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--nbx-body-bg);
|
||||
color: var(--nbx-body-color);
|
||||
g#netbox-logo-1 {
|
||||
fill: #9cc8f8;
|
||||
stroke: #9cc8f8;
|
||||
}
|
||||
|
||||
g#netbox-logo-2 {
|
||||
fill: #1685fc;
|
||||
stroke: #1685fc;
|
||||
}
|
||||
&[data-netbox-color-mode='light'] {
|
||||
.btn.btn-primary {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
&[data-netbox-color-mode='dark'] {
|
||||
a:not(.btn) {
|
||||
color: $blue-300;
|
||||
}
|
||||
.breadcrumb .breadcrumb-item > a {
|
||||
color: $blue-300;
|
||||
}
|
||||
.badge {
|
||||
color: $black;
|
||||
}
|
||||
.card,
|
||||
.sidebar {
|
||||
.text-muted {
|
||||
color: $gray-400 !important;
|
||||
}
|
||||
}
|
||||
.text-body[class] {
|
||||
color: var(--nbx-body-color) !important;
|
||||
}
|
||||
g#netbox-logo-1 {
|
||||
fill: $white;
|
||||
stroke: $white;
|
||||
}
|
||||
|
||||
g#netbox-logo-2 {
|
||||
fill: $gray-200;
|
||||
stroke: $gray-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nav.search {
|
||||
background-color: var(--nbx-body-bg);
|
||||
}
|
||||
|
||||
main.login-container {
|
||||
display: flex;
|
||||
height: calc(100vh - 4rem);
|
||||
@ -20,6 +118,37 @@ footer.login-footer {
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: $font-weight-bolder;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4 {
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
h5,
|
||||
h6 {
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
|
||||
h1.accordion-item-title,
|
||||
h2.accordion-item-title,
|
||||
h3.accordion-item-title,
|
||||
h4.accordion-item-title,
|
||||
h5.accordion-item-title,
|
||||
h6.accordion-item-title {
|
||||
padding: 0 0.5rem;
|
||||
font-weight: $font-weight-bold;
|
||||
text-transform: uppercase;
|
||||
color: var(--nbx-sidebar-title-color);
|
||||
font-size: $font-size-sm;
|
||||
}
|
||||
|
||||
.form-login {
|
||||
width: 100%;
|
||||
max-width: 330px;
|
||||
@ -53,16 +182,6 @@ li.dropdown-item.dropdown-item-btns {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100; /* Behind the navbar */
|
||||
// padding: 48px 0 0; /* Height of navbar */
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.sidebar {
|
||||
top: 5rem;
|
||||
@ -84,9 +203,28 @@ li.dropdown-item.dropdown-item-btns {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
nav.nav.nav-pills {
|
||||
.nav-item.nav-link {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: $font-size-base;
|
||||
border-radius: $border-radius;
|
||||
&:hover {
|
||||
color: $body-color;
|
||||
background-color: var(--nbx-sidebar-link-hover-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 100; /* Behind the navbar */
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
|
||||
background-color: var(--nbx-sidebar-bg);
|
||||
.sidebar-nav-link {
|
||||
color: $gray-800;
|
||||
color: var(--nbx-sidebar-link-color);
|
||||
}
|
||||
.accordion-body {
|
||||
max-height: calc(100vh - 24rem);
|
||||
@ -95,10 +233,11 @@ li.dropdown-item.dropdown-item-btns {
|
||||
.nav-link {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
.nav-link:hover {
|
||||
background-color: $blue-100;
|
||||
border-radius: $border-radius;
|
||||
&:hover {
|
||||
color: $body-color;
|
||||
background-color: var(--nbx-sidebar-link-hover-bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,7 +249,7 @@ li.dropdown-item.dropdown-item-btns {
|
||||
padding-right: 0.5rem;
|
||||
position: sticky;
|
||||
height: 8rem;
|
||||
background-color: $light;
|
||||
background-color: var(--nbx-sidebar-bg);
|
||||
box-shadow: inset -1px 0 0 rgba(0, 0, 0, 0.1);
|
||||
.nav-link {
|
||||
padding: 0.5rem 0.25rem;
|
||||
@ -132,19 +271,6 @@ li.dropdown-item.dropdown-item-btns {
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
h1.accordion-item-title,
|
||||
h2.accordion-item-title,
|
||||
h3.accordion-item-title,
|
||||
h4.accordion-item-title,
|
||||
h5.accordion-item-title,
|
||||
h6.accordion-item-title {
|
||||
padding: 0 0.5rem;
|
||||
font-weight: $font-weight-bold;
|
||||
text-transform: uppercase;
|
||||
color: $text-muted;
|
||||
font-size: $font-size-sm;
|
||||
}
|
||||
|
||||
#object-type-selector {
|
||||
button.dropdown-item,
|
||||
h6.dropdown-header {
|
||||
@ -167,8 +293,8 @@ span.color-label {
|
||||
|
||||
pre {
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid $gray-200;
|
||||
background-color: $gray-100;
|
||||
border: 1px solid var(--nbx-pre-border-color);
|
||||
background-color: var(--nbx-pre-bg);
|
||||
padding: $spacer;
|
||||
white-space: pre;
|
||||
}
|
||||
@ -273,7 +399,6 @@ table tr.vertical-align {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
// Pad all adjacent cards
|
||||
.card:not(:only-of-type) {
|
||||
margin-bottom: $spacer;
|
||||
}
|
||||
@ -285,9 +410,9 @@ table tr.vertical-align {
|
||||
nav.breadcrumb-container {
|
||||
padding: $badge-padding-y $badge-padding-x;
|
||||
border-radius: $border-radius;
|
||||
background-color: $light;
|
||||
font-size: $font-size-sm;
|
||||
width: fit-content;
|
||||
background-color: var(--nbx-breadcrumb-bg);
|
||||
|
||||
ol.breadcrumb {
|
||||
margin-bottom: 0;
|
||||
@ -310,21 +435,6 @@ div.paginator > form > div.input-group {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
button.btn.btn-outline-gray.dropdown-toggle:after {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
// Apply bootstrap focus styling to Choices.JS elements.
|
||||
div.choices.is-focused > div.choices__inner {
|
||||
border-color: $form-select-focus-border-color;
|
||||
outline: 0;
|
||||
@if $enable-shadows {
|
||||
@include box-shadow($form-select-box-shadow, $form-select-focus-box-shadow);
|
||||
} @else {
|
||||
box-shadow: $form-select-focus-box-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
div.field-group:not(:first-of-type) {
|
||||
margin-top: $spacer * 3;
|
||||
|
||||
@ -365,8 +475,13 @@ span.bi-plus:before {
|
||||
font-weight: $font-weight-bold !important;
|
||||
}
|
||||
|
||||
table tbody tr.success {
|
||||
background-color: rgba($success, 0.15);
|
||||
table tbody {
|
||||
@each $color, $value in $theme-colors {
|
||||
tr.#{$color} {
|
||||
background-color: rgba($value, 0.15);
|
||||
border-color: $gray-500;
|
||||
}
|
||||
}
|
||||
}
|
||||
table td,
|
||||
table th {
|
||||
@ -380,16 +495,16 @@ table th {
|
||||
text-align: center;
|
||||
}
|
||||
.cable-trace .node {
|
||||
background-color: $gray-100;
|
||||
border: $border-width solid $gray-200;
|
||||
background-color: var(--nbx-cable-node-bg);
|
||||
border: $border-width solid var(--nbx-cable-node-border-color);
|
||||
border-radius: $border-radius;
|
||||
padding: 1.5rem 1rem;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.cable-trace .termination {
|
||||
background-color: $gray-200;
|
||||
border: $border-width solid $gray-300;
|
||||
background-color: var(--nbx-cable-termination-bg);
|
||||
border: $border-width solid var(--nbx-cable-termination-border-color);
|
||||
box-shadow: $box-shadow;
|
||||
border-radius: $border-radius;
|
||||
margin: -1rem auto;
|
||||
@ -399,7 +514,7 @@ table th {
|
||||
z-index: 2;
|
||||
}
|
||||
.cable-trace .active {
|
||||
border: 0.25rem solid $green;
|
||||
border: 0.25rem solid $success;
|
||||
}
|
||||
.cable-trace .cable {
|
||||
border-left-style: solid;
|
||||
@ -422,10 +537,10 @@ pre.change-data {
|
||||
padding-left: $spacer;
|
||||
padding-right: $spacer;
|
||||
&.added {
|
||||
background-color: rgba($green, 0.4);
|
||||
background-color: var(--nbx-change-added);
|
||||
}
|
||||
&.removed {
|
||||
background-color: rgba($red, 0.4);
|
||||
background-color: var(--nbx-change-removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -433,10 +548,10 @@ pre.change-data {
|
||||
pre.change-diff {
|
||||
border-color: transparent;
|
||||
&.change-removed {
|
||||
background-color: rgba($red, 0.4);
|
||||
background-color: var(--nbx-change-removed);
|
||||
}
|
||||
&.change-added {
|
||||
background-color: rgba($green, 0.4);
|
||||
background-color: var(--nbx-change-added);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,7 @@
|
||||
"main": "dist/netbox.js",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"bundle:css": "parcel build --public-url /static -o netbox.css main.scss && parcel build --public-url /static -o rack_elevation.css rack_elevation.scss",
|
||||
"bundle:js": "parcel build --public-url /static -o netbox.js src/index.ts && parcel build --public-url /static -o jobs.js src/jobs.ts && parcel build --public-url /static -o lldp.js src/device/lldp.ts && parcel build --public-url /static -o config.js src/device/config.ts && parcel build --public-url /static -o status.js src/device/status.ts",
|
||||
"bundle": "yarn bundle:css && yarn bundle:js"
|
||||
"bundle": "node bundle.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mdi/font": "^5.9.55",
|
||||
|
@ -1,5 +1,6 @@
|
||||
/* Stylesheet for rendering SVG rack elevations */
|
||||
@import './theme.scss';
|
||||
@import './theme-light.scss';
|
||||
|
||||
* {
|
||||
font-family: $font-family-sans-serif;
|
||||
font-size: $font-size-sm;
|
||||
@ -11,59 +12,92 @@ text {
|
||||
text-anchor: middle;
|
||||
dominant-baseline: middle;
|
||||
}
|
||||
.rack {
|
||||
background-color: $gray-100;
|
||||
fill: none;
|
||||
stroke: black;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.slot {
|
||||
fill: $gray-200;
|
||||
stroke: $gray-500;
|
||||
}
|
||||
.slot:hover {
|
||||
fill: $white;
|
||||
}
|
||||
.slot + .add-device {
|
||||
fill: none;
|
||||
}
|
||||
.slot:hover + .add-device {
|
||||
fill: $primary;
|
||||
}
|
||||
.add-device:hover {
|
||||
fill: $primary;
|
||||
}
|
||||
.add-device:hover + .slot {
|
||||
fill: $white;
|
||||
}
|
||||
.reserved {
|
||||
fill: url(#reserved);
|
||||
}
|
||||
.reserved:hover {
|
||||
fill: url(#reserved);
|
||||
}
|
||||
.occupied {
|
||||
fill: url(#occupied);
|
||||
}
|
||||
.occupied:hover {
|
||||
fill: url(#occupied);
|
||||
}
|
||||
.blocked {
|
||||
fill: url(#blocked);
|
||||
}
|
||||
.blocked:hover {
|
||||
fill: url(#blocked);
|
||||
}
|
||||
.blocked:hover + .add-device {
|
||||
fill: none;
|
||||
}
|
||||
.unit {
|
||||
margin: 0;
|
||||
padding: 5px 0px;
|
||||
fill: $gray-400;
|
||||
font-size: $font-size-sm;
|
||||
font-family: $font-family-sans-serif;
|
||||
}
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
|
||||
svg {
|
||||
.rack {
|
||||
background-color: $gray-100;
|
||||
fill: none;
|
||||
stroke: $body-color;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.slot {
|
||||
fill: $gray-100;
|
||||
stroke: $gray-500;
|
||||
&:hover {
|
||||
fill: $gray-50;
|
||||
}
|
||||
& + .add-device {
|
||||
fill: none;
|
||||
}
|
||||
&:hover + .add-device {
|
||||
fill: $blue;
|
||||
}
|
||||
& .add-device {
|
||||
&:hover {
|
||||
fill: $blue;
|
||||
}
|
||||
&:hover + .slot {
|
||||
fill: $white;
|
||||
}
|
||||
}
|
||||
&.reserved[class] {
|
||||
fill: url(#reserved);
|
||||
}
|
||||
&.reserved:hover[class] {
|
||||
fill: url(#reserved);
|
||||
}
|
||||
&.occupied[class] {
|
||||
fill: url(#occupied);
|
||||
}
|
||||
&.occupied:hover[class] {
|
||||
fill: url(#occupied);
|
||||
}
|
||||
&.blocked[class] {
|
||||
fill: url(#blocked);
|
||||
}
|
||||
&.blocked:hover[class] {
|
||||
fill: url(#blocked);
|
||||
}
|
||||
&.blocked:hover + .add-device {
|
||||
fill: none;
|
||||
}
|
||||
}
|
||||
|
||||
.unit {
|
||||
margin: 0;
|
||||
padding: 5px 0px;
|
||||
fill: $gray-400;
|
||||
font-size: $font-size-sm;
|
||||
font-family: $font-family-sans-serif;
|
||||
}
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
&[data-netbox-color-mode='dark'] {
|
||||
.rack {
|
||||
background-color: $gray-800;
|
||||
}
|
||||
.slot {
|
||||
fill: $gray-700;
|
||||
stroke: $gray-400;
|
||||
&:hover {
|
||||
fill: $gray-600;
|
||||
}
|
||||
& + .add-device {
|
||||
fill: none;
|
||||
}
|
||||
&:hover + .add-device {
|
||||
fill: $blue-300;
|
||||
}
|
||||
}
|
||||
.add-device {
|
||||
&:hover {
|
||||
fill: $blue-300;
|
||||
}
|
||||
&:hover + .slot {
|
||||
fill: $black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ div.form-floating div.ss-main div.ss-multi-selected {
|
||||
@import './node_modules/slim-select/src/slim-select/slimselect.scss';
|
||||
|
||||
.ss-main {
|
||||
color: $form-select-color;
|
||||
|
||||
&.is-invalid .ss-single-selected,
|
||||
&.is-invalid .ss-multi-selected {
|
||||
border-color: $form-feedback-icon-invalid-color;
|
||||
@ -39,6 +41,7 @@ div.form-floating div.ss-main div.ss-multi-selected {
|
||||
}
|
||||
|
||||
.ss-single-selected {
|
||||
background-color: $form-select-bg;
|
||||
span.ss-arrow {
|
||||
// Inherit the arrow color from the parent (see color selector).
|
||||
span.arrow-down,
|
||||
@ -56,6 +59,7 @@ div.form-floating div.ss-main div.ss-multi-selected {
|
||||
align-items: center;
|
||||
padding-left: $input-padding-x;
|
||||
padding-right: $input-padding-x;
|
||||
background-color: $form-select-bg;
|
||||
|
||||
.ss-values {
|
||||
padding-top: $spacer * 2 !important;
|
||||
@ -69,7 +73,15 @@ div.form-floating div.ss-main div.ss-multi-selected {
|
||||
}
|
||||
|
||||
.ss-content {
|
||||
background-color: $gray-900;
|
||||
.ss-list {
|
||||
.ss-option.ss-option-selected {
|
||||
background-color: $gray-600;
|
||||
color: $white;
|
||||
}
|
||||
.ss-option:hover {
|
||||
background-color: $blue-400;
|
||||
}
|
||||
.ss-option:last-child {
|
||||
border-bottom-left-radius: $form-select-border-radius;
|
||||
border-bottom-right-radius: $form-select-border-radius;
|
||||
@ -79,6 +91,7 @@ div.form-floating div.ss-main div.ss-multi-selected {
|
||||
border-bottom-right-radius: $form-select-border-radius;
|
||||
.ss-search {
|
||||
input[type='search'] {
|
||||
background-color: $form-select-bg;
|
||||
border: $form-select-border-width solid $form-select-border-color;
|
||||
&:focus {
|
||||
border-color: $form-select-focus-border-color;
|
||||
|
134
netbox/project-static/src/colorMode.ts
Normal file
@ -0,0 +1,134 @@
|
||||
import { getElements, isTruthy } from './util';
|
||||
|
||||
type ColorMode = 'light' | 'dark';
|
||||
type ColorModePreference = ColorMode | 'none';
|
||||
|
||||
const COLOR_MODE_KEY = 'netbox-color-mode';
|
||||
const TEXT_WHEN_DARK = 'Light Mode';
|
||||
const TEXT_WHEN_LIGHT = 'Dark Mode';
|
||||
const ICON_WHEN_DARK = 'mdi-lightbulb-on';
|
||||
const ICON_WHEN_LIGHT = 'mdi-lightbulb';
|
||||
|
||||
function isColorMode(value: string): value is ColorMode {
|
||||
return value === 'dark' || value === 'light';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the color mode to light or dark.
|
||||
*
|
||||
* @param mode `'light'` or `'dark'`
|
||||
* @returns `true` if the color mode was successfully set, `false` if not.
|
||||
*/
|
||||
function storeColorMode(mode: ColorMode): void {
|
||||
return localStorage.setItem(COLOR_MODE_KEY, mode);
|
||||
}
|
||||
|
||||
function updateElements(targetMode: ColorMode): void {
|
||||
document.body.setAttribute(`data-${COLOR_MODE_KEY}`, targetMode);
|
||||
|
||||
for (const text of getElements<HTMLSpanElement>('span.color-mode-text')) {
|
||||
if (targetMode === 'light') {
|
||||
text.innerText = TEXT_WHEN_LIGHT;
|
||||
} else if (targetMode === 'dark') {
|
||||
text.innerText = TEXT_WHEN_DARK;
|
||||
}
|
||||
}
|
||||
for (const icon of getElements<HTMLSpanElement>('i.color-mode-icon', 'span.color-mode-icon')) {
|
||||
if (targetMode === 'light') {
|
||||
icon.classList.remove(ICON_WHEN_DARK);
|
||||
icon.classList.add(ICON_WHEN_LIGHT);
|
||||
} else if (targetMode === 'dark') {
|
||||
icon.classList.remove(ICON_WHEN_LIGHT);
|
||||
icon.classList.add(ICON_WHEN_DARK);
|
||||
}
|
||||
}
|
||||
|
||||
for (const elevation of getElements<HTMLObjectElement>('.rack_elevation')) {
|
||||
const svg = elevation.contentDocument?.querySelector('svg') ?? null;
|
||||
if (svg !== null) {
|
||||
svg.setAttribute(`data-${COLOR_MODE_KEY}`, targetMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call all functions necessary to update the color mode across the UI.
|
||||
*
|
||||
* @param mode Target color mode.
|
||||
*/
|
||||
function setColorMode(mode: ColorMode): void {
|
||||
for (const func of [storeColorMode, updateElements]) {
|
||||
func(mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the color mode when a color mode toggle is clicked.
|
||||
*/
|
||||
function handleColorModeToggle(): void {
|
||||
const currentValue = localStorage.getItem(COLOR_MODE_KEY);
|
||||
if (currentValue === 'light') {
|
||||
setColorMode('dark');
|
||||
} else if (currentValue === 'dark') {
|
||||
setColorMode('light');
|
||||
} else {
|
||||
console.warn('Unable to determine the current color mode');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the user's preference and set it as the color mode.
|
||||
*/
|
||||
function defaultColorMode(): void {
|
||||
// Get the current color mode value from local storage.
|
||||
const currentValue = localStorage.getItem(COLOR_MODE_KEY) as Nullable<ColorMode>;
|
||||
const bodyValue = document.body.getAttribute(`data-${COLOR_MODE_KEY}`);
|
||||
|
||||
if (isTruthy(bodyValue) && isTruthy(currentValue)) {
|
||||
return setColorMode(currentValue);
|
||||
}
|
||||
|
||||
let preference: ColorModePreference = 'none';
|
||||
|
||||
// Determine if the user prefers dark or light mode.
|
||||
for (const mode of ['dark', 'light']) {
|
||||
if (window.matchMedia(`(prefers-color-scheme: ${mode})`).matches) {
|
||||
preference = mode as ColorModePreference;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTruthy(currentValue) && !isTruthy(bodyValue) && isColorMode(currentValue)) {
|
||||
return setColorMode(currentValue);
|
||||
}
|
||||
|
||||
switch (preference) {
|
||||
case 'dark':
|
||||
return setColorMode('dark');
|
||||
case 'light':
|
||||
return setColorMode('light');
|
||||
case 'none':
|
||||
return setColorMode('light');
|
||||
default:
|
||||
return setColorMode('light');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize color mode toggle buttons and set the default color mode.
|
||||
*/
|
||||
function initColorModeToggle(): void {
|
||||
for (const element of getElements<HTMLButtonElement>('button.color-mode-toggle')) {
|
||||
element.addEventListener('click', handleColorModeToggle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize all color mode elements.
|
||||
*/
|
||||
export function initColorMode(): void {
|
||||
window.addEventListener('load', defaultColorMode);
|
||||
for (const func of [initColorModeToggle]) {
|
||||
func();
|
||||
}
|
||||
}
|
@ -4,15 +4,16 @@ import { initSearch } from './search';
|
||||
import { initSelect } from './select';
|
||||
import { initButtons } from './buttons';
|
||||
import { initSecrets } from './secrets';
|
||||
import { initColorMode } from './colorMode';
|
||||
import { initMessages } from './messages';
|
||||
import { initClipboard } from './clipboard';
|
||||
import { initDateSelector } from './dateSelector';
|
||||
|
||||
import { initTableConfig } from './tableConfig';
|
||||
|
||||
function init() {
|
||||
for (const init of [
|
||||
initBootstrap,
|
||||
initColorMode,
|
||||
initMessages,
|
||||
initForms,
|
||||
initSearch,
|
||||
|
211
netbox/project-static/theme-base.scss
Normal file
@ -0,0 +1,211 @@
|
||||
@import 'bootstrap/scss/functions';
|
||||
|
||||
$alt: #13293d;
|
||||
$darker: #010101;
|
||||
|
||||
$gray: #6b7280;
|
||||
$red: #ef4444;
|
||||
$yellow: #f59e0b;
|
||||
$green: #10b981;
|
||||
$blue: #3b82f6;
|
||||
$purple: #8b5cf6;
|
||||
$pink: #ec4899;
|
||||
|
||||
$gray-50: #f9fafb;
|
||||
$gray-100: #f3f4f6;
|
||||
$gray-200: #e5e7eb;
|
||||
$gray-300: #d1d5db;
|
||||
$gray-400: #9ca3af;
|
||||
$gray-500: #6b7280;
|
||||
$gray-600: #4b5563;
|
||||
$gray-700: #374151;
|
||||
$gray-800: #1f2937;
|
||||
$gray-900: #111827;
|
||||
|
||||
$red-50: #fef2f2;
|
||||
$red-100: #fee2e2;
|
||||
$red-200: #fecaca;
|
||||
$red-300: #fca5a5;
|
||||
$red-400: #f87171;
|
||||
$red-500: #ef4444;
|
||||
$red-600: #dc2626;
|
||||
$red-700: #b91c1c;
|
||||
$red-800: #991b1b;
|
||||
$red-900: #7f1d1d;
|
||||
|
||||
$yellow-50: #fffbeb;
|
||||
$yellow-100: #fef3c7;
|
||||
$yellow-200: #fde68a;
|
||||
$yellow-300: #fcd34d;
|
||||
$yellow-400: #fbbf24;
|
||||
$yellow-500: #f59e0b;
|
||||
$yellow-600: #d97706;
|
||||
$yellow-700: #b45309;
|
||||
$yellow-800: #92400e;
|
||||
$yellow-900: #78350f;
|
||||
|
||||
$green-50: #ecfdf5;
|
||||
$green-100: #d1fae5;
|
||||
$green-200: #a7f3d0;
|
||||
$green-300: #6ee7b7;
|
||||
$green-400: #34d399;
|
||||
$green-500: #10b981;
|
||||
$green-600: #059669;
|
||||
$green-700: #047857;
|
||||
$green-800: #065f46;
|
||||
$green-900: #064e3b;
|
||||
|
||||
$blue-50: #eff6ff;
|
||||
$blue-100: #dbeafe;
|
||||
$blue-200: #bfdbfe;
|
||||
$blue-300: #93c5fd;
|
||||
$blue-400: #60a5fa;
|
||||
$blue-500: #3b82f6;
|
||||
$blue-600: #2563eb;
|
||||
$blue-700: #1d4ed8;
|
||||
$blue-800: #1e40af;
|
||||
$blue-900: #1e3a8a;
|
||||
|
||||
$indigo-50: #eef2ff;
|
||||
$indigo-100: #e0e7ff;
|
||||
$indigo-200: #c7d2fe;
|
||||
$indigo-300: #a5b4fc;
|
||||
$indigo-400: #818cf8;
|
||||
$indigo-500: #6366f1;
|
||||
$indigo-600: #4f46e5;
|
||||
$indigo-700: #4338ca;
|
||||
$indigo-800: #3730a3;
|
||||
$indigo-900: #312e81;
|
||||
|
||||
$purple-50: #f5f3ff;
|
||||
$purple-100: #ede9fe;
|
||||
$purple-200: #ddd6fe;
|
||||
$purple-300: #c4b5fd;
|
||||
$purple-400: #a78bfa;
|
||||
$purple-500: #8b5cf6;
|
||||
$purple-600: #7c3aed;
|
||||
$purple-700: #6d28d9;
|
||||
$purple-800: #5b21b6;
|
||||
$purple-900: #4c1d95;
|
||||
|
||||
$pink-50: #fdf2f8;
|
||||
$pink-100: #fce7f3;
|
||||
$pink-200: #fbcfe8;
|
||||
$pink-300: #f9a8d4;
|
||||
$pink-400: #f472b6;
|
||||
$pink-500: #ec4899;
|
||||
$pink-600: #db2777;
|
||||
$pink-700: #be185d;
|
||||
$pink-800: #9d174d;
|
||||
$pink-900: #831843;
|
||||
|
||||
$card-cap-bg: 'unset';
|
||||
|
||||
$border-radius-md: 0.375rem;
|
||||
$border-radius-lg: 0.5rem;
|
||||
$border-radius-xl: 0.75rem;
|
||||
$border-radius-2xl: 1.5rem;
|
||||
|
||||
$border-radius: $border-radius-lg;
|
||||
|
||||
$border-radius-sm: $border-radius;
|
||||
$border-radius-lg: $border-radius-xl;
|
||||
|
||||
$badge-border-radius: $border-radius-md;
|
||||
$progress-border-radius: $border-radius-md;
|
||||
|
||||
$font-weight-lighter: 200;
|
||||
$font-weight-medium: 600;
|
||||
$font-weight-bolder: 800;
|
||||
|
||||
$theme-color-addons: (
|
||||
'alt': $alt,
|
||||
'gray': $gray-400,
|
||||
'darker': $darker,
|
||||
'gray-50': $gray-50,
|
||||
'gray-100': $gray-100,
|
||||
'gray-200': $gray-200,
|
||||
'gray-300': $gray-300,
|
||||
'gray-400': $gray-400,
|
||||
'gray-500': $gray-500,
|
||||
'gray-600': $gray-600,
|
||||
'gray-700': $gray-700,
|
||||
'gray-800': $gray-800,
|
||||
'gray-900': $gray-900,
|
||||
'red-50': $red-50,
|
||||
'red-100': $red-100,
|
||||
'red-200': $red-200,
|
||||
'red-300': $red-300,
|
||||
'red-400': $red-400,
|
||||
'red-500': $red-500,
|
||||
'red-600': $red-600,
|
||||
'red-700': $red-700,
|
||||
'red-800': $red-800,
|
||||
'red-900': $red-900,
|
||||
'yellow-50': $yellow-50,
|
||||
'yellow-100': $yellow-100,
|
||||
'yellow-200': $yellow-200,
|
||||
'yellow-300': $yellow-300,
|
||||
'yellow-400': $yellow-400,
|
||||
'yellow-500': $yellow-500,
|
||||
'yellow-600': $yellow-600,
|
||||
'yellow-700': $yellow-700,
|
||||
'yellow-800': $yellow-800,
|
||||
'yellow-900': $yellow-900,
|
||||
'green-50': $green-50,
|
||||
'green-100': $green-100,
|
||||
'green-200': $green-200,
|
||||
'green-300': $green-300,
|
||||
'green-400': $green-400,
|
||||
'green-500': $green-500,
|
||||
'green-600': $green-600,
|
||||
'green-700': $green-700,
|
||||
'green-800': $green-800,
|
||||
'green-900': $green-900,
|
||||
'blue-50': $blue-50,
|
||||
'blue-100': $blue-100,
|
||||
'blue-200': $blue-200,
|
||||
'blue-300': $blue-300,
|
||||
'blue-400': $blue-400,
|
||||
'blue-500': $blue-500,
|
||||
'blue-600': $blue-600,
|
||||
'blue-700': $blue-700,
|
||||
'blue-800': $blue-800,
|
||||
'blue-900': $blue-900,
|
||||
'indigo-50': $indigo-50,
|
||||
'indigo-100': $indigo-100,
|
||||
'indigo-200': $indigo-200,
|
||||
'indigo-300': $indigo-300,
|
||||
'indigo-400': $indigo-400,
|
||||
'indigo-500': $indigo-500,
|
||||
'indigo-600': $indigo-600,
|
||||
'indigo-700': $indigo-700,
|
||||
'indigo-800': $indigo-800,
|
||||
'indigo-900': $indigo-900,
|
||||
'purple-50': $purple-50,
|
||||
'purple-100': $purple-100,
|
||||
'purple-200': $purple-200,
|
||||
'purple-300': $purple-300,
|
||||
'purple-400': $purple-400,
|
||||
'purple-500': $purple-500,
|
||||
'purple-600': $purple-600,
|
||||
'purple-700': $purple-700,
|
||||
'purple-800': $purple-800,
|
||||
'purple-900': $purple-900,
|
||||
'pink-50': $pink-50,
|
||||
'pink-100': $pink-100,
|
||||
'pink-200': $pink-200,
|
||||
'pink-300': $pink-300,
|
||||
'pink-400': $pink-400,
|
||||
'pink-500': $pink-500,
|
||||
'pink-600': $pink-600,
|
||||
'pink-700': $pink-700,
|
||||
'pink-800': $pink-800,
|
||||
'pink-900': $pink-900,
|
||||
);
|
||||
|
||||
$font-family-sans-serif: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
|
||||
'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
$font-family-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
|
||||
'Courier New', monospace;
|
292
netbox/project-static/theme-dark.scss
Normal file
@ -0,0 +1,292 @@
|
||||
@import './theme-base.scss';
|
||||
|
||||
$primary: $blue-300;
|
||||
$secondary: $gray-400;
|
||||
$success: $green-300;
|
||||
$info: $cyan-300;
|
||||
$warning: $yellow-300;
|
||||
$danger: $red-300;
|
||||
$light: $gray-300;
|
||||
$dark: $gray-400;
|
||||
|
||||
$theme-colors: (
|
||||
'primary': $primary,
|
||||
'secondary': $secondary,
|
||||
'success': $success,
|
||||
'info': $info,
|
||||
'warning': $warning,
|
||||
'danger': $danger,
|
||||
'light': $light,
|
||||
'dark': $dark,
|
||||
);
|
||||
|
||||
$theme-color-addons-dark: (
|
||||
'alt': #13293d,
|
||||
'darker': #010101,
|
||||
);
|
||||
|
||||
$theme-colors: map-merge($theme-colors, $theme-color-addons);
|
||||
$theme-color-addons: map-merge($theme-color-addons, $theme-color-addons-dark);
|
||||
|
||||
// On import, any variables marked `!default` will be overridden by the above.
|
||||
@import 'bootstrap/scss/variables';
|
||||
|
||||
// Customize the light and dark text colors for use in our color contrast function.
|
||||
|
||||
// Gradient
|
||||
$gradient: linear-gradient(180deg, rgba($white, 0.15), rgba($white, 0));
|
||||
|
||||
// Body
|
||||
$body-bg: $gray-900;
|
||||
$body-color: $white;
|
||||
$body-text-align: null;
|
||||
$border-color: $gray-700;
|
||||
$box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
|
||||
$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);
|
||||
$box-shadow-lg: 0 1rem 3rem rgba($black, 0.175);
|
||||
$box-shadow-inset: inset 0 1px 2px rgba($black, 0.075);
|
||||
// $component-active-color: $white;
|
||||
// $component-active-bg: $primary;
|
||||
$text-muted: $gray-500;
|
||||
$blockquote-footer-color: $gray-600;
|
||||
$mark-bg: #fcf8e3;
|
||||
|
||||
// Tables
|
||||
$table-color: $gray-100;
|
||||
$table-border-color: $border-color;
|
||||
// $table-bg: transparent;
|
||||
$table-striped-color: $table-color;
|
||||
$table-striped-bg: rgba($white, $table-striped-bg-factor);
|
||||
$table-active-color: $table-color;
|
||||
$table-active-bg: rgba($white, $table-active-bg-factor);
|
||||
$table-hover-color: $table-color;
|
||||
$table-hover-bg: rgba($white, $table-hover-bg-factor);
|
||||
// $table-group-separator-color: currentColor;
|
||||
|
||||
// Buttons + Forms
|
||||
// $input-btn-focus-color: rgba($component-active-bg, $input-btn-focus-color-opacity);
|
||||
// $input-btn-focus-box-shadow: 0 0 0 $input-btn-focus-width $input-btn-focus-color;
|
||||
|
||||
// Buttons
|
||||
$btn-box-shadow: inset 0 1px 0 rgba($black, 0.15), 0 1px 1px rgba($white, 0.075);
|
||||
$btn-active-box-shadow: inset 0 3px 5px rgba($white, 0.125);
|
||||
// $btn-link-color: $link-color;
|
||||
// $btn-link-hover-color: $link-hover-color;
|
||||
$btn-link-disabled-color: $gray-300;
|
||||
|
||||
// Forms
|
||||
$form-text-color: $text-muted;
|
||||
$input-bg: $gray-800;
|
||||
$input-disabled-bg: $gray-700;
|
||||
$input-color: $gray-100;
|
||||
$input-border-color: $gray-700;
|
||||
$input-focus-bg: $input-bg;
|
||||
$input-focus-border-color: tint-color($component-active-bg, 10%);
|
||||
$input-focus-color: $input-color;
|
||||
$input-placeholder-color: $gray-300;
|
||||
$input-plaintext-color: $body-color;
|
||||
|
||||
$form-check-input-active-filter: brightness(90%);
|
||||
$form-check-input-bg: $input-bg;
|
||||
$form-check-input-border: 1px solid rgba(255, 255, 255, 0.25);
|
||||
$form-check-input-checked-color: $component-active-color;
|
||||
$form-check-input-checked-bg-color: $component-active-bg;
|
||||
$form-check-input-checked-border-color: $form-check-input-checked-bg-color;
|
||||
$form-check-input-indeterminate-color: $component-active-color;
|
||||
$form-check-input-indeterminate-bg-color: $component-active-bg;
|
||||
$form-check-input-indeterminate-border-color: $form-check-input-indeterminate-bg-color;
|
||||
|
||||
$form-switch-color: rgba(255, 255, 255, 0.25);
|
||||
$form-switch-focus-color: $input-focus-border-color;
|
||||
$form-switch-checked-color: $component-active-color;
|
||||
|
||||
$input-group-addon-color: $input-color;
|
||||
$input-group-addon-bg: $gray-700;
|
||||
$input-group-addon-border-color: $input-border-color;
|
||||
|
||||
$form-select-color: $input-color;
|
||||
$form-select-disabled-color: $gray-400;
|
||||
$form-select-bg: $input-bg;
|
||||
$form-select-disabled-bg: $input-disabled-bg;
|
||||
$form-select-indicator-color: $gray-800;
|
||||
|
||||
$form-select-border-color: $input-border-color;
|
||||
$form-range-track-bg: $gray-300;
|
||||
|
||||
$form-range-thumb-bg: $component-active-bg;
|
||||
$form-range-thumb-box-shadow: 0 0.1rem 0.25rem rgba($black, 0.1);
|
||||
$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow;
|
||||
$form-range-thumb-active-bg: tint-color($component-active-bg, 70%);
|
||||
$form-range-thumb-disabled-bg: $gray-500;
|
||||
|
||||
$form-file-button-color: $input-color;
|
||||
$form-file-button-bg: $input-group-addon-bg;
|
||||
$form-file-button-hover-bg: shade-color($form-file-button-bg, 5%);
|
||||
|
||||
// Navs
|
||||
$nav-link-color: $body-color;
|
||||
$nav-link-hover-color: null;
|
||||
$nav-link-disabled-color: $gray-800;
|
||||
$nav-tabs-border-color: $border-color;
|
||||
$nav-tabs-link-hover-border-color: rgba($gray-800, 0.5) rgba($gray-800, 0.5) $nav-tabs-border-color;
|
||||
$nav-tabs-link-active-color: $gray-50;
|
||||
$nav-tabs-link-active-bg: $body-bg;
|
||||
$nav-tabs-link-active-border-color: $gray-800 $gray-800 $nav-tabs-link-active-bg;
|
||||
$nav-pills-link-active-color: $component-active-color;
|
||||
$nav-pills-link-active-bg: $component-active-bg;
|
||||
|
||||
// Dropdowns
|
||||
$dropdown-color: $body-color;
|
||||
$dropdown-bg: $gray-900;
|
||||
$dropdown-border-color: rgba($white, 0.15);
|
||||
$dropdown-link-color: $gray-100;
|
||||
$dropdown-link-hover-color: shade-color($gray-50, 10%);
|
||||
$dropdown-link-hover-bg: $gray-500;
|
||||
$dropdown-link-disabled-color: $gray-800;
|
||||
$dropdown-header-color: $gray-300;
|
||||
// $dropdown-dark-color: $gray-300;
|
||||
// $dropdown-dark-bg: $gray-800;
|
||||
// $dropdown-dark-border-color: $dropdown-border-color;
|
||||
// $dropdown-dark-divider-bg: $dropdown-divider-bg;
|
||||
// $dropdown-dark-box-shadow: null;
|
||||
// $dropdown-dark-link-color: $dropdown-dark-color;
|
||||
// $dropdown-dark-link-hover-color: $white;
|
||||
// $dropdown-dark-link-hover-bg: rgba($white, .15);
|
||||
// $dropdown-dark-link-active-color: $dropdown-link-active-color;
|
||||
// $dropdown-dark-link-active-bg: $dropdown-link-active-bg;
|
||||
// $dropdown-dark-link-disabled-color: $gray-500;
|
||||
// $dropdown-dark-header-color: $gray-500;
|
||||
|
||||
// Pagination
|
||||
$pagination-color: $link-color;
|
||||
$pagination-bg: $gray-800;
|
||||
$pagination-border-color: $gray-600;
|
||||
$pagination-focus-color: $link-hover-color;
|
||||
$pagination-focus-bg: $gray-400;
|
||||
$pagination-hover-color: $link-hover-color;
|
||||
$pagination-hover-bg: $gray-400;
|
||||
$pagination-hover-border-color: $gray-500;
|
||||
$pagination-active-color: $component-active-color;
|
||||
$pagination-active-bg: $component-active-bg;
|
||||
$pagination-active-border-color: $pagination-active-bg;
|
||||
$pagination-disabled-color: $gray-600;
|
||||
$pagination-disabled-bg: $gray-800;
|
||||
$pagination-disabled-border-color: $gray-600;
|
||||
|
||||
// Cards
|
||||
$card-border-color: rgba($white, 0.125);
|
||||
$card-inner-border-radius: subtract($card-border-radius, $card-border-width);
|
||||
|
||||
$card-cap-color: null;
|
||||
$card-height: null;
|
||||
$card-color: null;
|
||||
$card-bg: $gray-800;
|
||||
|
||||
// Accordion
|
||||
$accordion-color: $body-color;
|
||||
// $accordion-bg: transparent;
|
||||
$accordion-border-color: rgba($white, 0.125);
|
||||
$accordion-button-color: $accordion-color;
|
||||
$accordion-button-bg: $accordion-bg;
|
||||
$accordion-button-active-bg: tint-color($component-active-bg, 5%);
|
||||
$accordion-button-active-color: shade-color($primary, 10%);
|
||||
$accordion-button-focus-border-color: $input-focus-border-color;
|
||||
$accordion-icon-color: $accordion-color;
|
||||
$accordion-icon-active-color: $accordion-button-active-color;
|
||||
|
||||
$accordion-button-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-color}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>");
|
||||
$accordion-button-active-icon: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$accordion-icon-active-color}'><path fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/></svg>");
|
||||
|
||||
// Tooltips
|
||||
$tooltip-color: $body-color;
|
||||
$tooltip-bg: $gray-700;
|
||||
// $tooltip-opacity: .9;
|
||||
$tooltip-arrow-color: $tooltip-bg;
|
||||
$form-feedback-tooltip-opacity: $tooltip-opacity;
|
||||
|
||||
// Popovers
|
||||
$popover-bg: $gray-700;
|
||||
$popover-border-color: rgba($white, 0.2);
|
||||
$popover-header-bg: shade-color($popover-bg, 6%);
|
||||
$popover-header-color: $headings-color;
|
||||
$popover-body-color: $body-color;
|
||||
$popover-arrow-color: $popover-bg;
|
||||
$popover-arrow-outer-color: fade-in($popover-border-color, 0.05);
|
||||
|
||||
// Toasts
|
||||
$toast-color: null;
|
||||
$toast-background-color: rgba($white, 0.85);
|
||||
$toast-border-color: rgba(0, 0, 0, 0.1);
|
||||
$toast-header-color: $gray-600;
|
||||
$toast-header-background-color: rgba($white, 0.85);
|
||||
$toast-header-border-color: rgba(0, 0, 0, 0.05);
|
||||
|
||||
// Badges
|
||||
$badge-color: $white;
|
||||
|
||||
// Modals
|
||||
$modal-content-color: null;
|
||||
$modal-content-bg: $gray-800;
|
||||
$modal-content-border-color: rgba($white, 0.2);
|
||||
$modal-backdrop-bg: $black;
|
||||
// $modal-backdrop-opacity: .5;
|
||||
$modal-header-border-color: $border-color;
|
||||
$modal-footer-border-color: $modal-header-border-color;
|
||||
|
||||
// Alerts
|
||||
// $alert-bg-scale: -80%;
|
||||
// $alert-border-scale: -70%;
|
||||
// $alert-color-scale: 40%;
|
||||
|
||||
// Progress bars
|
||||
$progress-bg: $gray-600;
|
||||
$progress-bar-color: $white;
|
||||
$progress-bar-bg: $primary;
|
||||
|
||||
// List group
|
||||
$list-group-color: null;
|
||||
$list-group-bg: $card-bg;
|
||||
$list-group-border-color: rgba($white, 0.125);
|
||||
$list-group-hover-bg: rgba($gray-50, 0.15);
|
||||
$list-group-active-color: $component-active-color;
|
||||
$list-group-active-bg: $component-active-bg;
|
||||
$list-group-active-border-color: $list-group-active-bg;
|
||||
// $list-group-disabled-color: $gray-600;
|
||||
$list-group-disabled-bg: $list-group-bg;
|
||||
$list-group-action-color: $gray-300;
|
||||
$list-group-action-hover-color: $body-color;
|
||||
$list-group-action-active-color: $body-color;
|
||||
$list-group-action-active-bg: rgba($gray-300, 0.125);
|
||||
|
||||
// Image thumbnails
|
||||
$thumbnail-bg: $body-bg;
|
||||
$thumbnail-border-color: $gray-300;
|
||||
|
||||
// Figures
|
||||
$figure-caption-color: $gray-600;
|
||||
|
||||
// Breadcrumbs
|
||||
// $breadcrumb-bg: $gray-700;
|
||||
$breadcrumb-divider-color: $gray-100;
|
||||
$breadcrumb-active-color: $body-color;
|
||||
$breadcrumb-divider-flipped: $breadcrumb-divider;
|
||||
$breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='#{$breadcrumb-divider-color}'/%3E%3C/svg%3E");
|
||||
|
||||
// Carousel
|
||||
$carousel-control-color: $white;
|
||||
$carousel-indicator-active-bg: $white;
|
||||
$carousel-caption-color: $white;
|
||||
$carousel-dark-indicator-active-bg: $black;
|
||||
$carousel-dark-caption-color: $black;
|
||||
$carousel-dark-control-icon-filter: invert(1) grayscale(100);
|
||||
|
||||
// Close
|
||||
$btn-close-color: $white;
|
||||
$btn-close-bg: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='#{$btn-close-color}'><path d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/></svg>");
|
||||
$btn-close-white-filter: invert(1) grayscale(100%) brightness(200%);
|
||||
|
||||
// Code
|
||||
$code-color: $pink-300;
|
||||
$kbd-color: $white;
|
||||
$kbd-bg: $gray-300;
|
||||
$pre-color: null;
|
@ -1,24 +1,16 @@
|
||||
@import 'bootstrap/scss/functions';
|
||||
@import './theme-base.scss';
|
||||
|
||||
// Override built-in variables/add new variables.
|
||||
$green: #47e5bc;
|
||||
$orange: #f9a620;
|
||||
// $yellow: #ffd449;
|
||||
$red: #ff5964;
|
||||
$alt: #13293d;
|
||||
|
||||
$card-cap-bg: none;
|
||||
$input-border-color: $gray-200;
|
||||
|
||||
// On import, any variables marked `!default` will be overridden by the above.
|
||||
@import 'bootstrap/scss/variables';
|
||||
|
||||
$theme-color-addons: (
|
||||
'alt': $alt,
|
||||
'gray': $gray-400,
|
||||
);
|
||||
|
||||
// Merge/modify bootstrap variables.
|
||||
|
||||
$theme-colors: map-merge($theme-colors, $theme-color-addons);
|
||||
|
||||
$light: $gray-100;
|
||||
|
||||
$card-cap-color: $gray-800;
|
||||
|
||||
$breadcrumb-divider: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3E%3Cpath d='M2.5 0L1 1.5 3.5 4 1 6.5 2.5 8l4-4-4-4z' fill='currentColor'/%3E%3C/svg%3E");
|
@ -7,7 +7,7 @@
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"noUnusedLocals": true,
|
||||
"declaration": true,
|
||||
"declaration": false,
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"jsx": "react",
|
||||
|
@ -22,7 +22,10 @@
|
||||
</script>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
{% with color_mode=preferences|get_key:'ui.colormode' %}
|
||||
|
||||
<body{%if color_mode == 'dark'%} data-netbox-color-mode="dark"{% elif color_mode == 'light' %} data-netbox-color-mode="light"{% endif %}>
|
||||
|
||||
{% block layout %}{% endblock %}
|
||||
{% block javascript %}{% endblock %}
|
||||
{% include './messages.html' %}
|
||||
@ -30,4 +33,5 @@
|
||||
{% block data %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
{% endwith %}
|
||||
</html>
|
||||
|
@ -13,7 +13,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for change in changelog %}
|
||||
<tr class="table-{% get_status change.get_action_display %}">
|
||||
<tr class="{% get_status change.get_action_display %}">
|
||||
<th scope="row">{{ change.user|default:change.user_name }}</th>
|
||||
<td>{{ change.get_action_display|bettertitle }}</td>
|
||||
<td>{{ change.changed_object_type.name|bettertitle }}</td>
|
||||
|
@ -4,7 +4,7 @@
|
||||
<h5 class="card-header">
|
||||
Search
|
||||
</h5>
|
||||
<div class="card-body">
|
||||
<div class="card-body overflow-visible">
|
||||
<form action="." method="get">
|
||||
{% for field in filter_form.hidden_fields %}
|
||||
{{ field }}
|
||||
|
@ -7,12 +7,12 @@
|
||||
<div class="row">
|
||||
<nav
|
||||
id="sidebar-menu"
|
||||
class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse px-0"
|
||||
class="col-md-3 col-lg-2 d-md-block sidebar collapse px-0"
|
||||
>
|
||||
<div class="position-sticky pt-3">
|
||||
<a class="px-2 sidebar-logo" href="{% url 'home' %}">
|
||||
{% load static %}
|
||||
<img src="{% static 'netbox_logo.svg' %}" height="50" />
|
||||
{% include 'logo.html' %}
|
||||
</a>
|
||||
<ul class="nav flex-column">
|
||||
{% load nav %} {% nav %}
|
||||
@ -22,7 +22,7 @@
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-0">
|
||||
<nav class="navbar navbar-light sticky-top flex-md-nowrap py-4 bg-white container-fluid">
|
||||
<nav class="navbar navbar-light sticky-top flex-md-nowrap py-4 search container-fluid">
|
||||
<button
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
|
21
netbox/templates/logo.html
Normal file
@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 320" height="50">
|
||||
<g id="netbox-logo-1">
|
||||
<circle cx="37" cy="284" r="23"/>
|
||||
<circle cx="101" cy="37" r="23"/>
|
||||
<circle cx="101" cy="220" r="23"/>
|
||||
<circle cx="284" cy="220" r="23"/>
|
||||
<rect x="93" y="37" width="16" height="180"/>
|
||||
<rect x="101" y="212" width="180" height="16"/>
|
||||
<rect x="93" y="212" width="16" height="90" transform="rotate(45 101 220)"/>
|
||||
</g>
|
||||
<g id="netbox-logo-2">
|
||||
<circle cx="284" cy="37" r="23"/>
|
||||
<circle cx="37" cy="101" r="23"/>
|
||||
<circle cx="220" cy="101" r="23"/>
|
||||
<circle cx="220" cy="284" r="23"/>
|
||||
<rect x="37" y="93" width="180" height="16"/>
|
||||
<rect x="212" y="101" width="16" height="180"/>
|
||||
<rect x="212" y="93" width="16" height="90" transform="rotate(225 220 101)"/>
|
||||
<path transform="translate(380, 8)" d="M13.60 200L13.60 104L36.40 104L36.40 119.40L36.80 119.40Q40.20 112.20 47.20 106.90Q54.20 101.60 66.20 101.60L66.20 101.60Q75.80 101.60 82.50 104.80Q89.20 108 93.40 113.20Q97.60 118.40 99.40 125.20Q101.20 132 101.20 139.40L101.20 139.40L101.20 200L77.20 200L77.20 151.40Q77.20 147.40 76.80 142.50Q76.40 137.60 74.70 133.30Q73 129 69.40 126.10Q65.80 123.20 59.60 123.20L59.60 123.20Q53.60 123.20 49.50 125.20Q45.40 127.20 42.70 130.60Q40 134 38.80 138.40Q37.60 142.80 37.60 147.60L37.60 147.60L37.60 200L13.60 200ZM224.80 160.40L151.60 160.40Q152.80 171.20 160 177.20Q167.20 183.20 177.40 183.20L177.40 183.20Q186.40 183.20 192.50 179.50Q198.60 175.80 203.20 170.20L203.20 170.20L220.40 183.20Q212 193.60 201.60 198Q191.20 202.40 179.80 202.40L179.80 202.40Q169 202.40 159.40 198.80Q149.80 195.20 142.80 188.60Q135.80 182 131.70 172.70Q127.60 163.40 127.60 152L127.60 152Q127.60 140.60 131.70 131.30Q135.80 122 142.80 115.40Q149.80 108.80 159.40 105.20Q169 101.60 179.80 101.60L179.80 101.60Q189.80 101.60 198.10 105.10Q206.40 108.60 212.30 115.20Q218.20 121.80 221.50 131.50Q224.80 141.20 224.80 153.80L224.80 153.80L224.80 160.40ZM151.60 142.40L200.80 142.40Q200.60 131.80 194.20 125.70Q187.80 119.60 176.40 119.60L176.40 119.60Q165.60 119.60 159.30 125.80Q153 132 151.60 142.40L151.60 142.40ZM259.80 124.40L240.00 124.40L240.00 104L259.80 104L259.80 76.20L283.80 76.20L283.80 104L310.20 104L310.20 124.40L283.80 124.40L283.80 166.40Q283.80 173.60 286.50 177.80Q289.20 182 297.20 182L297.20 182Q300.40 182 304.20 181.30Q308 180.60 310.20 179L310.20 179L310.20 199.20Q306.40 201 300.90 201.70Q295.40 202.40 291.20 202.40L291.20 202.40Q281.60 202.40 275.50 200.30Q269.40 198.20 265.90 193.90Q262.40 189.60 261.10 183.20Q259.80 176.80 259.80 168.40L259.80 168.40L259.80 124.40ZM333.20 200L333.20 48.80L357.20 48.80L357.20 116.20L357.80 116.20Q359.60 113.80 362.40 111.30Q365.20 108.80 369.20 106.60Q373.20 104.40 378.40 103Q383.60 101.60 390.40 101.60L390.40 101.60Q400.60 101.60 409.20 105.50Q417.80 109.40 423.90 116.20Q430 123 433.40 132.20Q436.80 141.40 436.80 152L436.80 152Q436.80 162.60 433.60 171.80Q430.40 181 424.20 187.80Q418 194.60 409.20 198.50Q400.40 202.40 389.40 202.40L389.40 202.40Q379.20 202.40 370.40 198.40Q361.60 194.40 356.40 185.60L356.40 185.60L356 185.60L356 200L333.20 200ZM412.80 152L412.80 152Q412.80 146.40 410.90 141.20Q409 136 405.30 132Q401.60 128 396.40 125.60Q391.20 123.20 384.60 123.20L384.60 123.20Q378 123.20 372.80 125.60Q367.60 128 363.90 132Q360.20 136 358.30 141.20Q356.40 146.40 356.40 152L356.40 152Q356.40 157.60 358.30 162.80Q360.20 168 363.90 172Q367.60 176 372.80 178.40Q378 180.80 384.60 180.80L384.60 180.80Q391.20 180.80 396.40 178.40Q401.60 176 405.30 172Q409 168 410.90 162.80Q412.80 157.60 412.80 152ZM458.40 152L458.40 152Q458.40 140.60 462.50 131.30Q466.60 122 473.60 115.40Q480.60 108.80 490.20 105.20Q499.80 101.60 510.60 101.60L510.60 101.60Q521.40 101.60 531 105.20Q540.60 108.80 547.60 115.40Q554.60 122 558.70 131.30Q562.80 140.60 562.80 152L562.80 152Q562.80 163.40 558.70 172.70Q554.60 182 547.60 188.60Q540.60 195.20 531 198.80Q521.40 202.40 510.60 202.40L510.60 202.40Q499.80 202.40 490.20 198.80Q480.60 195.20 473.60 188.60Q466.60 182 462.50 172.70Q458.40 163.40 458.40 152ZM482.40 152L482.40 152Q482.40 157.60 484.30 162.80Q486.20 168 489.90 172Q493.60 176 498.80 178.40Q504 180.80 510.60 180.80L510.60 180.80Q517.20 180.80 522.40 178.40Q527.60 176 531.30 172Q535 168 536.90 162.80Q538.80 157.60 538.80 152L538.80 152Q538.80 146.40 536.90 141.20Q535 136 531.30 132Q527.60 128 522.40 125.60Q517.20 123.20 510.60 123.20L510.60 123.20Q504 123.20 498.80 125.60Q493.60 128 489.90 132Q486.20 136 484.30 141.20Q482.40 146.40 482.40 152ZM575.40 200L614 148.40L580.80 104L610 104L629.20 132.80L650 104L677.40 104L644.60 148.40L683.20 200L654 200L629 165.60L603.80 200L575.40 200Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.6 KiB |
@ -10,21 +10,27 @@
|
||||
<span id="navbar_user">{{ request.user|truncatechars:"30" }}</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li class="dropdown-item">
|
||||
<li>
|
||||
<button type="button" class="dropdown-item color-mode-toggle">
|
||||
<i class="color-mode-icon mdi mdi-lightbulb"></i>
|
||||
<span class="color-mode-text">Dark Mode</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
{% if request.user.is_staff %}
|
||||
<a class="text-decoration-none" href="{% url 'admin:index' %}">
|
||||
<a class="dropdown-item" href="{% url 'admin:index' %}">
|
||||
<i class="mdi mdi-cog"></i> Admin
|
||||
</a>
|
||||
{% endif %}
|
||||
</li>
|
||||
<li class="dropdown-item">
|
||||
<a class="text-decoration-none" href="{% url 'user:profile' %}">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'user:profile' %}">
|
||||
<i class="mdi mdi-account"></i> Profile
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider" /></li>
|
||||
<li class="dropdown-item">
|
||||
<a class="text-decoration-none" href="{% url 'logout' %}">
|
||||
<li>
|
||||
<a class="dropdown-item" href="{% url 'logout' %}">
|
||||
<i class="mdi mdi-logout-variant"></i> Log Out
|
||||
</a>
|
||||
</li>
|
||||
|
@ -4,32 +4,56 @@
|
||||
{% block title %}User Preferences{% endblock %}
|
||||
|
||||
{% block usercontent %}
|
||||
{% if preferences %}
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" class="toggle" title="Toggle all"></th>
|
||||
<th>Preference</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in preferences.items %}
|
||||
<tr>
|
||||
<td class="min-width"><input type="checkbox" name="pk" value="{{ key }}"></td>
|
||||
<td><samp>{{ key }}</samp></td>
|
||||
<td><samp>{{ value }}</samp></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Clear Selected
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<div class="field-group mb-3">
|
||||
<h4>Color Mode</h4>
|
||||
<p class="lead text-muted">Set your preferred UI color mode</p>
|
||||
{% with color_mode=preferences|get_key:'ui.colormode'%}
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-dark" value="dark"{% if color_mode == 'dark'%} checked{% endif %}>
|
||||
<label class="form-check-label" for="color-mode-preference-dark">Dark</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="radio" name="ui.colormode" id="color-mode-preference-light" value="light"{% if color_mode == 'light'%} checked{% endif %}>
|
||||
<label class="form-check-label" for="color-mode-preference-light">Light</label>
|
||||
</div>
|
||||
{% endwith %}
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<button type="submit" class="btn btn-success" name="_update">
|
||||
Save
|
||||
</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<h3 class="text-muted text-center">No preferences found</h3>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if preferences %}
|
||||
<div class="field-group">
|
||||
<h4>Other Preferences</h4>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" class="toggle" title="Toggle all"></th>
|
||||
<th>Preference</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for key, value in preferences.items %}
|
||||
<tr>
|
||||
<td class="min-width"><input type="checkbox" name="pk" value="{{ key }}"></td>
|
||||
<td><samp>{{ key }}</samp></td>
|
||||
<td><samp>{{ value }}</samp></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<button type="submit" class="btn btn-danger" name="_delete">
|
||||
<span class="mdi mdi-trash-can-outline" aria-hidden="true"></span> Clear Selected
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3 class="text-muted text-center">No preferences found</h3>
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
@ -91,6 +91,7 @@ class LogoutView(View):
|
||||
"""
|
||||
Deauthenticate a web user.
|
||||
"""
|
||||
|
||||
def get(self, request):
|
||||
logger = logging.getLogger('netbox.auth.logout')
|
||||
|
||||
@ -136,9 +137,17 @@ class UserConfigView(LoginRequiredMixin, View):
|
||||
data = userconfig.all()
|
||||
|
||||
# Delete selected preferences
|
||||
for key in request.POST.getlist('pk'):
|
||||
if key in data:
|
||||
userconfig.clear(key)
|
||||
if "_delete" in request.POST:
|
||||
for key in request.POST.getlist('pk'):
|
||||
if key in data:
|
||||
userconfig.clear(key)
|
||||
# Update specific values
|
||||
elif "_update" in request.POST:
|
||||
for key in request.POST:
|
||||
if not key.startswith('_') and not key.contains('csrf'):
|
||||
for value in request.POST.getlist(key):
|
||||
userconfig.set(key, value)
|
||||
|
||||
userconfig.save()
|
||||
messages.success(request, "Your preferences have been updated.")
|
||||
|
||||
|