From 0548bee8ad26d219a7b2f31216694b9bd506abb1 Mon Sep 17 00:00:00 2001 From: eeintech Date: Mon, 7 Sep 2020 11:29:24 -0500 Subject: [PATCH] Added Color Theme view in settings --- InvenTree/InvenTree/forms.py | 13 +- .../InvenTree/static/css/inventree-darker.css | 785 ++++++++++++++++++ InvenTree/InvenTree/urls.py | 3 +- InvenTree/InvenTree/views.py | 15 +- InvenTree/common/migrations/0007_theme.py | 20 + InvenTree/common/models.py | 14 + .../templates/InvenTree/settings/tabs.html | 3 + .../templates/InvenTree/settings/theme.html | 27 + 8 files changed, 875 insertions(+), 5 deletions(-) create mode 100644 InvenTree/InvenTree/static/css/inventree-darker.css create mode 100644 InvenTree/common/migrations/0007_theme.py create mode 100644 InvenTree/templates/InvenTree/settings/theme.html diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 1e70b525c6..e090ab9de3 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -11,7 +11,7 @@ from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Field from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText from django.contrib.auth.models import User - +from common.models import Theme class HelperForm(forms.ModelForm): """ Provides simple integration of crispy_forms extension. """ @@ -161,3 +161,14 @@ class SetPasswordForm(HelperForm): 'enter_password', 'confirm_password' ] + + +class ThemeSelectForm(forms.ModelForm): + """ Form for setting color theme + """ + + class Meta: + model = Theme + fields = [ + 'theme' + ] \ No newline at end of file diff --git a/InvenTree/InvenTree/static/css/inventree-darker.css b/InvenTree/InvenTree/static/css/inventree-darker.css new file mode 100644 index 0000000000..2df593b2fa --- /dev/null +++ b/InvenTree/InvenTree/static/css/inventree-darker.css @@ -0,0 +1,785 @@ +:root { + --primary-color: #335d88; + --secondary-color: #b69c80; + --highlight-color: #f5efe8; + --basic-color: #333; + + --label-red: #e35a57; + --label-blue: #4194bd; + --label-green: #50aa51; + --label-grey: #aaa; + --label-yellow: #fdc82a; +} + +.markdownx .row { + margin: 5px; + padding: 5px; + border: 1px solid #cce; + border-radius: 4px; +} + +.markdownx-editor { + width: 100%; + border: 1px solid #cce; + border-radius: 3px; + padding: 10px; +} + +.panel-content { + padding: 10px; +} + +.markdownx-preview { + border: 1px solid #cce; + border-radius: 3px; + padding: 10px; +} + +/* Progress bars */ + +.progress { + position: relative; + width: 100%; + margin-bottom: 0px; + background: #eeeef5; +} + +.progress-bar { + opacity: 60%; + background: #2aa02a; +} + +.progress-bar-under { + background: #eeaa33; +} + +.progress-bar-over { + background: #337ab7; +} + +.progress-value { + width: 100%; + color: #333; + position: absolute; + text-align: center; + top: 0px; + left: 0px; + font-size: 110%; +} + +.qr-code { + max-width: 400px; + max-height: 400px; + align-content: center; +} + +.qr-container { + width: 100%; + align-content: center; +} + +.navbar-brand { + float: left; +} + +.navbar-barcode-li { + border-left: none; + border-right: none; +} + +.navbar-nav > li { + border-left: 1px solid; + border-right: 1px solid; + border-color: rgb(179, 179, 179); + +} + +.navbar-nav > li > a { + color:#0b2a62 !important; +} + +.navbar-nav > li > a:hover { + color:#202020 !important; +} + +.navbar-nav > .open > a { + color:#202020 !important; +} + + + + +.navbar-form { + padding-right: 3px; +} + +.navbar { + background-color: rgb(189, 189, 189); +} + +.table-condensed > tbody > tr > td { + border-top: 1px solid #062152 !important ; +} + +.table-striped > tbody > tr > td { + border-top: 1px solid #92b3f1 ; +} + +.table-bordered, .table-bordered > tbody > tr > td { + border: 1px solid rgb(182,182,182); +} + +.table-bordered > thead > tr > th { + border: 1px solid rgb(182, 182, 182); + background-color: rgb(235, 235, 235); +} + +h3 { + color:#06255d; +} + +#barcode-scan { + margin-top: 8px; +} + +.icon-header { + margin-right: 10px; +} + +.glyphicon { + font-size: 18px; +} + + +.glyphicon-small { + font-size: 12px; +} + +.glyphicon-right { + float: right; +} + +.starred-part { + color: #ffbb00; +} + +.red-cell { + background-color: #ec7f7f; +} + +.part-price { + color: rgb(13, 245, 25); +} + +.icon-red { + color: #c55; +} + +.icon-green { + color: #43bb43; +} + +.icon-blue { + color: #55c; +} + +.icon-yellow { + color: #CC2; +} + +/* CSS overrides for treeview */ +.expand-icon { + font-size: 11px; +} + +.treeview .badge { + font-size: 10px; +} + +.treeview .list-group-item { + padding: 6px 12px; +} + +.list-group-item-condensed { + padding: 5px 10px; +} + +/* Extra label styles */ + +.label-large { + margin: 3px; + font-size: 100%; + border: 3px solid; + border-radius: 15px; + background: none; + padding-right: 10px; + padding-left: 10px; + padding-top: 5px; + padding-bottom: 5px; +} + +.label-large-red { + color: var(--label-red); + border-color: var(--label-red); +} + +.label-red { + background: var(--label-red); +} + +.label-large-blue { + color: var(--label-blue); + border-color: var(--label-blue); +} + +.label-blue { + background: var(--label-blue); +} + +.label-large-green { + color: var(--label-green); + border-color: var(--label-green); +} + +.label-green { + background: var(--label-green); +} + +.label-large-grey { + color: var(--label-grey); + border-color: var(--label-grey); +} + +.label-grey { + background: var(--label-grey); +} + +.label-large-yellow { + color: var(--label-yellow); + border-color: var(--label-yellow); +} + +.label-yellow { + background: var(--label-yellow); +} + +.label-right { + float: right; + margin-left: 3px; + margin-right: 3px; +} + +/* Bootstrap table overrides */ + +.stock-sub-group td { + background-color: #ebf4f4; +} + +.sub-table { + margin-left: 45px; + margin-right: 45px; +} + +.detail-icon .glyphicon { + color: #98d296; +} + +/* Force select2 elements in modal forms to be full width */ +.select-full-width { + width: 100%; +} + +.basecurrency { + color: #050; + font-style: italic; + font-weight: bold; +} + +.bomselect { + max-width: 250px; +} + +.rowvalid { + color: #050; +} + +.rowinvalid { + color: #A00; + font-style: italic; +} + +.dropdown { + padding-left: 1px; + margin-left: 1px; +} + +.dropdown-buttons { + display: inline-block +} + +.dropdown-menu .open{ + z-index: 1000; + position: relative; + overflow: visible; +} + +/* Styles for table buttons and filtering */ +.button-toolbar .btn { + margin-left: 1px; + margin-right: 1px; +} + +.filter-list { + display: inline-block; + *display: inline; + margin-bottom: 1px; + margin-top: 1px; + vertical-align: middle; + margin: 1px; + padding: 2px; + background: #eee; + border: 1px solid #eee; + border-radius: 3px; +} + +.filter-list .close { + cursor: pointer; + right: 0%; + padding-right: 2px; + padding-left: 2px; + transform: translate(0%, -25%); +} + +.filter-list .close:hover {background: #bbb;} + +.filter-tag { + display: inline-block; + *display: inline; + zoom: 1; + padding-left: 3px; + padding-right: 3px; + padding-top: 2px; + padding-bottom: 2px; + border: 1px solid #aaa; + border-radius: 3px; + background: #eee; + margin: 1px; + margin-left: 5px; + margin-right: 5px; +} + +.filter-input { + display: inline-block; + *display: inline; + zoom: 1; +} + +.filter-tag:hover { + background: #ddd; +} + +/* Part image icons with full-display on mouse hover */ + +.hover-img-thumb { + background: #eee; + width: 28px; + height: 28px; + object-fit: contain; + border: 1px solid #cce; +} + +.hover-img-large { + background: #eee; + display: none; + position: absolute; + z-index: 400; + border: 1px solid #555; + max-width: 250px; +} + +.hover-icon { + margin-right: 10px; +} + +.hover-icon:hover > .hover-img-large { + display: block; +} + +/* dropzone class - for Drag-n-Drop file uploads */ +.dropzone { + z-index: 2; +} + +/* +.dropzone * { + pointer-events: none; +} +*/ + +.dragover { + background-color: #55A; + border: 1px dashed #111; + opacity: 0.1; + -moz-opacity: 10%; + -webkit-opacity: 10%; +} + +/* grid display for part images */ + +.table-img-grid tr { + display: inline; +} + +.table-img-grid td { + padding: 10px; + margin: 10px; +} + +.table-img-grid .grid-image { + + height: 128px; + width: 128px; + object-fit: contain; + background: #eee; +} + +.btn-glyph { + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 2px; +} + +.action-button { + font-size: 125%; +} + +.action-buttons .btn { + font-size: 175%; + align-content: center; + vertical-align: middle; + padding-left: 6px; + padding-right: 6px; + padding-top: 3px; + padding-bottom: 2px; +}; + +.panel-heading .badge { + float: right; +} + +.badge { + float: right; + background-color: #777; + color: #fff; + border-radius: 5px; + margin-left: 10px; +} + +.badge-alert { + background-color: #f33; +} + +.part-thumb { + width: 200px; + height: 200px; + margin: 2px; + padding: 3px; + object-fit: contain; + border: 1px solid #aaa; + border-radius: 3px; +} + +.part-thumb-container:hover .part-thumb-overlay { + opacity: 1; +} + +.part-thumb-overlay { + position: absolute; + top: 0; + left: 0; + opacity: 0; + transition: .25s ease; + padding: 15px; + margin: 5px; +} + +.checkbox { + margin-left: 20px; +} + +.checkboxinput { + padding-left: 5px; + padding-right: 5px; +} + +.media { + padding-top: 15px; +} + +.media-body { + padding-top: 10px; + overflow: visible; +} + +.navigation { +} + +.nav-tabs { + margin-bottom: 20px; +} + +.settings-container { + width: 90%; + padding: 15px; +} + +.settings-nav { + height: 100%; + width: 160px; + position: fixed; + z-index: 1; + //top: 0; + //left: 0; + overflow-x: hidden; + padding-top: 20px; + padding-right: 25px; +} + +.settings-content { + margin-left: 175px; + padding: 0px 10px; +} + +.breadcrump { + margin-bottom: 5px; +} + +.inventree-body { + width: 100%; + padding: 5px; + margin: 10px; +} + +.inventree-pre-content { + width: 100%; + clear: both; +} + +.inventree-content { + padding-left: 5px; + padding-right: 5px; + padding-top: 5px; + width: auto; + transition: 0.1s; +} + +.body { + padding-top: 70px; +} + +.modal { + overflow: hidden; + z-index: 9999; +} + +.modal-primary { + z-index: 10000; +} + +.modal-secondary { + z-index: 11000; +} + +.js-modal-form .checkbox { + margin-left: 0px; +} + +.modal-dialog { + width: 60%; +} + +.modal-secondary .modal-dialog { + width: 40%; + padding-top: 15px; +} + +.modal-content h3 { + margin-top: 3px; + margin-bottom: 3px; +} + +.modal-form-content { + border-radius: 0; + position:relative; + height: auto !important; + max-height: calc(100vh - 200px) !important; + overflow-y: scroll; + padding: 10px; +} + +.modal input { + width: 100%; +} + +input[type="submit"] { + color: #333; + background-color: #e6e6e6; + border-color: #adadad; +} + +.modal textarea { + width: 100%; +} + +/* Force a control-label div to be 100% width */ +.modal .control-label { + width: 100%; + margin-top: 5px; +} + +.modal .control-label .btn { + padding-top: 3px; + padding-bottom: 3px; +} + +.modal .btn-secondary { + background-color: #5e7d87; +} + +/* The side navigation menu */ +.sidenav { + height: 100%; /* 100% Full-height */ + width: 0px; /* 0 width - change this with JavaScript */ + position: fixed; /* Stay in place */ + background-color: #fff; /* Black*/ + overflow-x: hidden; /* Disable horizontal scroll */ + transition: 0.1s; /* 0.5 second transition effect to slide in the sidenav */ +} + +.wrapper { + align-items: stretch; + display: flex; +} + +.help-inline { + color: #A11; +} + +.notification-area { + position: fixed; + top: 0px; + margin-top: 20px; + width: 100%; + padding: 20px; + z-index: 5000; + pointer-events: none; // Prevent this div from blocking links underneath +} + +.alert { + display: none; + border-radius: 5px; + opacity: 0.9; + pointer-events: all; +} + +.alert-block { + display: block; +} + +.btn { + margin-left: 2px; + margin-right: 2px; +} + +.btn-remove { + padding: 3px; + padding-left: 5px; + padding-right: 5px; + color: #A11; +} + +.btn-create { + padding: 3px; + padding-left: 5px; + padding-right: 5px; + color: #1A1; +} + +.btn-edit { + padding: 3px; + padding: 3px; + padding-left: 5px; + padding-right: 5px; + color: #55E; +} + +.button-toolbar { + padding-left: 0px; +} + +.panel-group { + margin-bottom: 5px; +} + +.panel-body { + padding: 10px; +} + +.panel-group .panel { + border-radius: 2px; +} + +.panel-heading { + padding: 5px 10px; + background-color: #fafafa; +} + +.float-right { + float: right; +} + +.warning-msg { + color: #e00; +} + +.login { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +.part-allocation { + padding: 3px 10px; + border: 1px solid #ccc; + border-radius: 2px; +} + +.part-allocation-pass { + background-color: #dbf0db; +} + +.part-allocation-underallocated { + background-color: #f0dbdb; +} + +.part-allocation-overallocated { + background-color: #ccf5ff; +} + +.glyphicon-refresh-animate { + -animation: spin .7s infinite linear; + -webkit-animation: spin2 .7s infinite linear; +} + +@-webkit-keyframes spin2 { + from { -webkit-transform: rotate(0deg);} + to { -webkit-transform: rotate(360deg);} +} + +@keyframes spin { + from { transform: scale(1) rotate(0deg);} + to { transform: scale(1) rotate(360deg);} +} + diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index d0076714ae..0ada05753c 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -36,7 +36,7 @@ from django.views.generic.base import RedirectView from rest_framework.documentation import include_docs_urls from .views import IndexView, SearchView, DatabaseStatsView -from .views import SettingsView, EditUserView, SetPasswordView +from .views import SettingsView, EditUserView, SetPasswordView, ThemeSelectView from .views import DynamicJsView from .api import InfoView @@ -71,6 +71,7 @@ settings_urls = [ url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'), url(r'^currency/?', SettingsView.as_view(template_name='InvenTree/settings/currency.html'), name='settings-currency'), url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'), + url(r'^theme/?', ThemeSelectView.as_view(), name='settings-theme'), url(r'^other/?', SettingsView.as_view(template_name='InvenTree/settings/other.html'), name='settings-other'), # Catch any other urls diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 333f11898b..a6b7c6cd26 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -11,16 +11,17 @@ from __future__ import unicode_literals from django.utils.translation import gettext_lazy as _ from django.template.loader import render_to_string from django.http import JsonResponse, HttpResponseRedirect +from django.urls import reverse_lazy from django.views import View -from django.views.generic import UpdateView, CreateView +from django.views.generic import UpdateView, CreateView, FormView from django.views.generic.base import TemplateView from part.models import Part, PartCategory from stock.models import StockLocation, StockItem -from common.models import InvenTreeSetting +from common.models import InvenTreeSetting, Theme -from .forms import DeleteForm, EditUserForm, SetPasswordForm +from .forms import DeleteForm, EditUserForm, SetPasswordForm, ThemeSelectForm from .helpers import str2bool from rest_framework import views @@ -556,6 +557,14 @@ class SettingsView(TemplateView): return ctx +class ThemeSelectView(FormView): + """ View for selecting a color theme """ + + form_class = ThemeSelectForm + success_url = reverse_lazy('settings-theme') + template_name = "InvenTree/settings/theme.html" + + class DatabaseStatsView(AjaxView): """ View for displaying database statistics """ diff --git a/InvenTree/common/migrations/0007_theme.py b/InvenTree/common/migrations/0007_theme.py new file mode 100644 index 0000000000..9af751af05 --- /dev/null +++ b/InvenTree/common/migrations/0007_theme.py @@ -0,0 +1,20 @@ +# Generated by Django 3.0.7 on 2020-09-07 16:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('common', '0006_auto_20200203_0951'), + ] + + operations = [ + migrations.CreateModel( + name='Theme', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('theme', models.IntegerField(choices=[(0, 'Default'), (1, 'Darker')], default=0)), + ], + ), + ] diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 6a2f6d8fa3..ef1778cb87 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -154,3 +154,17 @@ class Currency(models.Model): self.value = 1.0 super().save(*args, **kwargs) + + +class Theme(models.Model): + """ Color Theme setting """ + + class ThemeChoices(models.IntegerChoices): + DEFAULT = 0, _('Default') + DARKER = 1, _('Darker') + + theme = models.IntegerField(choices=ThemeChoices.choices, + default=ThemeChoices.DEFAULT) + + def __str__(self): + return self.theme diff --git a/InvenTree/templates/InvenTree/settings/tabs.html b/InvenTree/templates/InvenTree/settings/tabs.html index 7b049e4fc6..8719d29c81 100644 --- a/InvenTree/templates/InvenTree/settings/tabs.html +++ b/InvenTree/templates/InvenTree/settings/tabs.html @@ -8,6 +8,9 @@ Part + + Theme + {% if user.is_staff %} Other diff --git a/InvenTree/templates/InvenTree/settings/theme.html b/InvenTree/templates/InvenTree/settings/theme.html new file mode 100644 index 0000000000..234b8e1cd3 --- /dev/null +++ b/InvenTree/templates/InvenTree/settings/theme.html @@ -0,0 +1,27 @@ +{% extends "InvenTree/settings/settings.html" %} + +{% block tabs %} +{% include "InvenTree/settings/tabs.html" with tab='theme' %} +{% endblock %} + +{% block settings %} + +
+
+

Color Themes

+
+
+ +
+
+
+
+ {% csrf_token %} + {{ form }} + +
+
+
+
+ +{% endblock %} \ No newline at end of file