diff --git a/.gitignore b/.gitignore
index ee01551..8e883cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,6 +61,8 @@ cover/
local_settings.py
db.sqlite3
db.sqlite3-journal
+# Media
+media/
# Flask stuff:
instance/
diff --git a/shiftflow/templates/shiftflow/item_detail copy.html b/shiftflow/templates/shiftflow/item_detail copy.html
new file mode 100644
index 0000000..04d701e
--- /dev/null
+++ b/shiftflow/templates/shiftflow/item_detail copy.html
@@ -0,0 +1,120 @@
+{% extends 'base.html' %}
+
+{% block title %}ShiftFlow | {{ item.drawing_name|default:"Б/ч" }}{% endblock %}
+
+{% block content %}
+
-
- ShiftFlow
-
-
- ВОЙТИ В СИСТЕМУ
-
+
{% endblock %}
\ No newline at end of file
diff --git a/shiftflow/templates/shiftflow/registry.html b/shiftflow/templates/shiftflow/registry.html
new file mode 100644
index 0000000..44723a8
--- /dev/null
+++ b/shiftflow/templates/shiftflow/registry.html
@@ -0,0 +1,86 @@
+{% extends 'base.html' %}
+
+{% block content %}
+
+
+
+
+
+
+
+
+
+
+ {% for item in items %}
+
+ | {{ item.date|date:"d.m.y" }} |
+ {{ item.deal.number }} |
+ {{ item.machine.name }} |
+ {{ item.drawing_name }} |
+ {{ item.size_value }} |
+
+ {{ item.quantity_plan }} /
+ {{ item.quantity_fact }}
+ |
+ {{ item.material.name }} |
+
+ {% if item.drawing_file %}
+
+
+
+ {% endif %}
+ {% if item.extra_drawing %}
+
+
+
+ {% endif %}
+ |
+
+ {% if item.is_synced_1c %}
+
+ {% else %}
+
+ {% endif %}
+ |
+
+
+ {{ item.get_status_display }}
+
+ |
+
+ {% empty %}
+ | Заданий не найдено |
+ {% endfor %}
+
+
+
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/shiftflow/urls.py b/shiftflow/urls.py
index bbab240..c215e27 100644
--- a/shiftflow/urls.py
+++ b/shiftflow/urls.py
@@ -1,5 +1,5 @@
from django.urls import path
-from .views import IndexView, RegistryView
+from .views import IndexView, ItemUpdateView, RegistryView
urlpatterns = [
# Главная страница (путь пустой)
@@ -7,4 +7,5 @@ urlpatterns = [
# Реестр
path('registry/', RegistryView.as_view(), name='registry'),
+ path('item/
/', ItemUpdateView.as_view(), name='item_detail'),
]
\ No newline at end of file
diff --git a/shiftflow/views copy.py b/shiftflow/views copy.py
deleted file mode 100644
index 384b9b6..0000000
--- a/shiftflow/views copy.py
+++ /dev/null
@@ -1,72 +0,0 @@
-from django.shortcuts import render
-from .models import Item, Machine
-from django.utils import timezone
-from datetime import datetime
-
-def items_list_view(request):
- # Если не авторизован, просто отдаем пустой контекст для страницы входа
- if not request.user.is_authenticated:
- return render(request, 'shiftflow/items_list.html', {})
-
- # ОПРЕДЕЛЕНИЕ РОЛИ (Ищем в группах Джанго или ставим суперюзера)
- user_role = 'guest'
- if request.user.is_superuser:
- user_role = 'admin'
- elif request.user.groups.filter(name='Технолог').exists():
- user_role = 'technologist'
- elif request.user.groups.filter(name='Мастер').exists():
- user_role = 'master'
- elif request.user.groups.filter(name='Оператор').exists():
- user_role = 'operator'
- elif request.user.groups.filter(name='Учетчик').exists():
- user_role = 'clerk'
-
- # ФИЛЬТРЫ
- all_machines = Machine.objects.all()
- all_machine_ids = list(all_machines.values_list('id', flat=True))
-
- # Проверяем, был ли нажат фильтр (есть ли параметр 'filtered' в URL)
- is_filtered = 'filtered' in request.GET
-
- if is_filtered:
- selected_machines = [int(x) for x in request.GET.getlist('m_ids')]
- selected_statuses = request.GET.getlist('statuses')
- start_date = request.GET.get('start_date')
- end_date = request.GET.get('end_date')
- else:
- # Значения по умолчанию (при первом заходе или сбросе)
- selected_machines = all_machine_ids
- selected_statuses = ['work', 'partial'] # В работе и недоделы
- start_date = timezone.now().strftime('%Y-%m-%d')
- end_date = timezone.now().strftime('%Y-%m-%d')
-
- # ВЫБОРКА ДАННЫХ
- items = Item.objects.select_related('deal', 'material', 'machine')
-
- # Защита логики: если станки не выбраны — список пуст
- if not selected_machines:
- items = Item.objects.none()
- else:
- items = items.filter(
- machine_id__in=selected_machines,
- status__in=selected_statuses,
- date__range=[start_date, end_date]
- )
-
- # Разбиваем по статусам для вывода в разные таблицы (если они выбраны в фильтре)
- in_work = items.filter(status='work') if 'work' in selected_statuses else None
- backlog = items.filter(status='partial') if 'partial' in selected_statuses else None
- done_items = items.filter(status='done') if 'done' in selected_statuses else None
-
- context = {
- 'user_role': user_role,
- 'machines': all_machines,
- 'selected_machines': selected_machines,
- 'selected_statuses': selected_statuses,
- 'start_date': start_date,
- 'end_date': end_date,
- 'in_work': in_work,
- 'backlog': backlog,
- 'done_items': done_items,
- }
- return render(request, 'shiftflow/items_list.html', context)
\ No newline at end of file
diff --git a/shiftflow/views.py b/shiftflow/views.py
index 3fa9b78..4731e9b 100644
--- a/shiftflow/views.py
+++ b/shiftflow/views.py
@@ -1,5 +1,6 @@
from django.shortcuts import redirect
-from django.views.generic import TemplateView, ListView
+from django.urls import reverse_lazy
+from django.views.generic import TemplateView, ListView, UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import Item # Проверь, как точно называется твоя модель деталей/заказов
@@ -17,16 +18,40 @@ class IndexView(TemplateView):
# Класс реестра деталей (защищен LoginRequiredMixin)
class RegistryView(LoginRequiredMixin, ListView):
model = Item
- template_name = 'shiftflow/items_list.html'
+ template_name = 'shiftflow/registry.html'
context_object_name = 'items'
def get_queryset(self):
# Позже здесь добавим: .filter(machine__in=request.user.profile.machines.all())
- return Item.objects.all().order_by('-id')
+ # Сортируем: сначала статус (по алфавиту или логике choices),
+ # затем по дате (свежие сверху), по станку и по номеру сделки
+ return Item.objects.all().order_by('status', '-date', 'machine__name', 'deal__number')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# Передаем роль в шаблон, чтобы скрывать/показывать кнопки
if hasattr(self.request.user, 'profile'):
context['user_role'] = self.request.user.profile.role
- return context
\ No newline at end of file
+ return context
+
+# Вьюха детального вида и редактирования
+class ItemUpdateView(LoginRequiredMixin, UpdateView):
+ model = Item
+ template_name = 'shiftflow/item_detail.html'
+ # Перечисляем поля, которые можно редактировать (укажи нужные)
+ fields = [
+ 'drawing_name', 'machine', 'quantity_plan', 'quantity_fact',
+ 'material', 'size_value', 'status', 'is_synced_1c', 'extra_drawing'
+ ]
+ context_object_name = 'item'
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ # Обязательно добавляем роль в контекст этого шаблона!
+ if hasattr(self.request.user, 'profile'):
+ context['user_role'] = self.request.user.profile.role
+ return context
+
+ def get_success_url(self):
+ # После сохранения возвращаемся в реестр
+ return reverse_lazy('registry')
\ No newline at end of file
diff --git a/static/css/style.css b/static/css/style.css
index f763d1c..540a4fa 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -1,38 +1,105 @@
-/* Акцентные цвета для темной темы */
-[data-bs-theme="dark"] {
- --bs-body-bg: #121212;
- --bs-body-color: #e9ecef;
- --bs-accent: #ffc107; /* Тот самый желтый */
+/* --- ГЛОБАЛЬНЫЕ НАСТРОЙКИ --- */
+
+body {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ /* Убрали общее центрирование, чтобы реестр был сверху */
+ justify-content: flex-start;
}
-[data-bs-theme="dark"] .table-custom-header {
+/* Навбар и Футер: жестко фиксируем цвет для обеих тем */
+.navbar, .footer-custom {
+ /* Темный графит, который хорошо смотрится и там, и там */
+ background-color: #2c3034 !important;
+ border-bottom: 1px solid #3d4246 !important;
+ border-top: 1px solid #3d4246 !important; /* Для футера */
+}
+
+/* Принудительно светлый текст для футера и навбара */
+.navbar .nav-link,
+.navbar .navbar-brand,
+.footer-custom span,
+.footer-custom strong {
+ color: #e9ecef !important;
+}
+
+/* Состояние активной ссылки в меню */
+.nav-link.active {
+ color: var(--bs-accent) !important;
+ border-bottom: 2px solid var(--bs-accent);
+}
+
+/* Цвет ссылок в темном навбаре, чтобы не сливались */
+.navbar .nav-link, .navbar .navbar-brand, .navbar .text-reset {
+ color: #e9ecef !important;
+}
+
+/* --- РЕЕСТР --- */
+
+/* Делаем строку таблицы визуально кликабельной */
+.clickable-row {
+ cursor: pointer;
+ transition: background-color 0.2s ease;
+}
+
+/* Подсветка при наведении */
+.clickable-row:hover {
+ background-color: rgba(255, 193, 7, 0.05) !important; /* Легкий отсвет нашего акцента */
+}
+
+/* --- ТЕМЫ --- */
+
+[data-bs-theme="dark"] {
+ --bs-body-bg: #121212; /* Глубокий черный фон */
+ --bs-body-color: #e9ecef; /* Светло-серый текст */
+ --bs-accent: #ffc107; /* Желтый акцент (Amber) */
+}
+
+[data-bs-theme="light"] {
+ --bs-body-bg: #f8f9fa; /* Почти белый фон */
+ --bs-body-color: #212529; /* Темный текст */
+ --bs-accent: #0d6efd; /* Синий акцент для светлой темы */
+}
+
+/* --- ТАБЛИЦА И КАРТОЧКИ --- */
+
+/* Заголовок таблицы: всегда темный с акцентным текстом */
+.table-custom-header {
background-color: #1e1e1e !important;
color: var(--bs-accent) !important;
+ font-size: 0.9rem;
+ text-transform: uppercase; /* Все буквы заглавные */
}
-/* Акцентные цвета для светлой темы */
-[data-bs-theme="light"] {
- --bs-body-bg: #e2e2e2;
- --bs-body-color: #212529;
- --bs-accent: #0f5132; /* Темно-зеленый */
+/* Фикс для таблиц в светлой теме */
+[data-bs-theme="light"] .table {
+ --bs-table-bg: #ffffff;
+ --bs-table-color: #212529;
+ --bs-table-hover-bg: #f1f3f5;
}
-/* Общие классы */
-.text-accent {
- color: var(--bs-accent) !important;
-}
+/* --- ВСПОМОГАТЕЛЬНЫЕ КЛАССЫ --- */
+/* Текст акцентного цвета */
+.text-accent { color: var(--bs-accent) !important; }
+
+/* Кнопка с контуром акцентного цвета */
.btn-outline-accent {
color: var(--bs-accent) !important;
border-color: var(--bs-accent) !important;
}
+/* Состояние кнопки при наведении */
.btn-outline-accent:hover {
background-color: var(--bs-accent) !important;
- color: #000 !important;
+ color: #000 !important; /* Текст становится черным для контраста */
}
-/* Фикс для навигации */
-.nav-link.active {
- border-bottom: 2px solid var(--bs-accent);
+/* Специальный класс для центрирования окна логина (вернем его только там) */
+.flex-center-center {
+ display: flex;
+ flex-grow: 1;
+ align-items: center; /* Центр по вертикали */
+ justify-content: center; /* Центр по горизонтали */
}
\ No newline at end of file
diff --git a/templates/base.html b/templates/base.html
index 8a47f62..aad3d88 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -15,7 +15,7 @@
{% include 'components/_navbar.html' %}
{% endif %}
-
+
{% block content %}{% endblock %}
diff --git a/templates/components/_footer.html b/templates/components/_footer.html
index 7a3912f..5e6a20c 100644
--- a/templates/components/_footer.html
+++ b/templates/components/_footer.html
@@ -1,7 +1,8 @@
-