from __future__ import annotations from fastapi import APIRouter, Form, Request from fastapi.responses import RedirectResponse from app.core.http_responses import not_found_response from app.core.db.common import CommonSessionLocal from app.core.security.csrf import get_or_create_csrf_token, validate_csrf from app.core.security.session_auth import get_current_user from app.core.templating import templates from app.modules.core.rbac.deps import get_user_permissions, get_user_roles from app.modules.core.rbac.permission_guard import require_permission from app.modules.marketplace.services import ( LEAD_PRIORITIES, LEAD_SOURCES, LEAD_STATUSES, SERVICE_CATEGORIES, assign_marketplace_lead, convert_lead_to_client, create_marketplace_lead, get_marketplace_lead, get_marketplace_overview_counts, is_marketplace_domain_request, list_marketplace_leads, list_public_marketplace_audit_firms, list_public_marketplace_consultants, list_reference_audit_firms, list_reference_branches, list_reference_partners, update_lead_status, ) router = APIRouter(prefix="/marketplace", tags=["marketplace-ui"]) public_router = APIRouter(tags=["marketplace-public-ui"]) def _csrf_rejected(request: Request): from app.core.http_responses import forbidden_response return forbidden_response(request, "CSRF validation failed") def _public_marketplace_ctx(request: Request, db, **ctx): base = { "request": request, "current_user": None, "current_user_roles": [], "current_user_permissions": [], "csrf_token": get_or_create_csrf_token(request), "service_categories": SERVICE_CATEGORIES, "lead_priorities": LEAD_PRIORITIES, "lead_sources": LEAD_SOURCES, "marketplace_counts": get_marketplace_overview_counts(db), "public_audit_firms": list_public_marketplace_audit_firms(db, limit=8), "public_consultants": list_public_marketplace_consultants(db, limit=8), } base.update(ctx) return base def _render_public_marketplace(request: Request, template: str, db, **ctx): return templates.TemplateResponse(template, _public_marketplace_ctx(request, db, **ctx)) @public_router.get("/") def marketplace_domain_home(request: Request): """Marketplace domain landing page. Only marketplace-mapped domains use this as the public home page. Normal localhost, tenant domains and consultant domains continue to the normal login flow. """ if not is_marketplace_domain_request(request): return RedirectResponse(url="/login", status_code=303) db = CommonSessionLocal() try: return _render_public_marketplace( request, "modules/marketplace/templates/marketplace/public_marketplace_home.html", db, title="FilingABC Marketplace", ) finally: db.close() @public_router.get("/audit-firms") def marketplace_public_audit_firms(request: Request): if not is_marketplace_domain_request(request): return RedirectResponse(url="/login", status_code=303) db = CommonSessionLocal() try: return _render_public_marketplace( request, "modules/marketplace/templates/marketplace/public_audit_firms.html", db, title="Audit Firms", public_audit_firms=list_public_marketplace_audit_firms(db, limit=50), ) finally: db.close() @public_router.get("/consultants") def marketplace_public_consultants(request: Request): if not is_marketplace_domain_request(request): return RedirectResponse(url="/login", status_code=303) db = CommonSessionLocal() try: return _render_public_marketplace( request, "modules/marketplace/templates/marketplace/public_consultants.html", db, title="Consultants", public_consultants=list_public_marketplace_consultants(db, limit=50), ) finally: db.close() @public_router.get("/request-service") def marketplace_public_request_service(request: Request): if not is_marketplace_domain_request(request): return RedirectResponse(url="/marketplace/public-lead", status_code=303) db = CommonSessionLocal() try: return _render_public_marketplace( request, "modules/marketplace/templates/marketplace/public_marketplace_lead_form.html", db, title="Request a Service", ) finally: db.close() @public_router.post("/request-service") def marketplace_public_request_service_submit(request: Request, csrf_token: str = Form(...), lead_name: str = Form(...), business_name: str = Form(""), email: str = Form(""), mobile: str = Form(""), city: str = Form(""), state: str = Form(""), service_category: str = Form(""), service_requested: str = Form(...), message: str = Form("")): if not is_marketplace_domain_request(request): return RedirectResponse(url="/marketplace/public-lead", status_code=303) db = CommonSessionLocal() try: try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = create_marketplace_lead( db, lead_name=lead_name, business_name=business_name, email=email, mobile=mobile, city=city, state=state, service_category=service_category, service_requested=service_requested, message=message, source="public_website", priority="NORMAL", ) return _render_public_marketplace( request, "modules/marketplace/templates/marketplace/public_marketplace_thank_you.html", db, title="Thank You", lead=lead, ) finally: db.close() def _redirect_denied(): from app.core.http_responses import ui_access_denied return ui_access_denied() def _has_perm(db, user, code: str) -> bool: try: require_permission(db, user, code) return True except Exception: return False def _require_user(request: Request, db, permission_code: str): user = get_current_user(request, db=db) if not user: return None, RedirectResponse(url="/login", status_code=303) try: require_permission(db, user, permission_code) except Exception: # Assigned lead users are allowed if they have assigned-lead permission. if permission_code == "marketplace_leads.view" and _has_perm(db, user, "marketplace_leads.view_assigned"): return user, None return user, _redirect_denied() return user, None def _base_ctx(request: Request, db, user=None, **ctx): base = { "request": request, "current_user": user, "current_user_roles": get_user_roles(db, user.id) if user else [], "current_user_permissions": get_user_permissions(db, user.id) if user else [], "csrf_token": get_or_create_csrf_token(request), "lead_statuses": LEAD_STATUSES, "lead_priorities": LEAD_PRIORITIES, "lead_sources": LEAD_SOURCES, "service_categories": SERVICE_CATEGORIES, } base.update(ctx) return base def _render(request: Request, template: str, db, user=None, **ctx): return templates.TemplateResponse(template, _base_ctx(request, db, user, **ctx)) @router.get("") def dashboard(request: Request): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.view") if response: return response roles = get_user_roles(db, user.id) leads = list_marketplace_leads(db, user=user, role_names=roles) return _render(request, "modules/marketplace/templates/marketplace/dashboard.html", db, user, title="Marketplace Leads", leads=leads[:8], total=len(leads), can_create=_has_perm(db, user, "marketplace_leads.create"), can_assign=_has_perm(db, user, "marketplace_leads.assign")) finally: db.close() @router.get("/leads") def leads_list(request: Request, q: str = "", status: str = ""): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.view") if response: return response roles = get_user_roles(db, user.id) return _render(request, "modules/marketplace/templates/marketplace/leads_list.html", db, user, title="Marketplace Leads", rows=list_marketplace_leads(db, q=q, status=status, user=user, role_names=roles), q=q, selected_status=status, can_create=_has_perm(db, user, "marketplace_leads.create"), can_assign=_has_perm(db, user, "marketplace_leads.assign")) finally: db.close() @router.get("/leads/new") def lead_new(request: Request): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.create") if response: return response return _render(request, "modules/marketplace/templates/marketplace/lead_form.html", db, user, title="New Marketplace Lead", public_mode=False) finally: db.close() @router.post("/leads/new") def lead_create(request: Request, csrf_token: str = Form(...), lead_name: str = Form(...), business_name: str = Form(""), email: str = Form(""), mobile: str = Form(""), city: str = Form(""), state: str = Form(""), service_category: str = Form(""), service_requested: str = Form(...), message: str = Form(""), source: str = Form("manual"), priority: str = Form("NORMAL"), estimated_value: str = Form("0")): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.create") if response: return response try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = create_marketplace_lead(db, lead_name=lead_name, business_name=business_name, email=email, mobile=mobile, city=city, state=state, service_category=service_category, service_requested=service_requested, message=message, source=source, priority=priority, estimated_value=estimated_value, created_by_user_id=user.id) return RedirectResponse(url=f"/marketplace/leads/{lead.id}", status_code=303) finally: db.close() @router.get("/public-lead") def public_lead_form(request: Request): db = CommonSessionLocal() try: return _render(request, "modules/marketplace/templates/marketplace/public_lead_form.html", db, None, title="Request a Service") finally: db.close() @router.post("/public-lead") def public_lead_submit(request: Request, csrf_token: str = Form(...), lead_name: str = Form(...), business_name: str = Form(""), email: str = Form(""), mobile: str = Form(""), city: str = Form(""), state: str = Form(""), service_category: str = Form(""), service_requested: str = Form(...), message: str = Form("")): db = CommonSessionLocal() try: try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = create_marketplace_lead(db, lead_name=lead_name, business_name=business_name, email=email, mobile=mobile, city=city, state=state, service_category=service_category, service_requested=service_requested, message=message, source="public_website", priority="NORMAL") return _render(request, "modules/marketplace/templates/marketplace/public_thank_you.html", db, None, title="Thank You", lead=lead) finally: db.close() @router.get("/leads/{lead_id}") def lead_detail(request: Request, lead_id: int): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.view") if response: return response lead = get_marketplace_lead(db, lead_id) if not lead: return not_found_response(request, "Marketplace lead not found") return _render(request, "modules/marketplace/templates/marketplace/lead_detail.html", db, user, title=f"Lead {lead.lead_no}", lead=lead, audit_firms=list_reference_audit_firms(db), branches=list_reference_branches(db, lead.assigned_tenant_id), partners=list_reference_partners(db, lead.assigned_tenant_id), can_assign=_has_perm(db, user, "marketplace_leads.assign"), can_update=_has_perm(db, user, "marketplace_leads.update"), can_convert=_has_perm(db, user, "marketplace_leads.convert")) finally: db.close() @router.post("/leads/{lead_id}/assign") def lead_assign(request: Request, lead_id: int, csrf_token: str = Form(...), tenant_id: int = Form(...), branch_id: int = Form(0), partner_user_id: int = Form(0), notes: str = Form("")): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.assign") if response: return response try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = get_marketplace_lead(db, lead_id) if lead: assign_marketplace_lead(db, lead=lead, tenant_id=tenant_id, branch_id=branch_id or None, partner_user_id=partner_user_id or None, notes=notes, assigned_by_user_id=user.id) return RedirectResponse(url=f"/marketplace/leads/{lead_id}", status_code=303) finally: db.close() @router.post("/leads/{lead_id}/status") def lead_status_update(request: Request, lead_id: int, csrf_token: str = Form(...), status: str = Form(...)): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.update") if response: return response try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = get_marketplace_lead(db, lead_id) if lead: update_lead_status(db, lead=lead, status=status, user_id=user.id) return RedirectResponse(url=f"/marketplace/leads/{lead_id}", status_code=303) finally: db.close() @router.post("/leads/{lead_id}/convert-client") def lead_convert_client(request: Request, lead_id: int, csrf_token: str = Form(...), tenant_id: int = Form(...), branch_id: int = Form(...), partner_user_id: int = Form(0), client_code: str = Form("")): db = CommonSessionLocal() try: user, response = _require_user(request, db, "marketplace_leads.convert") if response: return response try: validate_csrf(request, csrf_token) except PermissionError: return _csrf_rejected(request) lead = get_marketplace_lead(db, lead_id) if lead: client = convert_lead_to_client(db, lead=lead, tenant_id=tenant_id, branch_id=branch_id, partner_user_id=partner_user_id or None, client_code=client_code, user_id=user.id) return RedirectResponse(url=f"/clients/{client.id}", status_code=303) return RedirectResponse(url="/marketplace/leads", status_code=303) finally: db.close()