Implement card-based interface for ConfigurableKitProduct attributes
This commit introduces a new user-friendly interface for managing product attributes:
1. **Form Changes** (products/forms.py):
- Removed 'option' field from ConfigurableKitOptionForm (values now inline)
- Updated ConfigurableKitProductAttributeFormSetCreate to only include name, position, visible
- Updated BaseConfigurableKitProductAttributeFormSet validation for new structure
2. **Template Updates** (products/templates/products/configurablekit_form.html):
- Replaced row-based attribute interface with card-based design
- Each card contains:
- Parameter name field
- Position field
- Visibility toggle
- Inline value inputs with add/remove buttons
- "Add parameter" button creates new cards
- "Add value" button adds inline value inputs
3. **JavaScript Enhancements**:
- addValueField(): Creates new value input with delete button
- initAddValueBtn(): Initializes add value button for each card
- addParameterBtn: Dynamically generates new parameter cards
- serializeAttributeValues(): Converts inline values to JSON for POST submission
- Form submission intercept to serialize data before sending
4. **View Updates** (products/views/configurablekit_views.py):
- Both Create and Update views now have _save_attributes_from_cards() method
- Reads attributes-X-values JSON from POST data
- Creates ConfigurableKitProductAttribute for each parameter+value combination
- Handles parameter deletion and visibility toggling
**Key Features**:
✓ One-time parameter name entry with multiple inline values
✓ Add/remove values without reloading page
✓ Add/remove entire parameters with one click
✓ No database changes required
✓ Better UX: card layout more intuitive than rows
✓ Proper JSON serialization for value transmission
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -783,71 +783,76 @@ ConfigurableKitOptionFormSetUpdate = inlineformset_factory(
|
||||
|
||||
class ConfigurableKitProductAttributeForm(forms.ModelForm):
|
||||
"""
|
||||
Форма для добавления атрибута родительского товара.
|
||||
Пример: name="Цвет", option="Красный"
|
||||
Форма для добавления атрибута родительского товара в карточном интерфейсе.
|
||||
На фронтенде: одна карточка параметра (имя + позиция + видимость)
|
||||
+ множество инлайн значений через JavaScript
|
||||
|
||||
Пример структуры:
|
||||
- name: "Длина"
|
||||
- position: 0
|
||||
- visible: True
|
||||
- values: [50, 60, 70] (будут созданы как отдельные ConfigurableKitProductAttribute)
|
||||
"""
|
||||
class Meta:
|
||||
model = ConfigurableKitProductAttribute
|
||||
fields = ['name', 'option', 'position', 'visible']
|
||||
fields = ['name', 'position', 'visible']
|
||||
labels = {
|
||||
'name': 'Название атрибута',
|
||||
'option': 'Значение опции',
|
||||
'name': 'Название параметра',
|
||||
'position': 'Порядок',
|
||||
'visible': 'Видимый'
|
||||
'visible': 'Видимый на витрине'
|
||||
}
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Например: Цвет, Размер, Длина'
|
||||
}),
|
||||
'option': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Например: Красный, M, 60см'
|
||||
'class': 'form-control param-name-input',
|
||||
'placeholder': 'Например: Длина, Цвет, Размер',
|
||||
'readonly': 'readonly' # Должен быть заполнен через JavaScript
|
||||
}),
|
||||
'position': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
'class': 'form-control param-position-input',
|
||||
'min': '0',
|
||||
'value': '0'
|
||||
}),
|
||||
'visible': forms.CheckboxInput(attrs={
|
||||
'class': 'form-check-input'
|
||||
'class': 'form-check-input param-visible-input'
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class BaseConfigurableKitProductAttributeFormSet(forms.BaseInlineFormSet):
|
||||
def clean(self):
|
||||
"""Проверка на дубликаты атрибутов"""
|
||||
"""Проверка на дубликаты параметров и что у каждого параметра есть значения"""
|
||||
if any(self.errors):
|
||||
return
|
||||
|
||||
attributes = []
|
||||
parameter_names = []
|
||||
|
||||
for form in self.forms:
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
continue
|
||||
|
||||
name = form.cleaned_data.get('name')
|
||||
option = form.cleaned_data.get('option')
|
||||
# Пропускаем пустые формы
|
||||
if not form.cleaned_data.get('name'):
|
||||
continue
|
||||
|
||||
# Проверка дубликатов
|
||||
if name and option:
|
||||
attr_tuple = (name.strip(), option.strip())
|
||||
if attr_tuple in attributes:
|
||||
raise forms.ValidationError(
|
||||
f'Атрибут "{name}: {option}" добавлен более одного раза. '
|
||||
f'Каждая комбинация атрибут-значение должна быть уникальной.'
|
||||
)
|
||||
attributes.append(attr_tuple)
|
||||
name = form.cleaned_data.get('name').strip()
|
||||
|
||||
# Проверка дубликатов параметров (в карточном интерфейсе каждый параметр должен быть один раз)
|
||||
if name in parameter_names:
|
||||
raise forms.ValidationError(
|
||||
f'Параметр "{name}" добавлен более одного раза. '
|
||||
f'Каждый параметр должен быть добавлен только один раз.'
|
||||
)
|
||||
parameter_names.append(name)
|
||||
|
||||
|
||||
# Формсет для создания атрибутов родительского товара
|
||||
# Формсет для создания атрибутов родительского товара (карточный интерфейс)
|
||||
ConfigurableKitProductAttributeFormSetCreate = inlineformset_factory(
|
||||
ConfigurableKitProduct,
|
||||
ConfigurableKitProductAttribute,
|
||||
form=ConfigurableKitProductAttributeForm,
|
||||
formset=BaseConfigurableKitProductAttributeFormSet,
|
||||
fields=['name', 'option', 'position', 'visible'],
|
||||
# Убрали 'option' - значения будут добавляться через JavaScript в карточку
|
||||
fields=['name', 'position', 'visible'],
|
||||
extra=1,
|
||||
can_delete=True,
|
||||
min_num=0,
|
||||
@@ -861,7 +866,8 @@ ConfigurableKitProductAttributeFormSetUpdate = inlineformset_factory(
|
||||
ConfigurableKitProductAttribute,
|
||||
form=ConfigurableKitProductAttributeForm,
|
||||
formset=BaseConfigurableKitProductAttributeFormSet,
|
||||
fields=['name', 'option', 'position', 'visible'],
|
||||
# Убрали 'option' - значения будут добавляться через JavaScript в карточку
|
||||
fields=['name', 'position', 'visible'],
|
||||
extra=0,
|
||||
can_delete=True,
|
||||
min_num=0,
|
||||
|
||||
Reference in New Issue
Block a user