diff --git a/app/core/config.py b/app/core/config.py index f175afa..b48b964 100644 --- a/app/core/config.py +++ b/app/core/config.py @@ -253,6 +253,11 @@ class Settings(BaseSettings): ANYDESK_DRY_RUN: bool = True # SAFETY: Log without executing API calls ANYDESK_TIMEOUT_SECONDS: int = 30 ANYDESK_AUTO_START_SESSION: bool = True # Auto-start session when requested + ANYDESK_LOCAL_SESSIONS_URL: str = "http://localhost:8001/anydesk/sessions" + ANYDESK_LOCAL_SYNC_ENABLED: bool = True + ANYDESK_LOCAL_SYNC_INTERVAL_MINUTES: int = 15 + ANYDESK_LOCAL_SYNC_TIMEOUT_SECONDS: int = 20 + ANYDESK_LOCAL_SYNC_DRY_RUN: bool = False # Telefoni (Yealink) Integration TELEFONI_SHARED_SECRET: str = "" # If set, required as ?token=... diff --git a/app/jobs/anydesk_local_sync.py b/app/jobs/anydesk_local_sync.py new file mode 100644 index 0000000..85dd284 --- /dev/null +++ b/app/jobs/anydesk_local_sync.py @@ -0,0 +1,41 @@ +""" +AnyDesk local sessions sync job. +Polls local AnyDesk bridge endpoint and enriches local session rows. +""" + +import logging + +from app.core.config import settings +from app.services.anydesk import AnyDeskService + +logger = logging.getLogger(__name__) +anydesk_service = AnyDeskService() + + +async def sync_anydesk_local_sessions(): + """Sync AnyDesk sessions from local endpoint every N minutes.""" + if not settings.ANYDESK_LOCAL_SYNC_ENABLED: + return + + try: + logger.info("🔄 AnyDesk local sync started") + result = await anydesk_service.fetch_sessions_from_local_endpoint( + endpoint_url=settings.ANYDESK_LOCAL_SESSIONS_URL, + timeout_seconds=settings.ANYDESK_LOCAL_SYNC_TIMEOUT_SECONDS, + dry_run=settings.ANYDESK_LOCAL_SYNC_DRY_RUN, + ) + + if result.get("error"): + logger.error("❌ AnyDesk local sync failed: %s", result["error"]) + return + + logger.info( + "✅ AnyDesk local sync completed: total=%s imported=%s updated=%s matched=%s errors=%s", + result.get("total", 0), + result.get("imported", 0), + result.get("updated", 0), + result.get("matched", 0), + len(result.get("errors") or []), + ) + except Exception as exc: + logger.error("❌ Unexpected AnyDesk local sync error: %s", exc) diff --git a/app/modules/sag/templates/detail.html b/app/modules/sag/templates/detail.html index 82af0d6..be02276 100644 --- a/app/modules/sag/templates/detail.html +++ b/app/modules/sag/templates/detail.html @@ -1842,7 +1842,16 @@ } .case-tabs-topbar.topbar-secondary { - grid-template-columns: repeat(8, minmax(150px, 1fr)); + /* Vægt kolonner så dato-felter (som har indlejrede ikoner) får mere plads */ + grid-template-columns: + minmax(110px, 0.75fr) /* Type */ + minmax(110px, 0.8fr) /* Prioritet */ + minmax(105px, 0.75fr) /* Oprettet */ + minmax(195px, 1.3fr) /* Arbejdsstart (2 knapper) */ + minmax(195px, 1.3fr) /* Start senest (2 knapper) */ + minmax(150px, 1.1fr) /* Deadline (1 knap) */ + minmax(120px, 0.85fr) /* AnyDesk */ + minmax(140px, 1fr) /* Dokumenter */; } .case-tabs-topbar-item { @@ -1966,12 +1975,6 @@ text-align: left; } - .topbar-deferred-shortcuts { - display: flex; - gap: 0.35rem; - margin-top: 0.35rem; - flex-wrap: wrap; - } .topbar-deferred-current { margin-top: 0.4rem; @@ -1994,24 +1997,7 @@ background: rgba(15, 76, 117, 0.09); } - .topbar-mini-trigger { - border: 1px solid rgba(0,0,0,0.14); - background: rgba(255,255,255,0.75); - color: var(--text-primary); - border-radius: 999px; - font-size: 0.7rem; - font-weight: 700; - line-height: 1; - padding: 0.34rem 0.58rem; - min-height: 30px; - letter-spacing: 0.01em; - } - .topbar-mini-trigger:hover { - border-color: var(--accent); - color: var(--accent); - background: rgba(255,255,255,0.95); - } .topbar-secondary-action:hover { border-color: var(--accent); @@ -2019,31 +2005,6 @@ background: rgba(255,255,255,0.95); } - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-add { - background: rgba(32, 120, 72, 0.24); - border-color: rgba(92, 194, 132, 0.35); - } - - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-type { - background: rgba(80, 120, 200, 0.18); - border-color: rgba(140, 180, 255, 0.35); - } - - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-created { - background: rgba(40, 120, 80, 0.22); - border-color: rgba(90, 200, 140, 0.35); - } - - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-priority { - background: rgba(110, 80, 170, 0.24); - border-color: rgba(180, 145, 255, 0.35); - } - - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-start { - background: rgba(150, 120, 40, 0.24); - border-color: rgba(230, 190, 90, 0.35); - } - [data-bs-theme="dark"] .topbar-deferred-current { color: rgba(236, 242, 255, 0.82); background: rgba(255, 255, 255, 0.06); @@ -2056,16 +2017,6 @@ background: rgba(19, 100, 154, 0.24); } - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-start-before { - background: rgba(150, 90, 30, 0.24); - border-color: rgba(230, 160, 90, 0.35); - } - - [data-bs-theme="dark"] .topbar-secondary .case-tabs-topbar-item.field-deadline { - background: rgba(150, 40, 40, 0.24); - border-color: rgba(240, 120, 120, 0.35); - } - [data-bs-theme="dark"] .topbar-secondary-action { background: rgba(20, 27, 38, 0.72); border-color: rgba(170, 190, 216, 0.35); @@ -2081,16 +2032,7 @@ background: rgba(20, 27, 38, 0.78); } - [data-bs-theme="dark"] .topbar-mini-trigger { - background: rgba(20, 27, 38, 0.78); - border-color: rgba(170, 190, 216, 0.4); - color: #dce8f4; - } - [data-bs-theme="dark"] .topbar-mini-trigger:hover { - border-color: #9fc4e8; - color: #9fc4e8; - } .case-add-side-backdrop { position: fixed; @@ -2399,43 +2341,56 @@
Arbejdsstart
-
+
- -
-
- - - - -
-
Start senest
-
+
- -
-
- - +
{% set deferred_case_ns = namespace(title='') %} {% if case.deferred_until_case_id %} @@ -2459,21 +2414,22 @@
Deadline dato
-
+
- +
AnyDesk
-
@@ -3487,36 +3443,41 @@
- +