Files
gogo-telefon/gogo/web.py
Senad Uka 646917c322 M3: chat widget, owner dashboard, super-admin panel
- Embeddable vanilla-JS chat widget (one script tag + tenant public key):
  WebSocket, resumable sessions (24h localStorage token), honeypot + per-IP
  and per-message rate limits, Bosnian UI (§7)
- Session auth (bcrypt + signed cookies), owner accounts + super-admins,
  admin impersonation ('otvori kao salon', §11)
- Owner dashboard (§10, Bosnian): requests with same actions as email,
  call/chat history with transcripts, usage bar; settings for profile,
  working hours (manual + LLM paste-to-parse), services CRUD + 'Zalijepi
  cjenovnik' LLM import with review step, scheduling (Google OAuth free/busy
  read-only + calendar mappings + buffers, partner status), telephony
  (forwarding MMI codes per operator, ring group, worker SIP creds + QR),
  agent voice/price-mode/notes + widget snippet, notifications/SMS templates
- Super-admin panel (§11): tenant CRUD incl. plan/minutes/paid-until,
  partner API config (encrypted secrets), number/SIM registry + assignment,
  connectivity test (live availability + dry-run push), versioned prompt
  template editor with publish/rollback, playground, usage overview, health
- Fixes: WAL + busy_timeout for sqlite tests, no awaits in WS disconnect
  path (deadlock), template publish autoflush bug
- 76 tests green (incl. widget WS e2e booking flow); dashboard, widget and
  admin verified live in a browser against Postgres

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 10:51:57 +02:00

81 lines
2.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Shared web helpers: Jinja templates, redirects, Bosnian labels."""
from __future__ import annotations
from pathlib import Path
from urllib.parse import quote
from fastapi.responses import RedirectResponse
from fastapi.templating import Jinja2Templates
templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates"))
STATUS_LABELS = {
"pending": "na čekanju",
"resolved_by_owner": "riješeno",
"confirmed": "potvrđeno",
"rejected": "odbijeno",
"expired": "isteklo",
}
OUTCOME_LABELS = {
"human_answered": "javilo se osoblje",
"request_created": "zahtjev kreiran",
"info_only": "informacija",
"message_taken": "poruka",
"abandoned": "prekinut",
}
DAY_LABELS = [
("mon", "Ponedjeljak"),
("tue", "Utorak"),
("wed", "Srijeda"),
("thu", "Četvrtak"),
("fri", "Petak"),
("sat", "Subota"),
("sun", "Nedjelja"),
]
# Curated TTS voices (§10.5) — final list confirmed by the Phase-0 PoC bake-off.
TTS_VOICES = [
{"id": "azure:sr-RS-SophieNeural", "label": "Sofija — ženski, topao (Azure)"},
{"id": "azure:sr-RS-NicholasNeural", "label": "Nikola — muški, smiren (Azure)"},
{"id": "azure:hr-HR-GabrijelaNeural", "label": "Gabrijela — ženski, vedar (Azure)"},
]
def redirect(url: str, msg: str = "", err: str = "") -> RedirectResponse:
if msg:
url += ("&" if "?" in url else "?") + "msg=" + quote(msg)
if err:
url += ("&" if "?" in url else "?") + "err=" + quote(err)
return RedirectResponse(url, status_code=303)
def hours_to_text(working_hours: dict) -> dict[str, str]:
"""{'mon': [['09:00','13:00'],['14:00','18:00']]} → {'mon': '09:00-13:00, 14:00-18:00'}"""
out = {}
for key, _label in DAY_LABELS:
intervals = (working_hours or {}).get(key, [])
out[key] = ", ".join(f"{a}-{b}" for a, b in intervals)
return out
def text_to_intervals(text: str) -> list[list[str]]:
"""'09:00-13:00, 14:00-18:00' → [['09:00','13:00'],['14:00','18:00']]; '' → []"""
import re
intervals = []
for part in text.split(","):
part = part.strip()
if not part:
continue
m = re.match(r"^(\d{1,2})(?::(\d{2}))?\s*[-]\s*(\d{1,2})(?::(\d{2}))?$", part)
if not m:
raise ValueError(f"Neispravan format: '{part}' (očekivano npr. 09:00-18:00)")
h1, m1, h2, m2 = m.groups()
a = f"{int(h1):02d}:{m1 or '00'}"
b = f"{int(h2):02d}:{m2 or '00'}"
intervals.append([a, b])
return intervals