Allow for customization of workflow
This commit is contained in:
parent
444aeb37e6
commit
326de9444d
34 changed files with 315 additions and 129 deletions
|
@ -4,4 +4,5 @@ from .layout import LayoutAdmin
|
||||||
from .item import ItemAdmin
|
from .item import ItemAdmin
|
||||||
from .manufacturer import ManufacturerAdmin
|
from .manufacturer import ManufacturerAdmin
|
||||||
from .form_factor import FormFactorAdmin
|
from .form_factor import FormFactorAdmin
|
||||||
from .tag import TagAdmin
|
from .tag import TagAdmin
|
||||||
|
from .settings import SettingsAdmin
|
21
inventory/admin/settings.py
Normal file
21
inventory/admin/settings.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from typing import Optional, TypeVar
|
||||||
|
from django.db.models import Model
|
||||||
|
from django.http.request import HttpRequest
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from inventory.models import Settings
|
||||||
|
|
||||||
|
|
||||||
|
_ModelT = TypeVar("_ModelT", bound=Model)
|
||||||
|
|
||||||
|
|
||||||
|
class SettingsAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
|
def has_add_permission(self, request: HttpRequest) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def has_delete_permission(self, request: HttpRequest, obj: Optional[_ModelT] = None) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(Settings, SettingsAdmin)
|
28
inventory/migrations/0004_auto_20201222_1858.py
Normal file
28
inventory/migrations/0004_auto_20201222_1858.py
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
# Generated by Django 3.1.4 on 2020-12-22 18:58
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0003_item_size'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='area',
|
||||||
|
name='show_sub_area',
|
||||||
|
field=models.BooleanField(default=True, help_text='Allow sub-areas to be defined in this area'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='workshop',
|
||||||
|
name='show_boxes',
|
||||||
|
field=models.BooleanField(default=True, help_text='Allow boxes to be defined directly in this workshop'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='item',
|
||||||
|
name='size',
|
||||||
|
field=models.PositiveIntegerField(default=1, help_text='Number of sub-compartments this item takes up'),
|
||||||
|
),
|
||||||
|
]
|
27
inventory/migrations/0005_settings.py
Normal file
27
inventory/migrations/0005_settings.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 3.1.4 on 2020-12-22 19:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
def create_settings(apps, schema_editor):
|
||||||
|
Settings = apps.get_model('inventory', 'Settings')
|
||||||
|
Settings.objects.create(default_container=None)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0004_auto_20201222_1858'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Settings',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('default_container', models.ForeignKey(blank=True, default=None, help_text='Default container to display when calling the index page', null=True, on_delete=django.db.models.deletion.SET_NULL, to='inventory.container')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.RunPython(create_settings)
|
||||||
|
]
|
|
@ -9,3 +9,4 @@ from .manufacturer import Manufacturer
|
||||||
from .tag import Tag
|
from .tag import Tag
|
||||||
from .workshop import Workshop
|
from .workshop import Workshop
|
||||||
from .container import Container, CanBeContained
|
from .container import Container, CanBeContained
|
||||||
|
from .settings import Settings
|
|
@ -6,6 +6,7 @@ from .container import Container, CanBeContained
|
||||||
class Area(CanBeContained, Container):
|
class Area(CanBeContained, Container):
|
||||||
name = models.CharField(max_length=255, unique=True)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
description = models.CharField(max_length=4096)
|
description = models.CharField(max_length=4096)
|
||||||
|
show_sub_area = models.BooleanField(default=True, help_text="Allow sub-areas to be defined in this area")
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
changed_at = models.DateTimeField(auto_now=True)
|
changed_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from django.urls import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
|
|
||||||
|
@ -30,6 +29,9 @@ class Container(models.Model):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self):
|
def url(self):
|
||||||
|
_, obj = self.subclass
|
||||||
|
if obj is not None:
|
||||||
|
return obj.url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@ -48,4 +50,4 @@ class CanBeContained(models.Model):
|
||||||
@property
|
@property
|
||||||
def container_url(self):
|
def container_url(self):
|
||||||
_, obj = self.container.subclass
|
_, obj = self.container.subclass
|
||||||
return obj.url
|
return obj.url
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.postgres.fields import JSONField, ArrayField
|
|
||||||
|
|
||||||
from .container import CanBeContained
|
from .container import CanBeContained
|
||||||
|
|
||||||
|
@ -23,4 +22,4 @@ class Item(CanBeContained):
|
||||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -11,4 +11,4 @@ class Manufacturer(models.Model):
|
||||||
icon = models.ImageField(null=True, blank=True)
|
icon = models.ImageField(null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
18
inventory/models/settings.py
Normal file
18
inventory/models/settings.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
|
class Settings(models.Model):
|
||||||
|
default_container = models.ForeignKey(
|
||||||
|
'inventory.Container',
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
default=None,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
help_text='Default container to display when calling the index page'
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return 'Settings'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name_plural = 'Settings'
|
|
@ -6,6 +6,7 @@ from .container import Container
|
||||||
class Workshop(Container):
|
class Workshop(Container):
|
||||||
name = models.CharField(max_length=255, unique=True)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
description = models.CharField(max_length=4096)
|
description = models.CharField(max_length=4096)
|
||||||
|
show_boxes = models.BooleanField(default=True, help_text="Allow boxes to be defined directly in this workshop")
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
changed_at = models.DateTimeField(auto_now=True)
|
changed_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,15 @@ tr {
|
||||||
right: 0;
|
right: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
|
min-width: 100px;
|
||||||
|
min-height: 75px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell:nth-child(2n) {
|
.cell:nth-child(2n) {
|
||||||
|
|
|
@ -54,11 +54,11 @@ table.box th {
|
||||||
|
|
||||||
nav {
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background-color: #292981;
|
background-color: #292981;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nav form {
|
nav form {
|
||||||
|
@ -73,6 +73,12 @@ nav button {
|
||||||
padding: 8px 10px 8px 10px;
|
padding: 8px 10px 8px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nav .icon-only-button {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
@ -103,29 +109,29 @@ nav .icon {
|
||||||
filter: invert();
|
filter: invert();
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
nav ul {
|
||||||
margin: auto;
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1025px) {
|
nav ul li {
|
||||||
h1, h2 {
|
display: inline-block;
|
||||||
width: 80%;
|
text-indent: 0;
|
||||||
}
|
margin: 0;
|
||||||
|
padding: 0 10px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
nav .spacer {
|
||||||
width: 80%;
|
flex-grow: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
margin: auto;
|
||||||
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
h1, h2 {
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
main {
|
|
||||||
width: 95%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
@ -144,4 +150,24 @@ h2 .icon {
|
||||||
width: 25px;
|
width: 25px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
top: 3px;
|
top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ul.nav-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 100%;
|
||||||
|
width: 30%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.nav-list li {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
border: 1px solid black;
|
||||||
|
margin-bottom: -1px;
|
||||||
|
padding: 10px;
|
||||||
|
text-indent: 0;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
1
inventory/static/inventory/img/star-filled.svg
Normal file
1
inventory/static/inventory/img/star-filled.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M259.3 17.8L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0z"/></svg>
|
After Width: | Height: | Size: 516 B |
1
inventory/static/inventory/img/star-outline.svg
Normal file
1
inventory/static/inventory/img/star-outline.svg
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!-- Font Awesome Free 5.15.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) --><path d="M528.1 171.5L382 150.2 316.7 17.8c-11.7-23.6-45.6-23.9-57.4 0L194 150.2 47.9 171.5c-26.2 3.8-36.7 36.1-17.7 54.6l105.7 103-25 145.5c-4.5 26.3 23.2 46 46.4 33.7L288 439.6l130.7 68.7c23.2 12.2 50.9-7.4 46.4-33.7l-25-145.5 105.7-103c19-18.5 8.5-50.8-17.7-54.6zM388.6 312.3l23.7 138.4L288 385.4l-124.3 65.3 23.7-138.4-100.6-98 139-20.2 62.2-126 62.2 126 139 20.2-100.6 98z"/></svg>
|
After Width: | Height: | Size: 628 B |
|
@ -13,6 +13,15 @@
|
||||||
{% block header_bar %}
|
{% block header_bar %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
|
<div class="spacer">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% block header_icons %}
|
||||||
|
{% endblock %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
<form method="POST" action="{% url "logout" %}">
|
<form method="POST" action="{% url "logout" %}">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -9,28 +9,34 @@
|
||||||
<span class="small">{{ area.description}}</span>
|
<span class="small">{{ area.description}}</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_icons %}
|
||||||
|
{% include 'inventory/set_index.html' with item=area is_index=is_index %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<h3>Areas</h3>
|
{% if area.show_sub_area %}
|
||||||
<ul>
|
<h3>Areas</h3>
|
||||||
{% for a in area.area_related.all %}
|
<ul class="nav-list">
|
||||||
<li>
|
{% for a in area.area_related.all %}
|
||||||
<a href="{% url 'area-detail' a.id %}" title="{{ a.description }}">{{ a.name }}</a>
|
<li>
|
||||||
{% if user.is_staff %}
|
<a href="{% url 'area-detail' a.id %}" title="{{ a.description }}">{{ a.name }}</a>
|
||||||
<a class="edit" href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
{% if user.is_staff %}
|
||||||
{% endif %}
|
<a class="edit" href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||||
</li>
|
{% endif %}
|
||||||
{% empty %}
|
</li>
|
||||||
<li>No areas defined</li>
|
{% empty %}
|
||||||
{% endfor %}
|
<li>No areas defined</li>
|
||||||
</ul>
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ area.id }}&index=0">Create new area...</a></p>
|
<p><a href="{% url "admin:inventory_area_add" %}?container={{ area.id }}&index=0">Create new area...</a></p>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>Boxes</h3>
|
<h3>Containers</h3>
|
||||||
<ul>
|
<ul class="nav-list">
|
||||||
{% for box in area.box_related.all %}
|
{% for box in area.box_related.all %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'box-detail' box.id %}" title="{{ box.description }}">{{ box.name }} ({{ box.layout.name }})</a>
|
<a href="{% url 'box-detail' box.id %}" title="{{ box.description }}">{{ box.name }} ({{ box.layout.name }})</a>
|
||||||
|
@ -39,7 +45,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<li>No boxes defined</li>
|
<li>No containers defined</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
|
18
inventory/templates/inventory/box-detail.html
Normal file
18
inventory/templates/inventory/box-detail.html
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block head %}
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block title %}{{ object.name }}{% endblock %}
|
||||||
|
|
||||||
|
{% block header_bar %}
|
||||||
|
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||||
|
{{ object.name }}
|
||||||
|
<span class="small">{{ object.description}}</span></h2>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_icons %}
|
||||||
|
{% include 'inventory/set_index.html' with item=object is_index=is_index %}
|
||||||
|
{% endblock %}
|
|
@ -1,12 +1,4 @@
|
||||||
{% extends "base.html" %}
|
{% extends "inventory/box-detail.html" %}
|
||||||
|
|
||||||
{% block title %}{{ box.container.display_name }} - {{ box.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block header_bar %}
|
|
||||||
<a href="{{ box.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
|
||||||
{{ box.container.display_name }} - {{ box.name }}
|
|
||||||
<span class="small">{{ box.description}}</span>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "inventory/box-detail.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block title %}{{ object.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block header_bar %}
|
|
||||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
|
||||||
{{ object.name }}
|
|
||||||
<span class="small">{{ object.description}}</span>{% endblock %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<table class="box">
|
<table class="box">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "inventory/box-detail.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block title %}{{ object.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block header_bar %}
|
|
||||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
|
||||||
{{ object.name }}
|
|
||||||
<span class="small">{{ object.description}}</span></h2>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<table class="box">
|
<table class="box">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "inventory/box-detail.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block title %}{{ object.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block header_bar %}
|
|
||||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
|
||||||
{{ object.name }}
|
|
||||||
<span class="small">{{ object.description}}</span></h2>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<table class="box">
|
<table class="box">
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
{% extends "base.html" %}
|
{% extends "inventory/box-detail.html" %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load admin_urls %}
|
{% load admin_urls %}
|
||||||
|
|
||||||
{% block head %}
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block title %}{{ object.name }}{% endblock %}
|
|
||||||
|
|
||||||
{% block header_bar %}
|
|
||||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
|
||||||
{{ object.name }}
|
|
||||||
<span class="small">{{ object.description}}</span></h2>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% for part in layouted %}
|
{% for part in layouted %}
|
||||||
<table class="box">
|
<table class="box">
|
||||||
|
|
15
inventory/templates/inventory/set_index.html
Normal file
15
inventory/templates/inventory/set_index.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<form method="POST" action="{% url "index" %}">
|
||||||
|
{% csrf_token %}
|
||||||
|
<input type="hidden" name="index_id" value='{{ item.id }}'>
|
||||||
|
<button class="icon-only-button">
|
||||||
|
{% if is_index %}
|
||||||
|
<img class="icon" src="{% static "inventory/img/star-filled.svg" %}">
|
||||||
|
{% else %}
|
||||||
|
<img class="icon" src="{% static "inventory/img/star-outline.svg" %}">
|
||||||
|
{% endif %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
|
@ -9,9 +9,13 @@
|
||||||
<span class="small">{{ workshop.description}}</span>
|
<span class="small">{{ workshop.description}}</span>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block header_icons %}
|
||||||
|
{% include 'inventory/set_index.html' with item=workshop is_index=is_index %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h3>Areas</h3>
|
<h3>Areas</h3>
|
||||||
<ul>
|
<ul class="nav-list">
|
||||||
{% for area in workshop.area_related.all %}
|
{% for area in workshop.area_related.all %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'area-detail' area.id %}" title="{{ area.description }}">{{ area.name }}</a>
|
<a href="{% url 'area-detail' area.id %}" title="{{ area.description }}">{{ area.name }}</a>
|
||||||
|
@ -29,22 +33,24 @@
|
||||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ workshop.id }}&index=0">Create new area...</a></p>
|
<p><a href="{% url "admin:inventory_area_add" %}?container={{ workshop.id }}&index=0">Create new area...</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<h3>Boxes</h3>
|
{% if workshop.show_boxes %}
|
||||||
<ul>
|
<h3>Containers</h3>
|
||||||
{% for box in workshop.box_related.all %}
|
<ul class="nav-list">
|
||||||
<li>
|
{% for box in workshop.box_related.all %}
|
||||||
<a href="{% url 'box-detail' box.id %}" title="{{ box.description }}">{{ box.name }} ({{ box.layout.name }})</a>
|
<li>
|
||||||
({{ box.description }})
|
<a href="{% url 'box-detail' box.id %}" title="{{ box.description }}">{{ box.name }} ({{ box.layout.name }})</a>
|
||||||
{% if user.is_staff %}
|
({{ box.description }})
|
||||||
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
{% if user.is_staff %}
|
||||||
{% endif %}
|
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||||
</li>
|
{% endif %}
|
||||||
{% empty %}
|
</li>
|
||||||
<li>No boxes defined</li>
|
{% empty %}
|
||||||
{% endfor %}
|
<li>No containers defined</li>
|
||||||
</ul>
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<p><a href="{% url "admin:inventory_box_add" %}?container={{ workshop.id }}&index=0">Create new box...</a></p>
|
<p><a href="{% url "admin:inventory_box_add" %}?container={{ workshop.id }}&index=0">Create new container...</a></p>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -8,7 +8,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<ul>
|
<ul class="nav-list">
|
||||||
{% for workshop in object_list %}
|
{% for workshop in object_list %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url 'workshop-detail' workshop.id %}" title="{{ workshop.description }}">{{ workshop.name }}</a>
|
<a href="{% url 'workshop-detail' workshop.id %}" title="{{ workshop.description }}">{{ workshop.name }}</a>
|
||||||
|
@ -23,4 +23,5 @@
|
||||||
<p><a href="{% url "admin:inventory_workshop_add" %}?layout=1">Create new workshop...</a></p>
|
<p><a href="{% url "admin:inventory_workshop_add" %}?layout=1">Create new workshop...</a></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -17,7 +17,7 @@ Including another URLconf
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from .views import WorkshopListView, AreaListView, BoxListView, ItemListView, ManufacturerListView, DistributorListView
|
from .views import WorkshopListView, AreaListView, BoxListView, ItemListView, ManufacturerListView, DistributorListView
|
||||||
from .views import WorkshopView, AreaView, BoxView, ItemView, DistributorView, ManufacturerView
|
from .views import WorkshopView, AreaView, BoxView, ItemView, DistributorView, ManufacturerView, IndexView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('workshops', WorkshopListView.as_view(), name='workshop-list'),
|
path('workshops', WorkshopListView.as_view(), name='workshop-list'),
|
||||||
|
@ -33,4 +33,5 @@ urlpatterns = [
|
||||||
path('manufacturer/<int:pk>', ManufacturerView.as_view(), name='manufacturer-detail'),
|
path('manufacturer/<int:pk>', ManufacturerView.as_view(), name='manufacturer-detail'),
|
||||||
path('distributors', DistributorListView.as_view(), name='distributor-list'),
|
path('distributors', DistributorListView.as_view(), name='distributor-list'),
|
||||||
path('distributor/<int:pk>', DistributorView.as_view(), name='distributor-detail'),
|
path('distributor/<int:pk>', DistributorView.as_view(), name='distributor-detail'),
|
||||||
|
path('', IndexView.as_view(), name='index')
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,3 +4,14 @@ from .distributor import DistributorView, DistributorListView
|
||||||
from .item import ItemView, ItemListView
|
from .item import ItemView, ItemListView
|
||||||
from .manufacturer import ManufacturerView, ManufacturerListView
|
from .manufacturer import ManufacturerView, ManufacturerListView
|
||||||
from .workshop import WorkshopView, WorkshopListView
|
from .workshop import WorkshopView, WorkshopListView
|
||||||
|
from .index import IndexView
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
AreaView, AreaListView,
|
||||||
|
BoxView, BoxListView,
|
||||||
|
DistributorView, DistributorListView,
|
||||||
|
ItemView, ItemListView,
|
||||||
|
ManufacturerView, ManufacturerListView,
|
||||||
|
WorkshopView, WorkshopListView,
|
||||||
|
IndexView
|
||||||
|
]
|
|
@ -4,9 +4,11 @@ from django.views.generic import ListView, DetailView
|
||||||
|
|
||||||
from inventory.models import Area
|
from inventory.models import Area
|
||||||
|
|
||||||
|
from .utils import CanBeIndexMixin
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
class AreaView(DetailView):
|
class AreaView(CanBeIndexMixin, DetailView):
|
||||||
context_object_name = 'area'
|
context_object_name = 'area'
|
||||||
queryset = Area.objects.all().select_related(
|
queryset = Area.objects.all().select_related(
|
||||||
'container',
|
'container',
|
||||||
|
|
|
@ -6,9 +6,11 @@ from django.db.models import QuerySet
|
||||||
|
|
||||||
from inventory.models import Box
|
from inventory.models import Box
|
||||||
|
|
||||||
|
from .utils import CanBeIndexMixin
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
class BoxView(DetailView):
|
class BoxView(CanBeIndexMixin, DetailView):
|
||||||
context_object_name = 'box'
|
context_object_name = 'box'
|
||||||
template_name_field = 'template_name'
|
template_name_field = 'template_name'
|
||||||
queryset = Box.objects.all().select_related(
|
queryset = Box.objects.all().select_related(
|
||||||
|
|
30
inventory/views/index.py
Normal file
30
inventory/views/index.py
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.shortcuts import redirect
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
|
from django.views.generic import View
|
||||||
|
|
||||||
|
from inventory.models import Settings
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(login_required, name='dispatch')
|
||||||
|
class IndexView(View):
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
settings = Settings.objects.first()
|
||||||
|
if settings.default_container is not None:
|
||||||
|
print(settings.default_container.url)
|
||||||
|
return redirect(settings.default_container.url)
|
||||||
|
else:
|
||||||
|
return redirect(reverse('workshop-list'))
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if 'index_id' in request.POST:
|
||||||
|
settings = Settings.objects.first()
|
||||||
|
new_container_id = int(request.POST['index_id'])
|
||||||
|
if settings.default_container_id == new_container_id:
|
||||||
|
settings.default_container = None
|
||||||
|
else:
|
||||||
|
settings.default_container_id = new_container_id
|
||||||
|
settings.save()
|
||||||
|
return redirect(request.META['HTTP_REFERER'])
|
11
inventory/views/utils.py
Normal file
11
inventory/views/utils.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from inventory.models import Settings
|
||||||
|
|
||||||
|
|
||||||
|
class CanBeIndexMixin:
|
||||||
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context['is_index'] = Settings.objects.first().default_container_id == self.object.id
|
||||||
|
print(context)
|
||||||
|
return context
|
|
@ -1,12 +1,14 @@
|
||||||
|
from typing import Any, Dict
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.views.generic import ListView, DetailView
|
from django.views.generic import ListView, DetailView
|
||||||
|
|
||||||
from inventory.models import Workshop
|
from inventory.models import Workshop, Settings
|
||||||
|
from .utils import CanBeIndexMixin
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
class WorkshopView(DetailView):
|
class WorkshopView(CanBeIndexMixin, DetailView):
|
||||||
context_object_name = 'workshop'
|
context_object_name = 'workshop'
|
||||||
queryset = Workshop.objects.all().prefetch_related('area_related', 'box_related')
|
queryset = Workshop.objects.all().prefetch_related('area_related', 'box_related')
|
||||||
|
|
||||||
|
@ -14,4 +16,3 @@ class WorkshopView(DetailView):
|
||||||
@method_decorator(login_required, name='dispatch')
|
@method_decorator(login_required, name='dispatch')
|
||||||
class WorkshopListView(ListView):
|
class WorkshopListView(ListView):
|
||||||
model = Workshop
|
model = Workshop
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ from django.views.generic import RedirectView
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('', include('inventory.urls')),
|
path('', include('inventory.urls')),
|
||||||
path('', RedirectView.as_view(pattern_name='workshop-list', permanent=False)),
|
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
Loading…
Reference in a new issue