feat: add appointments, campaigns, admin, storage, tests and various updates

- Add new routers: admin, appointments, campaigns
- Add storage service and logging config
- Add migrations directory and test suite with pytest config
- Add supabase_migration_features.sql
- Update models, dependencies, config, and existing routers
- Remove whatsapp_service (deleted)
- Update pyproject.toml and uv.lock dependencies

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
belviskhoremk
2026-04-03 09:11:58 +00:00
parent 9dccc83293
commit 92d4c2fc5e
51 changed files with 7076 additions and 515 deletions

View File

@@ -2,7 +2,7 @@ from fastapi import APIRouter, HTTPException, Depends, Query
from fastapi.responses import StreamingResponse
from app.database import get_supabase
from app.dependencies import get_current_user
from app.models import LeadCreate, LeadResponse
from app.models import LeadCreate, LeadResponse, LeadUpdate
from typing import List, Optional
import uuid
import csv
@@ -60,6 +60,40 @@ async def list_leads(
return [LeadResponse(**lead) for lead in (result.data or [])]
@router.patch("/{lead_id}", response_model=LeadResponse)
async def update_lead(
lead_id: str,
data: LeadUpdate,
user=Depends(get_current_user),
):
"""Update lead status or notes."""
supabase = get_supabase()
_check_leads_access(user.id, supabase)
company_id = _get_user_company_id(user.id, supabase)
lead = supabase.table("leads").select("*, chatbots(company_id)") \
.eq("id", lead_id).execute()
if not lead.data:
raise HTTPException(status_code=404, detail="Lead not found")
if lead.data[0].get("chatbots", {}).get("company_id") != company_id:
raise HTTPException(status_code=403, detail="Not authorized")
update_fields: dict = {}
if data.status is not None:
valid_statuses = ("new", "contacted", "qualified", "closed", "lost")
if data.status not in valid_statuses:
raise HTTPException(status_code=400, detail=f"Status must be one of {valid_statuses}")
update_fields["status"] = data.status
if data.notes is not None:
update_fields["notes"] = data.notes
if not update_fields:
return LeadResponse(**lead.data[0])
result = supabase.table("leads").update(update_fields).eq("id", lead_id).execute()
return LeadResponse(**result.data[0])
@router.get("/export")
async def export_leads_csv(
chatbot_id: Optional[str] = Query(None),