diff --git a/app/routers/tickets.py b/app/routers/tickets.py index c6f21e1..d681940 100644 --- a/app/routers/tickets.py +++ b/app/routers/tickets.py @@ -16,6 +16,7 @@ from app.core.database import get_db from app.core.security import get_current_user from app.models.category import Category from app.models.ticket import Ticket, TicketPhoto +from app.models.unit import Unit from app.models.user import User from app.schemas.ticket import ( CategoryOut, @@ -27,6 +28,7 @@ from app.schemas.ticket import ( TicketOut, TicketPhotoOut, TicketUpdate, + UnitOut, ) from app.services import ticket as ticket_service from app.services.sla import get_sla_status @@ -38,6 +40,20 @@ UPLOADS_DIR = settings.BASE_DIR / "uploads" UPLOADS_DIR.mkdir(parents=True, exist_ok=True) +# ── Units ────────────────────────────────────────────────────────── +@router.get("/units", response_model=list[UnitOut]) +async def list_units( + db: Annotated[AsyncSession, Depends(get_db)], + property_filter: str | None = Query(None, alias="property"), +) -> list[Unit]: + """List all units, optionally filtered by property (East/West).""" + query = select(Unit).order_by(Unit.apartment_code) + if property_filter: + query = query.where(Unit.property == property_filter) + result = await db.execute(query) + return list(result.scalars().all()) + + # ── Categories ────────────────────────────────────────────────────── async def _build_category_tree(db: AsyncSession, parent_id: int | None = None) -> list[CategoryTreeOut]: """Build a nested category tree.""" diff --git a/app/schemas/ticket.py b/app/schemas/ticket.py index a3ff4d7..e958899 100644 --- a/app/schemas/ticket.py +++ b/app/schemas/ticket.py @@ -8,6 +8,17 @@ from decimal import Decimal from pydantic import BaseModel, Field +# ── Unit ───────────────────────────────────────────────────────────── +class UnitOut(BaseModel): + id: int + property: str + apartment_code: str + building: str | None = None + floor: int | None = None + + model_config = {"from_attributes": True} + + # ── Category ───────────────────────────────────────────────────────── class CategoryOut(BaseModel): id: int @@ -32,6 +43,8 @@ class TicketCreate(BaseModel): reported_via: str | None = None # whatsapp, phone, walk-in, qr, agent description: str | None = None assigned_to: int | None = None + customer_name: str | None = None + phone: str | None = None class TicketUpdate(BaseModel): diff --git a/app/templates/tickets/detail.html b/app/templates/tickets/detail.html index 17ceb36..c933a3a 100644 --- a/app/templates/tickets/detail.html +++ b/app/templates/tickets/detail.html @@ -334,12 +334,13 @@ // Users list not exposed via API directly, so use a known list // In a real system we'd have GET /api/users this.technicians = [ - { id: 8, full_name: 'Prosper' }, - { id: 9, full_name: 'Sam' }, - { id: 10, full_name: 'Steven' }, - { id: 11, full_name: 'Junior (Samuel)' }, - { id: 12, full_name: 'Francis' }, - { id: 13, full_name: 'Desmond Afful' }, + { id: 9, full_name: 'Prosper' }, + { id: 10, full_name: 'Sam' }, + { id: 11, full_name: 'Steven' }, + { id: 12, full_name: 'Junior (Samuel)' }, + { id: 13, full_name: 'Francis' }, + { id: 14, full_name: 'Desmond Afful' }, + { id: 15, full_name: 'Afful' }, ]; } catch (e) { console.error('Tech load error', e); } }, diff --git a/app/templates/tickets/new.html b/app/templates/tickets/new.html index c7b117b..ad060ca 100644 --- a/app/templates/tickets/new.html +++ b/app/templates/tickets/new.html @@ -196,17 +196,12 @@ this.form.apartment_code = ''; this.units = []; if (!this.form.property) return; - // Units aren't directly exposed via API, so we'll construct from known patterns - const prefix = this.form.property === 'East' ? 'E' : 'W'; - const units = []; - const buildings = ['A', 'B', 'C', 'D', 'E', 'F']; - for (let floor = 1; floor <= 10; floor++) { - for (const bld of buildings) { - const code = `${floor}0${bld}${prefix}`; - units.push({ id: code, apartment_code: code }); - } + try { + const data = await app().apiGet(`/api/tickets/units?property=${encodeURIComponent(this.form.property)}`); + this.units = data || []; + } catch (e) { + console.error('Units load error', e); } - this.units = units; }, handlePhotos(e) { @@ -240,13 +235,19 @@ return; } + // Resolve unit_id from selected apartment_code + const selectedUnit = this.units.find(u => u.apartment_code === this.form.apartment_code); + // Create the ticket const payload = { description: this.form.description, priority: this.form.priority || null, - reporter: this.form.reporter || app().user.full_name, + reporter: this.form.reporter || this.form.customer_name || app().user.full_name, reported_via: this.form.reported_via || 'walk-in', category_id: this.form.category_id ? parseInt(this.form.category_id) : (this.form.category_main ? parseInt(this.form.category_main) : null), + unit_id: selectedUnit ? selectedUnit.id : null, + customer_name: this.form.customer_name || null, + phone: this.form.phone || null, }; const ticket = await app().apiPost('/api/tickets', payload);