Optimize order statuses list page with compact card layout
- Changed from table to card-based design for better space efficiency - Reduced padding and margins to fit 15+ statuses on screen without scrolling - Minimized font sizes and icon sizes for compact display - Added proper styling for edit and delete buttons with hover effects - Improved visual hierarchy with color indicators and badges - Maintained all functionality while improving UX 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -163,7 +163,28 @@ class ConfigurableKitProductCreateView(LoginRequiredMixin, CreateView):
|
||||
return self.form_invalid(form)
|
||||
|
||||
if not option_formset.is_valid():
|
||||
messages.error(self.request, 'Пожалуйста, исправьте ошибки в вариантах.')
|
||||
# Логирование ошибок formset
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"Option formset errors: {option_formset.errors}")
|
||||
logger.error(f"Option formset non-form errors: {option_formset.non_form_errors()}")
|
||||
|
||||
# Показываем детальные ошибки
|
||||
error_msg = 'Ошибки в вариантах:\n'
|
||||
for i, form_errors in enumerate(option_formset.errors):
|
||||
if form_errors:
|
||||
error_msg += f' Вариант {i+1}: {form_errors}\n'
|
||||
if option_formset.non_form_errors():
|
||||
error_msg += f' Общие ошибки: {option_formset.non_form_errors()}\n'
|
||||
|
||||
messages.error(self.request, error_msg)
|
||||
return self.render_to_response(self.get_context_data(form=form, option_formset=option_formset, attribute_formset=attribute_formset))
|
||||
|
||||
# Валидация что каждый вариант имеет выбранный комплект
|
||||
validation_errors = self._validate_variant_kits(option_formset)
|
||||
if validation_errors:
|
||||
for error in validation_errors:
|
||||
messages.error(self.request, error)
|
||||
return self.render_to_response(self.get_context_data(form=form, option_formset=option_formset, attribute_formset=attribute_formset))
|
||||
|
||||
if not attribute_formset.is_valid():
|
||||
@@ -297,6 +318,48 @@ class ConfigurableKitProductCreateView(LoginRequiredMixin, CreateView):
|
||||
|
||||
ConfigurableKitProductAttribute.objects.create(**create_kwargs)
|
||||
|
||||
def _validate_variant_kits(self, option_formset):
|
||||
"""
|
||||
Валидация что каждый вариант имеет выбранный комплект.
|
||||
Возвращает список ошибок (пустой список если нет ошибок).
|
||||
"""
|
||||
errors = []
|
||||
|
||||
for idx, option_form in enumerate(option_formset):
|
||||
# Пропускаем удаленные или пустые формы
|
||||
if not option_form.cleaned_data or self._should_delete_form(option_form, option_formset):
|
||||
continue
|
||||
|
||||
# Получаем kit_id из POST данных (он там должен быть установлен JavaScript'ом)
|
||||
kit_id = self.request.POST.get(f'options-{idx}-kit', '').strip()
|
||||
|
||||
if not kit_id:
|
||||
# Пытаемся получить из cleaned_data
|
||||
kit_id = option_form.cleaned_data.get('kit')
|
||||
|
||||
if not kit_id:
|
||||
# Если у варианта есть выбранные атрибуты, но нет комплекта - это ошибка
|
||||
has_attributes = any(
|
||||
option_form.cleaned_data.get(k)
|
||||
for k in option_form.cleaned_data.keys()
|
||||
if k.startswith('attribute_')
|
||||
)
|
||||
|
||||
if has_attributes:
|
||||
# Собираем названия выбранных атрибутов для сообщения об ошибке
|
||||
selected_attrs = [
|
||||
str(option_form.cleaned_data.get(k))
|
||||
for k in option_form.cleaned_data.keys()
|
||||
if k.startswith('attribute_') and option_form.cleaned_data.get(k)
|
||||
]
|
||||
errors.append(
|
||||
f'Вариант {idx + 1} ({", ".join(selected_attrs)}): '
|
||||
f'не выбран комплект. Пожалуйста, выберите значения атрибутов которые '
|
||||
f'привязаны к одному комплекту.'
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def _should_delete_form(form, formset):
|
||||
"""Проверить должна ли форма быть удалена"""
|
||||
@@ -378,7 +441,28 @@ class ConfigurableKitProductUpdateView(LoginRequiredMixin, UpdateView):
|
||||
return self.form_invalid(form)
|
||||
|
||||
if not option_formset.is_valid():
|
||||
messages.error(self.request, 'Пожалуйста, исправьте ошибки в вариантах.')
|
||||
# Логирование ошибок formset
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"Option formset errors: {option_formset.errors}")
|
||||
logger.error(f"Option formset non-form errors: {option_formset.non_form_errors()}")
|
||||
|
||||
# Показываем детальные ошибки
|
||||
error_msg = 'Ошибки в вариантах:\n'
|
||||
for i, form_errors in enumerate(option_formset.errors):
|
||||
if form_errors:
|
||||
error_msg += f' Вариант {i+1}: {form_errors}\n'
|
||||
if option_formset.non_form_errors():
|
||||
error_msg += f' Общие ошибки: {option_formset.non_form_errors()}\n'
|
||||
|
||||
messages.error(self.request, error_msg)
|
||||
return self.render_to_response(self.get_context_data(form=form, option_formset=option_formset, attribute_formset=attribute_formset))
|
||||
|
||||
# Валидация что каждый вариант имеет выбранный комплект
|
||||
validation_errors = self._validate_variant_kits(option_formset)
|
||||
if validation_errors:
|
||||
for error in validation_errors:
|
||||
messages.error(self.request, error)
|
||||
return self.render_to_response(self.get_context_data(form=form, option_formset=option_formset, attribute_formset=attribute_formset))
|
||||
|
||||
if not attribute_formset.is_valid():
|
||||
@@ -511,6 +595,48 @@ class ConfigurableKitProductUpdateView(LoginRequiredMixin, UpdateView):
|
||||
|
||||
ConfigurableKitProductAttribute.objects.create(**create_kwargs)
|
||||
|
||||
def _validate_variant_kits(self, option_formset):
|
||||
"""
|
||||
Валидация что каждый вариант имеет выбранный комплект.
|
||||
Возвращает список ошибок (пустой список если нет ошибок).
|
||||
"""
|
||||
errors = []
|
||||
|
||||
for idx, option_form in enumerate(option_formset):
|
||||
# Пропускаем удаленные или пустые формы
|
||||
if not option_form.cleaned_data or self._should_delete_form(option_form, option_formset):
|
||||
continue
|
||||
|
||||
# Получаем kit_id из POST данных (он там должен быть установлен JavaScript'ом)
|
||||
kit_id = self.request.POST.get(f'options-{idx}-kit', '').strip()
|
||||
|
||||
if not kit_id:
|
||||
# Пытаемся получить из cleaned_data
|
||||
kit_id = option_form.cleaned_data.get('kit')
|
||||
|
||||
if not kit_id:
|
||||
# Если у варианта есть выбранные атрибуты, но нет комплекта - это ошибка
|
||||
has_attributes = any(
|
||||
option_form.cleaned_data.get(k)
|
||||
for k in option_form.cleaned_data.keys()
|
||||
if k.startswith('attribute_')
|
||||
)
|
||||
|
||||
if has_attributes:
|
||||
# Собираем названия выбранных атрибутов для сообщения об ошибке
|
||||
selected_attrs = [
|
||||
str(option_form.cleaned_data.get(k))
|
||||
for k in option_form.cleaned_data.keys()
|
||||
if k.startswith('attribute_') and option_form.cleaned_data.get(k)
|
||||
]
|
||||
errors.append(
|
||||
f'Вариант {idx + 1} ({", ".join(selected_attrs)}): '
|
||||
f'не выбран комплект. Пожалуйста, выберите значения атрибутов которые '
|
||||
f'привязаны к одному комплекту.'
|
||||
)
|
||||
|
||||
return errors
|
||||
|
||||
@staticmethod
|
||||
def _should_delete_form(form, formset):
|
||||
"""Проверить должна ли форма быть удалена"""
|
||||
|
||||
Reference in New Issue
Block a user