Compare commits

..

No commits in common. "441d3450851fa5443b0a07fddbba25a4f6df4f9b" and "b256d12feebe934141e94cb4a9c4acfc3dbdecc2" have entirely different histories.

52 changed files with 52 additions and 368 deletions

View file

@ -20,12 +20,12 @@ As configured by default you will need the following:
### Installation ### Installation
This is a standard Django 5.1 application, if you know how to deploy those the This is a standard Django 5.0 application, if you know how to deploy those the
following might sound familiar: following might sound familiar:
1. Checkout repository: `git clone https://github.com/dunkelstern/inventory.git` 1. Checkout repository: `git clone https://github.com/dunkelstern/inventory.git`
2. Change to checkout: `cd inventory` 2. Change to checkout: `cd inventory`
3. Install virtualenv and dependencies: `poetry install --no-root` 3. Install virtualenv and dependencies: `poetry install`
4. Migrate the Database: `poetry run python manage.py migrate` 4. Migrate the Database: `poetry run python manage.py migrate`
5. Create an admin user: `poetry run python manage.py createsuperuser` 5. Create an admin user: `poetry run python manage.py createsuperuser`
6. Run the server 6. Run the server
@ -43,35 +43,16 @@ go to `http://localhost:8000` to enter the inventory management system directly
2. For editing parts the Django admin interface is used, so edit-links will only 2. For editing parts the Django admin interface is used, so edit-links will only
appear if the currently logged in user is a `staff` user (set the checkbox appear if the currently logged in user is a `staff` user (set the checkbox
in the admin area). in the admin area).
3. If you want to change the default number of items on paginated views you can
set the page size in the settings by providing a parameter `PAGE_SIZE`
4. If you want to run this as a systemd service see the
[the service file](inventory.service) in the root of this repository for an
example.
### Screenshots ### Screenshots
#### Login
To be able to list all parts you'll need to login. You basically have three
levels of permissions:
- Normal Users
- Staff Users
- Admin Users
Normal users can view all parts and search, Staff users may edit in addition.
Admin users can create Users and do everything (like adding new layouts, etc.).
![Login page](docs/login.jpeg)
#### Overview Page #### Overview Page
here we have a layer of containers, you may nest multiple containers into each here we have a layer of containers, you may nest multiple containers into each
other, for example to define a cupboard which contains multiple boxes of parts, other, for example to define a cupboard which contains multiple boxes of parts,
or multiple rooms in your workshop that contain cupboards, etc. or multiple rooms in your workshop that contain cupboards, etc.
![Overview](docs/main_area.jpeg) ![Overview](docs/example_overview.png)
#### Box View #### Box View
@ -80,9 +61,9 @@ compartments, number of items per compartment and layout of compartments
themselves) all by yourself in the admin backend, by default the database comes themselves) all by yourself in the admin backend, by default the database comes
with an assortment of Ikea and Raaco sorter boxes. with an assortment of Ikea and Raaco sorter boxes.
![Box view 1](docs/smd_box.jpeg) ![Box view 1](docs/example_box.png)
![Box view 2](docs/smd_box_marker.jpeg) ![Box view 2](docs/example_box2.png)
The Overview and Box views are designed to be used on a touch-screen and the HTML, The Overview and Box views are designed to be used on a touch-screen and the HTML,
CSS and Javascript are designed to work on older Hardware (Apple iOS 9 has been CSS and Javascript are designed to work on older Hardware (Apple iOS 9 has been
@ -93,7 +74,7 @@ tested at lowest, so this works from iPad 2 up to the newest pro).
This is the detail view of a part, this is useful to find all parts by manufacturer This is the detail view of a part, this is useful to find all parts by manufacturer
or distributor, or when a part has multiple datasheets. or distributor, or when a part has multiple datasheets.
![Detail view](docs/part.jpeg) ![Detail view](docs/example_detail.png)
#### Part edit view #### Part edit view
@ -101,58 +82,4 @@ Editing is done on the standard Django admin interface, so all users that have n
*staff* privileges only can view all parts, all with *staff* privileges have access *staff* privileges only can view all parts, all with *staff* privileges have access
to the django admin backend and can edit parts too. to the django admin backend and can edit parts too.
![Edit view](docs/edit_part.jpeg) ![Edit view](docs/example_edit.png)
#### Search function
If you click on the loupe symbol on top you'll get a popup searchbox for a fulltext
search through all parts.
![Search view](docs/search.jpeg)
The search results contain a link to the container the object is stored in and if
you click that link the compartment will be hilighted so you can find the part faster:
![Search view hilight](docs/search_hilight.jpeg)
You can also reach the first datasheet directly from the search results by clicking
on the icon in front of the description text.
#### Tag cloud
If you select the tag icon in the header bar you will get a dynamically searchable
tag cloud if you do not know a search term exactly or if you need a group of parts
(e.g. give me all transistors of any type).
![Tag cloud](docs/tags.jpeg)
The detail view of a tag will list all items with that tag, as well as all containers
or footprints that have been assigned this tag:
![Tag detail](docs/tag_detail.jpeg)
Editing is in Django backend:
![Tag editing](docs/edit_tag.jpeg)
#### Other options
You can browse your parts inventory by distributor or part manufacturer if you want:
![Manufacturer list](docs/manufacturer.jpeg)
![Manufacturer detail](docs/manufacturer_detail.jpeg)
![Manufacturer edit](docs/edit_manufacturer.jpeg)
The distributor views have a convenient search box if a parametrized search link has
been set up in the backend.
This link will be used to link to a part directly if a part has a distributor part
number saved.
![Distributor list](docs/distributor.jpeg)
![Distributor detail](docs/distributor_detail.jpeg)
![Distributor edit](docs/edit_distributor.jpeg)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

BIN
docs/example_box.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

BIN
docs/example_box2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

BIN
docs/example_detail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
docs/example_edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

BIN
docs/example_overview.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 KiB

View file

@ -46,10 +46,6 @@ td.disabled {
align-items: center; align-items: center;
} }
.search-hilight {
background-color: #ffa0a0 !important;
}
.cell:nth-child(2n) { .cell:nth-child(2n) {
background-color: #f8f8f8; background-color: #f8f8f8;
} }
@ -145,26 +141,4 @@ td.disabled {
.dock-top { .dock-top {
margin-top: -1px; margin-top: -1px;
}
@media (max-width: 1024px) {
.cell .form_factor {
font-size: 75%;
top: 3px;
right: 7px;
}
.cell .price {
font-size: 75%;
bottom: 3px;
left: 3px;
}
.cell .shop {
bottom: 3px;
right: 7px;
width: 10px;
height: 10px;
}
} }

View file

@ -88,18 +88,14 @@ h1 {
} }
h1 .small { h1 .small {
font-size: 12px; font-size: 12pt;
font-weight: normal; font-weight: normal;
color: #808080; color: #808080;
} }
h2 .small { h2 .small {
display: inline-block; font-size: 12pt;
max-width: 400px;
font-size: 12px;
font-weight: normal; font-weight: normal;
vertical-align: middle;
margin-left: 5px;
} }
nav h2 { nav h2 {
@ -139,17 +135,9 @@ main {
padding: 20px; padding: 20px;
} }
body {
font-size: 12px;
}
@media (max-width: 1024px) { @media (max-width: 1024px) {
body { body {
font-size: 10px; font-size: 12px;
}
h2 .small {
max-width: 200px;
} }
} }
@ -219,6 +207,7 @@ ul.tag-list {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 6px; gap: 6px;
max-width: 500px;
text-indent: 0; text-indent: 0;
padding-left: 0; padding-left: 0;
} }
@ -302,23 +291,4 @@ table.list tbody td {
.right { .right {
text-align: right; text-align: right;
} }
#search-container {
display: none;
position: absolute;
background-color: #292981;
padding: 10px;
border: 1px solid white;
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.8);
}
.search-result em {
color: #c00000;
}
.search-result .small {
font-size: 10px;
font-weight: normal;
color: #808080;
}

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>

Before

Width:  |  Height:  |  Size: 463 B

View file

@ -23,29 +23,6 @@
{% endblock %} {% endblock %}
</ul> </ul>
<ul class="icon-nav"> <ul class="icon-nav">
<li>
<script>
function showSearch(e) {
const searchBox = document.getElementById("search-container");
if ((searchBox.style.display === '') || (searchBox.style.display === 'none')) {
searchBox.style.display = 'block';
searchBox.getElementsByTagName('input')[0].focus();
} else {
searchBox.style.display = 'none';
}
if (e) {
e.preventDefault();
}
}
</script>
<a href="{% url 'search' %}" onclick="showSearch(event)"><img class="icon" title="Search" src="{% static "inventory/img/search.svg" %}"></a>
<div id="search-container">
<form action="{% url 'search' %}" method="get">
<input name="q" id="search" type="text" placeholder="Search">
<button type="submit">Search</button>
</form>
</div>
</li>
<li> <li>
<a href="{% url 'manufacturer-list' %}"><img class="icon" title="Manufacturers" src="{% static "inventory/img/manufacturer.svg" %}"></a> <a href="{% url 'manufacturer-list' %}"><img class="icon" title="Manufacturers" src="{% static "inventory/img/manufacturer.svg" %}"></a>
</li> </li>
@ -56,7 +33,7 @@
<a href="{% url 'tag-list' %}"><img class="icon" title="Tags" src="{% static "inventory/img/tags.svg" %}"></a> <a href="{% url 'tag-list' %}"><img class="icon" title="Tags" src="{% static "inventory/img/tags.svg" %}"></a>
</li> </li>
<li> <li>
<a href="{% url 'index' %}"><img class="icon" title="Workshops" src="{% static "inventory/img/workshop.svg" %}"></a> <a href="{% url 'workshop-list' %}"><img class="icon" title="Workshops" src="{% static "inventory/img/workshop.svg" %}"></a>
</li> </li>
</ul> </ul>

View file

@ -15,7 +15,7 @@
<h3>Items</h3> <h3>Items</h3>
<ul> <ul>
{% for item in box.item_related.all %} {% for item in box.item_related.all %}
<li {% if hilight == item.id %}class="hilighted"{% endif %}><a href="{% url 'item-detail' item.id %}" title="{{ item.description }}">{{ item.name }}</a></li> <li><a href="{% url 'item-detail' item.id %}" title="{{ item.description }}">{{ item.name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}

View file

@ -9,7 +9,7 @@
<td rowspan="2"> <td rowspan="2">
<div class="compartments-vertical"> <div class="compartments-vertical">
{% for compartment in layouted.0.0 %} {% for compartment in layouted.0.0 %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -17,7 +17,7 @@
<td class="triple-height" colspan="3"> <td class="triple-height" colspan="3">
<div class="compartments"> <div class="compartments">
{% for compartment in layouted.0.1 %} {% for compartment in layouted.0.1 %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -27,7 +27,7 @@
<td class="triple-height"> <td class="triple-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -9,7 +9,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in layouted.0 %} {% for compartment in layouted.0 %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -10,7 +10,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -20,7 +20,7 @@
<td rowspan="2"> <td rowspan="2">
<div class="compartments-vertical"> <div class="compartments-vertical">
{% for compartment in layouted.1.0 %} {% for compartment in layouted.1.0 %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -29,7 +29,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -40,7 +40,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -11,7 +11,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -27,7 +27,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -42,7 +42,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -11,7 +11,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -27,7 +27,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -42,7 +42,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -11,7 +11,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -27,7 +27,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -42,7 +42,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -11,7 +11,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -10,7 +10,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -21,7 +21,7 @@
<td> <td>
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -10,7 +10,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -21,7 +21,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -32,7 +32,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -45,7 +45,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -10,7 +10,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -21,7 +21,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -32,7 +32,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>
@ -45,7 +45,7 @@
<td class="double-height"> <td class="double-height">
<div class="compartments"> <div class="compartments">
{% for compartment in item %} {% for compartment in item %}
{% include "inventory/cell.html" with item=compartment hilight=hilight %} {% include "inventory/cell.html" with item=compartment %}
{% endfor %} {% endfor %}
</div> </div>
</td> </td>

View file

@ -1,6 +1,7 @@
{% load static %} {% load static %}
{% load admin_urls %} {% load admin_urls %}
<div class="cell{% if hilight == item.id %} search-hilight{% endif %}">
<div class="cell">
{% if item.name %} {% if item.name %}
{% if item.metadata.package %} {% if item.metadata.package %}
<div class="package">{{ item.metadata.package }}</div> <div class="package">{{ item.metadata.package }}</div>

View file

@ -1,6 +1,6 @@
{% load static %} {% load static %}
<div class="pagination" id="{{ id }}"> <div class="pagination" id="paginator_top">
{% if paginator.has_previous %} {% if paginator.has_previous %}
<a href="{{ url }}?{{ param }}_page=1#{{ id }}"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="First page"></a> <a href="{{ url }}?{{ param }}_page=1#{{ id }}"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="First page"></a>
<a href="{{ url }}?{{ param }}_page={{ paginator.previous_page_number }}#{{ id }}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="Previous page"></a> <a href="{{ url }}?{{ param }}_page={{ paginator.previous_page_number }}#{{ id }}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="Previous page"></a>

View file

@ -1,15 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% block title %}Search{% endblock %}
{% block header_bar %}
Search
{% endblock %}
{% block content %}
<form method="get" action="{% url 'search' %}">
<input type="text" id="search" name="q">
<button type="submit">Search</button>
</form>
{% endblock %}

View file

@ -1,43 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% load hilight %}
{% block title %}Search{% endblock %}
{% block header_bar %}
Search
{% endblock %}
{% block content %}
<form method="get" action="{% url 'search' %}">
<input type="text" id="search" name="q" value="{{ q }}">
<button type="submit">Search</button>
</form>
<h2>Search result for '{{ q }}'</h2>
{% for result in results %}
<div class="search-result">
<h3><a href="{{ result.url }}">{{ result.title | hilight:tokens }}</a></h3>
{{ result.text | safe }}
<hr>
</div>
{% empty %}
<p>Noting found</p>
{% endfor %}
{% if pages > 1 %}
<div class="pagination">
{% if page > 1 %}
<a href="{% url 'search' %}?q={{ q }}&page=1"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="First page"></a>
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'-1'}}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="Previous page"></a>
{% endif %}
{{ page }}/{{ pages }}
{% if page < pages %}
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'1'}}"><img src="{% static 'inventory/img/next.svg' %}" class="icon" title="Next page"></a>
<a href="{% url 'search' %}?q={{ q }}&page={{ pages }}"><img src="{% static 'inventory/img/last.svg' %}" class="icon" title="Last page"></a>
{% endif %}
</div>
{% endif %}
{% endblock %}

View file

@ -1,17 +0,0 @@
{% load static %}
{% load hilight %}
<p>
{% if item.documentation.all %}
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}"></a>
{% endif %}
{{ item | hilight:tokens }}
<span class="small">{{item.form_factor.name}}</span>
</p>
<p>Contained in: <a href="{{ item.container_url }}?hilight={{ item.id }}">{{ item.container.display_name }}</a></p>
<ul class="tag-list">
{% for tag in item.all_tags %}
<li><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
{% empty %}
No tags
{% endfor %}
</ul>

View file

@ -5,13 +5,13 @@
{% block title %}Tags{% endblock %} {% block title %}Tags{% endblock %}
{% block header_bar %} {% block header_bar %}
Inventory Management - Tags Tags
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<script> <script>
function updateSearch(e) { function updateSearch(e) {
const input = document.getElementById('tag-search'); const input = document.getElementById('search');
const text = input.value.toLowerCase(); const text = input.value.toLowerCase();
if (text.length >= 2) { if (text.length >= 2) {
@ -39,7 +39,8 @@
} }
</script> </script>
<form> <form>
<input type="text" id="tag-search" name="tag-search" placeholder="Search" oninput="updateSearch(event)"> <label for="search">Search:</label>
<input type="text" id="search" name="search" oninput="updateSearch(event)">
<button onclick="updateSearch(event)">Search</button> <button onclick="updateSearch(event)">Search</button>
</form> </form>
<ul class="tag-list" id="tagcloud"> <ul class="tag-list" id="tagcloud">

View file

@ -33,8 +33,7 @@ from .views import (
DistributorView, DistributorView,
ManufacturerView, ManufacturerView,
IndexView, IndexView,
TagView, TagView
SearchView
) )
urlpatterns = [ urlpatterns = [
@ -53,6 +52,5 @@ urlpatterns = [
path('distributor/<int:pk>', DistributorView.as_view(), name='distributor-detail'), path('distributor/<int:pk>', DistributorView.as_view(), name='distributor-detail'),
path('tags', TagListView.as_view(), name='tag-list'), path('tags', TagListView.as_view(), name='tag-list'),
path('tag/<int:pk>', TagView.as_view(), name='tag-detail'), path('tag/<int:pk>', TagView.as_view(), name='tag-detail'),
path('search', SearchView.as_view(), name='search'),
path('', IndexView.as_view(), name='index') path('', IndexView.as_view(), name='index')
] ]

View file

@ -6,7 +6,6 @@ from .manufacturer import ManufacturerView, ManufacturerListView
from .workshop import WorkshopView, WorkshopListView from .workshop import WorkshopView, WorkshopListView
from .index import IndexView from .index import IndexView
from .tag import TagListView, TagView from .tag import TagListView, TagView
from .search import SearchView
__all__ = [ __all__ = [
AreaView, AreaListView, AreaView, AreaListView,
@ -16,6 +15,5 @@ __all__ = [
ManufacturerView, ManufacturerListView, ManufacturerView, ManufacturerListView,
WorkshopView, WorkshopListView, WorkshopView, WorkshopListView,
IndexView, IndexView,
TagView, TagListView, TagView, TagListView
SearchView
] ]

View file

@ -49,11 +49,9 @@ class BoxView(CanBeIndexMixin, DetailView):
return result, idx return result, idx
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
hilighted = int(request.GET.get('hilight', "0"))
self.object = cast(Box, self.get_object()) self.object = cast(Box, self.get_object())
context = self.get_context_data(object=self.object) context = self.get_context_data(object=self.object)
context['layouted'], _ = self.layout(self.object.item_related.all().order_by('index'), self.object.layout.data) context['layouted'], _ = self.layout(self.object.item_related.all().order_by('index'), self.object.layout.data)
context['hilight'] = hilighted
return self.render_to_response(context) return self.render_to_response(context)

View file

@ -1,84 +0,0 @@
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import View
from django.shortcuts import render
from django.urls import reverse
from django.template.loader import get_template
from django.db.models import Q
from django.core.paginator import Paginator
from django.conf import settings
from re import finditer
from inventory.models import Item
@method_decorator(login_required, name='dispatch')
class SearchView(View):
def get(self, request):
page = int(request.GET.get('page', "1"))
query = request.GET.get('q', None)
if not query:
return render(request, "inventory/search.html")
results: list[dict[str, str]] = []
tokens = query.split(" ")
tokens = map(str.lower, map(str.strip, tokens))
q: Q = None
item_template = get_template("inventory/search_result_item.html")
t: list[str] = []
for token in tokens:
combiner = 'or'
if token.startswith("+"):
token = token[1:]
combiner = 'and'
elif token.startswith("-"):
token = token[1:]
combiner = 'and not'
t.append(token)
q1 = Q(name__icontains=f'{token}')
q2 = Q(description__icontains=f'{token}')
q3 = Q(tags__name__icontains=f'{token}')
q4 = Q(tags__description__icontains=f'{token}')
q5 = Q(form_factor__tags__name__icontains=f'{token}')
q6 = Q(form_factor__tags__description__icontains=f'{token}')
qx = q1 | q2 | q3 | q4 | q5 | q6
if q == None:
q = qx
elif combiner == 'or':
q = q | qx
elif combiner == 'and':
q = q & qx
elif combiner == 'and not':
q = q & ~qx
items = Item.objects.filter(q).select_related("container", "form_factor").prefetch_related("tags", "documentation").distinct()
# FIXME: Rank search results
paginator = Paginator(items, getattr(settings, "PAGE_SIZE", 10))
for item in paginator.get_page(page):
text = item_template.render({
"item": item,
"tokens": t
})
result = {
"title": item.name,
"text": text,
"url": reverse('item-detail', kwargs={"pk": item.pk})
}
results.append(result)
return render(request, "inventory/search_result.html", {
"q": query,
"results": results,
"tokens": t,
"page": page,
"pages": paginator.num_pages
})

View file

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "inventory" name = "inventory"
version = "1.0.0" version = "0.1.0"
description = "" description = ""
authors = ["Johannes Schriewer <hallo@dunkelstern.de>"] authors = ["Johannes Schriewer <hallo@dunkelstern.de>"]