39448-vm/core/forms.py
2026-04-03 12:01:07 +00:00

127 lines
4.7 KiB
Python

from datetime import timedelta
from django import forms
from django.utils import timezone
from .models import ActivityItem, QuoteLine, SalesWorkspace
class StyledFormMixin:
def _apply_styles(self):
for name, field in self.fields.items():
widget = field.widget
css_class = "form-select" if isinstance(widget, forms.Select) else "form-control"
existing = widget.attrs.get("class", "")
widget.attrs["class"] = f"{existing} {css_class} workspace-input".strip()
class WorkspaceIntakeForm(StyledFormMixin, forms.ModelForm):
next_meeting_at = forms.DateTimeField(
required=False,
widget=forms.DateTimeInput(attrs={"type": "datetime-local"}),
)
class Meta:
model = SalesWorkspace
fields = [
"customer_name",
"project_name",
"project_number",
"zipcode",
"address",
"city",
"contact_name",
"contact_email",
"contact_phone",
"opportunity_title",
"estimated_value",
"layout_template",
"summary",
"next_step",
"next_meeting_at",
]
widgets = {
"summary": forms.Textarea(attrs={"rows": 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._apply_styles()
placeholders = {
"customer_name": "Van der Meer Construction",
"project_name": "Renovation of showroom",
"project_number": "PRJ-24018",
"zipcode": "3011 AA",
"address": "Binnenweg 18",
"city": "Rotterdam",
"contact_name": "Eva Jansen",
"contact_email": "eva@example.com",
"contact_phone": "+31 6 12345678",
"opportunity_title": "Lighting upgrade quotation",
"estimated_value": "18500",
"summary": "Existing customer requesting quick quote with site visit.",
"next_step": "Confirm site meeting and draft first quotation.",
}
for name, placeholder in placeholders.items():
self.fields[name].widget.attrs.setdefault("placeholder", placeholder)
self.fields["layout_template"].widget.attrs.setdefault("aria-label", "Workspace template")
def clean_next_meeting_at(self):
meeting = self.cleaned_data.get("next_meeting_at")
if meeting and meeting < timezone.now():
raise forms.ValidationError("Choose a future time for the next meeting.")
return meeting
class StageUpdateForm(StyledFormMixin, forms.Form):
stage = forms.ChoiceField(choices=SalesWorkspace.Stage.choices)
def __init__(self, *args, **kwargs):
current_stage = kwargs.pop("current_stage", None)
super().__init__(*args, **kwargs)
self._apply_styles()
if current_stage:
self.fields["stage"].initial = current_stage
class QuoteLineForm(StyledFormMixin, forms.ModelForm):
class Meta:
model = QuoteLine
fields = ["product_name", "description", "quantity", "unit_price"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._apply_styles()
self.fields["product_name"].widget.attrs.setdefault("placeholder", "Product or service")
self.fields["description"].widget.attrs.setdefault("placeholder", "Optional scope note")
self.fields["quantity"].widget.attrs.setdefault("min", 1)
self.fields["unit_price"].widget.attrs.setdefault("min", 0)
self.fields["unit_price"].widget.attrs.setdefault("step", "0.01")
class ActivityForm(StyledFormMixin, forms.ModelForm):
due_at = forms.DateTimeField(
widget=forms.DateTimeInput(attrs={"type": "datetime-local"})
)
class Meta:
model = ActivityItem
fields = ["title", "activity_type", "due_at", "owner", "notes"]
widgets = {
"notes": forms.Textarea(attrs={"rows": 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._apply_styles()
self.fields["title"].widget.attrs.setdefault("placeholder", "Schedule follow-up or task")
self.fields["owner"].widget.attrs.setdefault("placeholder", "Rep or team owner")
self.fields["notes"].widget.attrs.setdefault("placeholder", "Talking points, reminders, dependencies")
self.fields["due_at"].initial = (timezone.now() + timedelta(days=1)).strftime("%Y-%m-%dT%H:%M")
def clean_due_at(self):
due_at = self.cleaned_data["due_at"]
if due_at < timezone.now() - timedelta(minutes=5):
raise forms.ValidationError("Activity time should be now or in the future.")
return due_at