loki-mode 9.53.0 → 9.54.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/completion-council.sh +28 -5
- package/autonomy/council-v2.sh +13 -7
- package/autonomy/lib/done-recognition.sh +24 -16
- package/autonomy/lib/proof-check.sh +11 -3
- package/autonomy/lib/proof-generator.py +36 -5
- package/autonomy/lib/voter-agents.sh +8 -3
- package/autonomy/lib/workspace_diff.py +106 -12
- package/autonomy/prd-checklist.sh +19 -11
- package/autonomy/run.sh +503 -52
- package/dashboard/__init__.py +1 -1
- package/dashboard/api_v2.py +4 -0
- package/dashboard/audit.py +9 -1
- package/dashboard/auth.py +16 -4
- package/dashboard/registry.py +34 -12
- package/dashboard/rigour_integration.py +25 -17
- package/dashboard/server.py +252 -104
- package/dashboard/static/cost.html +55 -26
- package/dashboard/static/index.html +475 -1544
- package/dashboard/static/proofs.html +1 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/air-gapped.md +7 -2
- package/docs/image-provenance.md +4 -2
- package/docs/v10/BACKLOG.md +82 -2
- package/docs/v10/BOARD.md +105 -0
- package/docs/v10/DECISIONS.md +71 -0
- package/docs/v10/METRICS.md +2 -1
- package/docs/v10/PROGRESS.md +60 -0
- package/docs/v10/SWARM-PROMPT.md +98 -0
- package/docs/v10/SWARM.md +237 -0
- package/loki-ts/dist/loki.js +323 -322
- package/mcp/__init__.py +1 -1
- package/package.json +1 -1
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/providers/aider.sh +8 -0
- package/providers/cline.sh +8 -0
- package/providers/codex.sh +9 -0
- package/providers/opencode.sh +9 -0
- package/web-app/dist/assets/AdminPage-CZrBkzRF.js +32 -0
- package/web-app/dist/assets/Avatar-gQIp6FBI.js +11 -0
- package/web-app/dist/assets/{Badge-BF3mhT57.js → Badge-D2_fBTFY.js} +1 -1
- package/web-app/dist/assets/{Button-BDp9aJ9q.js → Button-D5qDd2ef.js} +1 -1
- package/web-app/dist/assets/CockpitPage-DVMk2RUK.js +47 -0
- package/web-app/dist/assets/ComparePage-NMLyE1xL.js +1 -0
- package/web-app/dist/assets/{ErrorBoundary-ut3wrUmu.js → ErrorBoundary-BRABI-BT.js} +1 -1
- package/web-app/dist/assets/EvidenceReceiptPanel-D2LNKqUx.js +1 -0
- package/web-app/dist/assets/GitHubIssuesPanel-BC5GzKUN.js +7 -0
- package/web-app/dist/assets/{GitHubPRsPanel-DurZgSaj.js → GitHubPRsPanel-BSlmoVTS.js} +3 -3
- package/web-app/dist/assets/HomePage-D_CDMoen.js +12 -0
- package/web-app/dist/assets/{LoginPage-HeRGJeZy.js → LoginPage-BBNA_N9t.js} +1 -1
- package/web-app/dist/assets/{MagicPage-CUKrqsZs.js → MagicPage-CnFRZCMx.js} +1 -1
- package/web-app/dist/assets/MetricsPage-CPfLMRlh.js +6 -0
- package/web-app/dist/assets/{NotFoundPage-BZDZ-B77.js → NotFoundPage-lA0XSGTz.js} +1 -1
- package/web-app/dist/assets/ProjectPage-CNAPbr9z.js +256 -0
- package/web-app/dist/assets/ProjectsPage-CMtm8goN.js +6 -0
- package/web-app/dist/assets/SettingsPage-BhIjVMYE.js +10 -0
- package/web-app/dist/assets/{ShowcasePage-BGS2jTGH.js → ShowcasePage-BndtGeYo.js} +1 -1
- package/web-app/dist/assets/SystemSettingsPage-euXI-KXE.js +1 -0
- package/web-app/dist/assets/TeamsPage-ClUKecun.js +11 -0
- package/web-app/dist/assets/TemplatesPage-BWHWVk0L.js +29 -0
- package/web-app/dist/assets/{TerminalOutput-togzv9Zl.js → TerminalOutput-Cv3N3Szf.js} +1 -1
- package/web-app/dist/assets/{activity-Dreyb_Kr.js → activity-dEhz-bXR.js} +1 -1
- package/web-app/dist/assets/{bell-DWH0drES.js → bell-B3XUMq1t.js} +1 -1
- package/web-app/dist/assets/{bot-78MoCuDl.js → bot-X-ssNlwR.js} +1 -1
- package/web-app/dist/assets/{check-ikfylPst.js → check-C_9Nx7kb.js} +1 -1
- package/web-app/dist/assets/{chevron-left-DXUqkd8J.js → chevron-left-tvNswo3A.js} +1 -1
- package/web-app/dist/assets/{circle-alert-Da74VO8P.js → circle-alert-CSB88nEs.js} +1 -1
- package/web-app/dist/assets/{clock-mlGCFq8l.js → clock-C8p2e4NI.js} +1 -1
- package/web-app/dist/assets/{cloud-B_Y5ZrIO.js → cloud-CKp2ZvhG.js} +1 -1
- package/web-app/dist/assets/{code-xml-Bbom7YWV.js → code-xml-dY70LCHD.js} +1 -1
- package/web-app/dist/assets/{database-CLcofiVp.js → database-DObn5fmn.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-C5mkjXIM.js → dollar-sign-DSu6n3K3.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CVrkaOQP.js → file-code-corner-DpCnS73z.js} +1 -1
- package/web-app/dist/assets/{file-plus-BV9240gX.js → file-plus-D3IPKCf1.js} +1 -1
- package/web-app/dist/assets/{globe-aZ0Zso4I.js → globe-C9QXkgH_.js} +1 -1
- package/web-app/dist/assets/{hammer-EgOQNukG.js → hammer-D_FtWe1y.js} +1 -1
- package/web-app/dist/assets/index-CI54rara.css +1 -0
- package/web-app/dist/assets/{index-DvZ0Kk2x.js → index-Cz7q1bvC.js} +2 -2
- package/web-app/dist/assets/{layers-BOVKmepE.js → layers-DsW7jnJX.js} +1 -1
- package/web-app/dist/assets/{loader-circle-0K8yLgHl.js → loader-circle-B3HspjKR.js} +1 -1
- package/web-app/dist/assets/{lock-XzlDMy8M.js → lock-DSI29pct.js} +1 -1
- package/web-app/dist/assets/{package-BtXTKusI.js → package-CLNxtA2x.js} +1 -1
- package/web-app/dist/assets/{plus-PxeiD8ju.js → plus-DyI4WC4_.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CjqzYczt.js → refresh-cw-D2dv0xA0.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-CY0os4DH.js → rotate-ccw-CBvihA-a.js} +1 -1
- package/web-app/dist/assets/scroll-text-DvoOvAl_.js +6 -0
- package/web-app/dist/assets/{server-pIIs9vNp.js → server-DenWvSUt.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CsDqKXwA.js → shield-alert-40cX_FvG.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-CAh3HVzH.js → thumbs-up-BrjZ6QuV.js} +12 -7
- package/web-app/dist/assets/trash-2-C6evwkEH.js +16 -0
- package/web-app/dist/assets/{trending-down-DrtBd_C9.js → trending-down-CfbZis3O.js} +1 -1
- package/web-app/dist/assets/{trending-up-D-DbCHXm.js → trending-up-DWuEK3WC.js} +1 -1
- package/web-app/dist/assets/{upload-D4l6DCOt.js → upload-B9DFBcPG.js} +1 -1
- package/web-app/dist/assets/{usePolling-D0aoTX80.js → usePolling-BryVC-T1.js} +1 -1
- package/web-app/dist/assets/{user-BQf_XJO_.js → user-DMRXwZiw.js} +1 -1
- package/web-app/dist/index.html +2 -2
- package/web-app/server.py +122 -47
- package/web-app/dist/assets/AdminPage-y6_b2Xo4.js +0 -32
- package/web-app/dist/assets/Avatar-BFPkXhIJ.js +0 -6
- package/web-app/dist/assets/CockpitPage-5TKICCoA.js +0 -47
- package/web-app/dist/assets/ComparePage-BSuNsUHy.js +0 -1
- package/web-app/dist/assets/EvidenceReceiptPanel-CcaBwEI-.js +0 -1
- package/web-app/dist/assets/GitHubIssuesPanel-DJeYgzAa.js +0 -12
- package/web-app/dist/assets/HomePage-Bs451LAy.js +0 -28
- package/web-app/dist/assets/MetricsPage-DvY1csgz.js +0 -6
- package/web-app/dist/assets/ProjectPage-CqHkEZkl.js +0 -257
- package/web-app/dist/assets/ProjectsPage-CLvCK1Y8.js +0 -6
- package/web-app/dist/assets/SettingsPage-DP7OmdwP.js +0 -10
- package/web-app/dist/assets/SystemSettingsPage-P_rlLh0x.js +0 -1
- package/web-app/dist/assets/TeamsPage-DxtBDcBC.js +0 -11
- package/web-app/dist/assets/TemplatesPage-5e-faH3W.js +0 -29
- package/web-app/dist/assets/folder-open-D_6yV78G.js +0 -11
- package/web-app/dist/assets/index-BSgmPwC0.css +0 -1
- package/web-app/dist/assets/lightbulb-DdASgqOG.js +0 -6
- package/web-app/dist/assets/mail-COu670_W.js +0 -6
- package/web-app/dist/assets/minus-bQpUt9ff.js +0 -6
- package/web-app/dist/assets/save-v8qep5cY.js +0 -6
- package/web-app/dist/assets/trash-2-Dt6urTyY.js +0 -6
package/web-app/server.py
CHANGED
|
@@ -2898,6 +2898,33 @@ async def stop_session() -> JSONResponse:
|
|
|
2898
2898
|
return JSONResponse(content={"stopped": True, "message": "Session stopped"})
|
|
2899
2899
|
|
|
2900
2900
|
|
|
2901
|
+
def _measured_state_cost(tokens) -> Optional[float]:
|
|
2902
|
+
"""Cost from dashboard-state.json's tokens block, or None when unmeasured.
|
|
2903
|
+
|
|
2904
|
+
Shared by GET /api/session/status and the WebSocket status push. A zero
|
|
2905
|
+
cost with no token counted is an unmeasured record, not a free run.
|
|
2906
|
+
"""
|
|
2907
|
+
if not isinstance(tokens, dict):
|
|
2908
|
+
return None
|
|
2909
|
+
cost = tokens.get("cost_usd")
|
|
2910
|
+
if isinstance(cost, bool) or not isinstance(cost, (int, float)):
|
|
2911
|
+
return None
|
|
2912
|
+
counted = any(isinstance(tokens.get(k), (int, float)) and not isinstance(tokens.get(k), bool)
|
|
2913
|
+
and tokens.get(k) > 0 for k in ("input", "output", "input_tokens", "output_tokens"))
|
|
2914
|
+
return float(cost) if cost > 0 or counted else None
|
|
2915
|
+
|
|
2916
|
+
|
|
2917
|
+
def _max_iterations_or_none(configured: int) -> Optional[int]:
|
|
2918
|
+
"""The configured iteration cap, else LOKI_MAX_ITERATIONS, else None (never an invented 10)."""
|
|
2919
|
+
if configured > 0:
|
|
2920
|
+
return configured
|
|
2921
|
+
try:
|
|
2922
|
+
env = int(os.environ.get("LOKI_MAX_ITERATIONS", ""))
|
|
2923
|
+
except ValueError:
|
|
2924
|
+
return None
|
|
2925
|
+
return env if env > 0 else None
|
|
2926
|
+
|
|
2927
|
+
|
|
2901
2928
|
@app.get("/api/session/status")
|
|
2902
2929
|
async def get_status() -> JSONResponse:
|
|
2903
2930
|
"""Get current session status."""
|
|
@@ -2921,15 +2948,17 @@ async def get_status() -> JSONResponse:
|
|
|
2921
2948
|
else:
|
|
2922
2949
|
is_running = False
|
|
2923
2950
|
|
|
2924
|
-
# Try to read .loki state files for richer status
|
|
2951
|
+
# Try to read .loki state files for richer status. A field no state file
|
|
2952
|
+
# carries stays None ("--" in the UI): no invented iteration 0, "standard"
|
|
2953
|
+
# complexity, 10-iteration cap or $0.00 cost.
|
|
2925
2954
|
loki_dir = _loki_dir()
|
|
2926
2955
|
phase = "idle"
|
|
2927
|
-
iteration =
|
|
2928
|
-
complexity =
|
|
2956
|
+
iteration = None
|
|
2957
|
+
complexity = None
|
|
2929
2958
|
current_task = ""
|
|
2930
|
-
pending_tasks =
|
|
2959
|
+
pending_tasks = None
|
|
2931
2960
|
max_iterations = 0
|
|
2932
|
-
cost_usd =
|
|
2961
|
+
cost_usd = None
|
|
2933
2962
|
|
|
2934
2963
|
# BUG-INT-002 fix: CLI writes dashboard-state.json, not state/session.json.
|
|
2935
2964
|
# Read from dashboard-state.json (primary) with orchestrator.json fallback.
|
|
@@ -2950,10 +2979,7 @@ async def get_status() -> JSONResponse:
|
|
|
2950
2979
|
in_progress = len(_ip) if isinstance(_ip, list) else int(_ip or 0)
|
|
2951
2980
|
if in_progress > 0:
|
|
2952
2981
|
current_task = f"{in_progress} task(s) in progress"
|
|
2953
|
-
|
|
2954
|
-
tokens = state.get("tokens")
|
|
2955
|
-
if isinstance(tokens, dict):
|
|
2956
|
-
cost_usd = float(tokens.get("cost_usd", 0) or 0)
|
|
2982
|
+
cost_usd = _measured_state_cost(state.get("tokens"))
|
|
2957
2983
|
except (json.JSONDecodeError, OSError):
|
|
2958
2984
|
pass
|
|
2959
2985
|
else:
|
|
@@ -2991,8 +3017,7 @@ async def get_status() -> JSONResponse:
|
|
|
2991
3017
|
break
|
|
2992
3018
|
except (OSError, ValueError):
|
|
2993
3019
|
pass
|
|
2994
|
-
|
|
2995
|
-
max_iterations = int(os.environ.get("LOKI_MAX_ITERATIONS", "10"))
|
|
3020
|
+
max_iterations = _max_iterations_or_none(max_iterations)
|
|
2996
3021
|
|
|
2997
3022
|
uptime = time.time() - session.start_time if is_running else 0
|
|
2998
3023
|
|
|
@@ -3021,13 +3046,14 @@ async def get_status() -> JSONResponse:
|
|
|
3021
3046
|
"provider": session.provider,
|
|
3022
3047
|
"current_task": current_task,
|
|
3023
3048
|
"pending_tasks": pending_tasks,
|
|
3024
|
-
|
|
3049
|
+
# Not tracked by this server: unknown, never a measured 0.
|
|
3050
|
+
"running_agents": None,
|
|
3025
3051
|
"uptime": round(uptime),
|
|
3026
3052
|
"version": "",
|
|
3027
3053
|
"pid": str(session.process.pid) if session.process else "",
|
|
3028
3054
|
"projectDir": session.project_dir,
|
|
3029
3055
|
"max_iterations": max_iterations,
|
|
3030
|
-
"cost": round(cost_usd, 4),
|
|
3056
|
+
"cost": round(cost_usd, 4) if cost_usd is not None else None,
|
|
3031
3057
|
"start_time": session.start_time if session.start_time > 0 else 0,
|
|
3032
3058
|
"exit_code": exit_code,
|
|
3033
3059
|
"last_output": last_output,
|
|
@@ -3131,7 +3157,8 @@ async def get_memory() -> JSONResponse:
|
|
|
3131
3157
|
"episodic_count": 0,
|
|
3132
3158
|
"semantic_count": 0,
|
|
3133
3159
|
"skill_count": 0,
|
|
3134
|
-
|
|
3160
|
+
# Not tracked by this reader: unknown, not 0.
|
|
3161
|
+
"total_tokens": None,
|
|
3135
3162
|
"last_consolidation": None,
|
|
3136
3163
|
})
|
|
3137
3164
|
|
|
@@ -3143,7 +3170,7 @@ async def get_memory() -> JSONResponse:
|
|
|
3143
3170
|
"episodic_count": episodic,
|
|
3144
3171
|
"semantic_count": semantic,
|
|
3145
3172
|
"skill_count": skills,
|
|
3146
|
-
"total_tokens":
|
|
3173
|
+
"total_tokens": None,
|
|
3147
3174
|
"last_consolidation": None,
|
|
3148
3175
|
})
|
|
3149
3176
|
|
|
@@ -3314,8 +3341,10 @@ async def get_templates() -> JSONResponse:
|
|
|
3314
3341
|
"description": description,
|
|
3315
3342
|
"category": category,
|
|
3316
3343
|
"tech_stack": tech_stack[:6], # Limit to 6 techs
|
|
3317
|
-
|
|
3318
|
-
"
|
|
3344
|
+
# None for a template the hand-written maps do not cover: the UI
|
|
3345
|
+
# shows nothing rather than an invented "intermediate" / "5-10 min".
|
|
3346
|
+
"difficulty": _difficulty_map.get(f.stem),
|
|
3347
|
+
"build_time": _build_time_map.get(f.stem),
|
|
3319
3348
|
"gradient": _gradient_map.get(category, _gradient_map["Other"]),
|
|
3320
3349
|
})
|
|
3321
3350
|
return JSONResponse(content=templates)
|
|
@@ -3407,9 +3436,11 @@ async def plan_session(req: PlanRequest) -> JSONResponse:
|
|
|
3407
3436
|
# Try to parse structured JSON from output first (loki plan may emit JSON blocks)
|
|
3408
3437
|
_log = logging.getLogger("purple-lab.plan")
|
|
3409
3438
|
|
|
3410
|
-
|
|
3439
|
+
# Anything the plan output does not state stays None: the modal shows
|
|
3440
|
+
# "--", never a "standard" / 5-iteration estimate nobody produced.
|
|
3441
|
+
complexity = None
|
|
3411
3442
|
cost_estimate = "unknown"
|
|
3412
|
-
iterations =
|
|
3443
|
+
iterations = None
|
|
3413
3444
|
phases: list[str] = []
|
|
3414
3445
|
parsed = False
|
|
3415
3446
|
|
|
@@ -3421,7 +3452,7 @@ async def plan_session(req: PlanRequest) -> JSONResponse:
|
|
|
3421
3452
|
try:
|
|
3422
3453
|
data = json.loads(json_match.group(0))
|
|
3423
3454
|
if isinstance(data.get("complexity"), dict):
|
|
3424
|
-
complexity = data["complexity"].get("tier"
|
|
3455
|
+
complexity = data["complexity"].get("tier")
|
|
3425
3456
|
elif isinstance(data.get("complexity"), str):
|
|
3426
3457
|
complexity = data["complexity"]
|
|
3427
3458
|
if isinstance(data.get("cost"), dict):
|
|
@@ -3433,7 +3464,7 @@ async def plan_session(req: PlanRequest) -> JSONResponse:
|
|
|
3433
3464
|
elif isinstance(data.get("cost_estimate"), str):
|
|
3434
3465
|
cost_estimate = data["cost_estimate"]
|
|
3435
3466
|
if isinstance(data.get("iterations"), dict):
|
|
3436
|
-
iterations = data["iterations"].get("estimated"
|
|
3467
|
+
iterations = data["iterations"].get("estimated")
|
|
3437
3468
|
elif isinstance(data.get("iterations"), (int, float)):
|
|
3438
3469
|
iterations = int(data["iterations"])
|
|
3439
3470
|
if isinstance(data.get("execution_plan"), list):
|
|
@@ -3476,12 +3507,17 @@ async def plan_session(req: PlanRequest) -> JSONResponse:
|
|
|
3476
3507
|
|
|
3477
3508
|
if not parsed and not phases:
|
|
3478
3509
|
_log.info("Plan parse produced no phases from output (%d chars)", len(output))
|
|
3510
|
+
parsed = parsed or complexity is not None or iterations is not None \
|
|
3511
|
+
or bool(phases) or cost_estimate != "unknown"
|
|
3479
3512
|
|
|
3480
3513
|
return JSONResponse(content={
|
|
3481
3514
|
"complexity": complexity,
|
|
3482
3515
|
"cost_estimate": cost_estimate,
|
|
3483
3516
|
"iterations": iterations,
|
|
3484
|
-
"phases": phases
|
|
3517
|
+
"phases": phases,
|
|
3518
|
+
# False when nothing in the output was recognised; the modal then
|
|
3519
|
+
# shows the raw output instead of an estimate.
|
|
3520
|
+
"parsed": parsed,
|
|
3485
3521
|
"output_text": output,
|
|
3486
3522
|
"returncode": rc,
|
|
3487
3523
|
})
|
|
@@ -3576,12 +3612,13 @@ async def get_metrics() -> JSONResponse:
|
|
|
3576
3612
|
return JSONResponse(content=data)
|
|
3577
3613
|
except (json.JSONDecodeError, ValueError):
|
|
3578
3614
|
pass
|
|
3579
|
-
# Fallback: parse key metrics from text output
|
|
3615
|
+
# Fallback: parse key metrics from text output. A metric the text does not
|
|
3616
|
+
# carry stays null, so the panel says "N/A" instead of a measured 0.
|
|
3580
3617
|
metrics: dict = {
|
|
3581
|
-
"iterations":
|
|
3582
|
-
"quality_gate_pass_rate":
|
|
3618
|
+
"iterations": None,
|
|
3619
|
+
"quality_gate_pass_rate": None,
|
|
3583
3620
|
"time_elapsed": "",
|
|
3584
|
-
"tokens_used":
|
|
3621
|
+
"tokens_used": None,
|
|
3585
3622
|
"output_text": output,
|
|
3586
3623
|
}
|
|
3587
3624
|
for line in output.splitlines():
|
|
@@ -6283,13 +6320,14 @@ async def _push_state_to_client(ws: WebSocket) -> None:
|
|
|
6283
6320
|
# Use asyncio.to_thread to avoid blocking the event loop on file I/O
|
|
6284
6321
|
def _read_state_files():
|
|
6285
6322
|
loki_dir = _loki_dir()
|
|
6323
|
+
# Same unknown-not-zero defaults as GET /api/session/status.
|
|
6286
6324
|
_phase = "idle"
|
|
6287
|
-
_iteration =
|
|
6288
|
-
_complexity =
|
|
6325
|
+
_iteration = None
|
|
6326
|
+
_complexity = None
|
|
6289
6327
|
_current_task = ""
|
|
6290
|
-
_pending_tasks =
|
|
6328
|
+
_pending_tasks = None
|
|
6291
6329
|
_agents = []
|
|
6292
|
-
_cost_usd =
|
|
6330
|
+
_cost_usd = None
|
|
6293
6331
|
_max_iterations = 0
|
|
6294
6332
|
|
|
6295
6333
|
# BUG-INT-002 fix: CLI writes dashboard-state.json, not state/session.json
|
|
@@ -6309,10 +6347,7 @@ async def _push_state_to_client(ws: WebSocket) -> None:
|
|
|
6309
6347
|
_in_progress = len(_tip) if isinstance(_tip, list) else int(_tip or 0)
|
|
6310
6348
|
if _in_progress > 0:
|
|
6311
6349
|
_current_task = f"{_in_progress} task(s) in progress"
|
|
6312
|
-
|
|
6313
|
-
_tokens = state_data.get("tokens")
|
|
6314
|
-
if isinstance(_tokens, dict):
|
|
6315
|
-
_cost_usd = float(_tokens.get("cost_usd", 0) or 0)
|
|
6350
|
+
_cost_usd = _measured_state_cost(state_data.get("tokens"))
|
|
6316
6351
|
# Agents are included in dashboard-state.json
|
|
6317
6352
|
_dash_agents = state_data.get("agents")
|
|
6318
6353
|
if isinstance(_dash_agents, list):
|
|
@@ -6350,8 +6385,7 @@ async def _push_state_to_client(ws: WebSocket) -> None:
|
|
|
6350
6385
|
except (json.JSONDecodeError, OSError, ValueError):
|
|
6351
6386
|
pass
|
|
6352
6387
|
|
|
6353
|
-
|
|
6354
|
-
_max_iterations = int(os.environ.get("LOKI_MAX_ITERATIONS", "10"))
|
|
6388
|
+
_max_iterations = _max_iterations_or_none(_max_iterations)
|
|
6355
6389
|
|
|
6356
6390
|
return _phase, _iteration, _complexity, _current_task, _pending_tasks, _agents, _cost_usd, _max_iterations
|
|
6357
6391
|
|
|
@@ -6370,13 +6404,13 @@ async def _push_state_to_client(ws: WebSocket) -> None:
|
|
|
6370
6404
|
"provider": session.provider,
|
|
6371
6405
|
"current_task": current_task,
|
|
6372
6406
|
"pending_tasks": pending_tasks,
|
|
6373
|
-
"running_agents":
|
|
6407
|
+
"running_agents": None,
|
|
6374
6408
|
"uptime": round(uptime),
|
|
6375
6409
|
"version": "",
|
|
6376
6410
|
"pid": str(session.process.pid) if session.process else "",
|
|
6377
6411
|
"projectDir": session.project_dir,
|
|
6378
6412
|
"max_iterations": ws_max_iter,
|
|
6379
|
-
"cost": round(ws_cost, 4),
|
|
6413
|
+
"cost": round(ws_cost, 4) if ws_cost is not None else None,
|
|
6380
6414
|
"start_time": session.start_time if session.start_time > 0 else 0,
|
|
6381
6415
|
}
|
|
6382
6416
|
|
|
@@ -6745,8 +6779,9 @@ def _list_checkpoints(session_dir: Path) -> list[dict]:
|
|
|
6745
6779
|
"id": entry.name,
|
|
6746
6780
|
"timestamp": meta.get("timestamp", ""),
|
|
6747
6781
|
"description": meta.get("description", f"Checkpoint {entry.name}"),
|
|
6748
|
-
|
|
6749
|
-
"
|
|
6782
|
+
# Unrecorded counts are None ("--"), never 0.
|
|
6783
|
+
"iteration": meta.get("iteration"),
|
|
6784
|
+
"files_changed": meta.get("files_changed"),
|
|
6750
6785
|
"is_current": False,
|
|
6751
6786
|
})
|
|
6752
6787
|
except (json.JSONDecodeError, OSError):
|
|
@@ -6754,8 +6789,8 @@ def _list_checkpoints(session_dir: Path) -> list[dict]:
|
|
|
6754
6789
|
"id": entry.name,
|
|
6755
6790
|
"timestamp": "",
|
|
6756
6791
|
"description": f"Checkpoint {entry.name}",
|
|
6757
|
-
"iteration":
|
|
6758
|
-
"files_changed":
|
|
6792
|
+
"iteration": None,
|
|
6793
|
+
"files_changed": None,
|
|
6759
6794
|
"is_current": False,
|
|
6760
6795
|
})
|
|
6761
6796
|
else:
|
|
@@ -6766,8 +6801,8 @@ def _list_checkpoints(session_dir: Path) -> list[dict]:
|
|
|
6766
6801
|
"%Y-%m-%dT%H:%M:%SZ", time.localtime(entry.stat().st_mtime)
|
|
6767
6802
|
),
|
|
6768
6803
|
"description": f"Checkpoint {entry.name}",
|
|
6769
|
-
"iteration":
|
|
6770
|
-
"files_changed":
|
|
6804
|
+
"iteration": None,
|
|
6805
|
+
"files_changed": None,
|
|
6771
6806
|
"is_current": False,
|
|
6772
6807
|
})
|
|
6773
6808
|
|
|
@@ -7826,7 +7861,7 @@ def _audit_actor(request=None) -> tuple:
|
|
|
7826
7861
|
|
|
7827
7862
|
|
|
7828
7863
|
def _audit(action: str, user: str = "", target: str = "", details: str = "",
|
|
7829
|
-
request=None):
|
|
7864
|
+
request=None, team_id: str = ""):
|
|
7830
7865
|
"""Record an audit log entry.
|
|
7831
7866
|
|
|
7832
7867
|
NOTE ON WHAT THIS LOG IS: _append_audit_log rewrites a 500-entry JSON array
|
|
@@ -7848,6 +7883,10 @@ def _audit(action: str, user: str = "", target: str = "", details: str = "",
|
|
|
7848
7883
|
"timestamp": datetime.now().isoformat(),
|
|
7849
7884
|
"details": details,
|
|
7850
7885
|
}
|
|
7886
|
+
# The Teams page Activity tab lists only entries carrying its team's id;
|
|
7887
|
+
# a target (a team name or an email) is not a team identity.
|
|
7888
|
+
if team_id:
|
|
7889
|
+
entry["team_id"] = team_id
|
|
7851
7890
|
_append_audit_log(entry)
|
|
7852
7891
|
|
|
7853
7892
|
|
|
@@ -7871,7 +7910,7 @@ async def create_team(req: CreateTeamRequest, request: Request = None) -> JSONRe
|
|
|
7871
7910
|
store = _load_teams()
|
|
7872
7911
|
store[team_id] = team
|
|
7873
7912
|
_save_teams(store)
|
|
7874
|
-
_audit("team.created", target=req.name, request=request)
|
|
7913
|
+
_audit("team.created", target=req.name, request=request, team_id=team_id)
|
|
7875
7914
|
return JSONResponse(content={"id": team_id, "name": req.name, "created": True})
|
|
7876
7915
|
|
|
7877
7916
|
|
|
@@ -7902,7 +7941,8 @@ async def add_team_member(team_id: str, req: AddMemberRequest, request: Request
|
|
|
7902
7941
|
}
|
|
7903
7942
|
team.setdefault("members", []).append(member)
|
|
7904
7943
|
_save_teams(store)
|
|
7905
|
-
_audit("member.added", target=req.email, details=f"Role: {req.role}", request=request
|
|
7944
|
+
_audit("member.added", target=req.email, details=f"Role: {req.role}", request=request,
|
|
7945
|
+
team_id=team_id)
|
|
7906
7946
|
return JSONResponse(content={"added": True, "member_id": member_id})
|
|
7907
7947
|
|
|
7908
7948
|
|
|
@@ -7997,6 +8037,39 @@ async def magic_get_spec(name: str) -> JSONResponse:
|
|
|
7997
8037
|
return JSONResponse(content={"name": name, "markdown": spec_path.read_text()})
|
|
7998
8038
|
|
|
7999
8039
|
|
|
8040
|
+
@app.get("/api/magic/components/{name}/code")
|
|
8041
|
+
async def magic_get_code(name: str) -> JSONResponse:
|
|
8042
|
+
"""Return a component's generated source, read from the paths its registry entry records."""
|
|
8043
|
+
if not re.match(r"^[a-zA-Z][a-zA-Z0-9_-]*$", name):
|
|
8044
|
+
return JSONResponse(status_code=400, content={"error": "Invalid component name"})
|
|
8045
|
+
reg_path = _magic_registry_path()
|
|
8046
|
+
if not reg_path.exists():
|
|
8047
|
+
return JSONResponse(status_code=404, content={"error": "no registry"})
|
|
8048
|
+
try:
|
|
8049
|
+
data = json.loads(reg_path.read_text())
|
|
8050
|
+
except Exception as exc:
|
|
8051
|
+
return JSONResponse(status_code=500, content={"error": str(exc)})
|
|
8052
|
+
comps = data.get("components", []) if isinstance(data, dict) else []
|
|
8053
|
+
entry = next((c for c in comps if isinstance(c, dict) and c.get("name") == name), None)
|
|
8054
|
+
if entry is None:
|
|
8055
|
+
return JSONResponse(status_code=404, content={"error": "component not found"})
|
|
8056
|
+
files = []
|
|
8057
|
+
for target in ("react", "webcomponent"):
|
|
8058
|
+
recorded = entry.get(f"{target}_path") or ""
|
|
8059
|
+
if not isinstance(recorded, str) or not recorded:
|
|
8060
|
+
continue
|
|
8061
|
+
# registry.json is a project file an agent can write; a recorded path
|
|
8062
|
+
# (or a symlink) that leaves the project is refused, never read.
|
|
8063
|
+
path = _safe_resolve(Path.cwd(), recorded)
|
|
8064
|
+
if path is None:
|
|
8065
|
+
return JSONResponse(status_code=403, content={"error": f"{target} path is outside the project"})
|
|
8066
|
+
if path.is_file():
|
|
8067
|
+
files.append({"target": target, "path": recorded, "code": path.read_text(errors="replace")})
|
|
8068
|
+
if not files:
|
|
8069
|
+
return JSONResponse(status_code=404, content={"error": "no generated code on disk"})
|
|
8070
|
+
return JSONResponse(content={"name": name, "files": files})
|
|
8071
|
+
|
|
8072
|
+
|
|
8000
8073
|
@app.post("/api/magic/components/{name}/debate")
|
|
8001
8074
|
async def magic_run_debate(name: str, req: dict = Body(default={})) -> JSONResponse:
|
|
8002
8075
|
"""Trigger a multi-persona debate on an existing component."""
|
|
@@ -8949,6 +9022,8 @@ async def webapp_cost_timeline() -> Response:
|
|
|
8949
9022
|
"current_run": {"iterations": [], "total_usd": None,
|
|
8950
9023
|
"cost_recorded": False},
|
|
8951
9024
|
"runs": [],
|
|
9025
|
+
# Unreadable, not zero: the Runs-with-cost tile says "Not recorded".
|
|
9026
|
+
"runs_count": None,
|
|
8952
9027
|
})
|
|
8953
9028
|
try:
|
|
8954
9029
|
return JSONResponse(content=await dash.get_cost_timeline())
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import{c as L,j as e,r as m,w as _,Z as X,n as Q,i as ee,b as R,F as ge,U as z,X as te,L as se,x as je,M as fe,T as G,S as be,m as ae,l as ne,y as ve,E as Ee,a0 as Ne,a2 as re}from"./index-DvZ0Kk2x.js";import{B as w}from"./Button-BDp9aJ9q.js";import{T as ye}from"./trending-up-D-DbCHXm.js";import{C as I}from"./clock-mlGCFq8l.js";import{A as ke,U as ie}from"./Avatar-BFPkXhIJ.js";import{A as H}from"./activity-Dreyb_Kr.js";import{R as le}from"./refresh-cw-CjqzYczt.js";import{G as Ae}from"./globe-aZ0Zso4I.js";import{C as we}from"./chevron-left-DXUqkd8J.js";import{M as Ce}from"./mail-COu670_W.js";import{C as ce}from"./check-ikfylPst.js";import{D as W}from"./dollar-sign-C5mkjXIM.js";import{U as Se}from"./user-BQf_XJO_.js";import{D as De}from"./database-CLcofiVp.js";import{L as Fe}from"./lock-XzlDMy8M.js";import{H as Me}from"./hammer-EgOQNukG.js";import{S as Be}from"./shield-alert-CsDqKXwA.js";import{S as $e}from"./server-pIIs9vNp.js";/**
|
|
2
|
-
* @license lucide-react v0.577.0 - ISC
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the ISC license.
|
|
5
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const Ie=[["path",{d:"M16 20V4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v16",key:"jecpp"}],["rect",{width:"20",height:"14",x:"2",y:"6",rx:"2",key:"i6l2r4"}]],Le=L("briefcase",Ie);/**
|
|
7
|
-
* @license lucide-react v0.577.0 - ISC
|
|
8
|
-
*
|
|
9
|
-
* This source code is licensed under the ISC license.
|
|
10
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
11
|
-
*/const Te=[["path",{d:"M21 12c.552 0 1.005-.449.95-.998a10 10 0 0 0-8.953-8.951c-.55-.055-.998.398-.998.95v8a1 1 0 0 0 1 1z",key:"pzmjnu"}],["path",{d:"M21.21 15.89A10 10 0 1 1 8 2.83",key:"k2fpak"}]],Re=L("chart-pie",Te);/**
|
|
12
|
-
* @license lucide-react v0.577.0 - ISC
|
|
13
|
-
*
|
|
14
|
-
* This source code is licensed under the ISC license.
|
|
15
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
16
|
-
*/const ze=[["path",{d:"M12 15V3",key:"m9g1x1"}],["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["path",{d:"m7 10 5 5 5-5",key:"brsn70"}]],V=L("download",ze);/**
|
|
17
|
-
* @license lucide-react v0.577.0 - ISC
|
|
18
|
-
*
|
|
19
|
-
* This source code is licensed under the ISC license.
|
|
20
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
21
|
-
*/const Ue=[["path",{d:"M10 20a1 1 0 0 0 .553.895l2 1A1 1 0 0 0 14 21v-7a2 2 0 0 1 .517-1.341L21.74 4.67A1 1 0 0 0 21 3H3a1 1 0 0 0-.742 1.67l7.225 7.989A2 2 0 0 1 10 14z",key:"sc7q7i"}]],Pe=L("funnel",Ue);/**
|
|
22
|
-
* @license lucide-react v0.577.0 - ISC
|
|
23
|
-
*
|
|
24
|
-
* This source code is licensed under the ISC license.
|
|
25
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
26
|
-
*/const Oe=[["circle",{cx:"9",cy:"12",r:"3",key:"u3jwor"}],["rect",{width:"20",height:"14",x:"2",y:"5",rx:"7",key:"g7kal2"}]],_e=L("toggle-left",Oe);/**
|
|
27
|
-
* @license lucide-react v0.577.0 - ISC
|
|
28
|
-
*
|
|
29
|
-
* This source code is licensed under the ISC license.
|
|
30
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
31
|
-
*/const Ge=[["circle",{cx:"15",cy:"12",r:"3",key:"1afu0r"}],["rect",{width:"20",height:"14",x:"2",y:"5",rx:"7",key:"g7kal2"}]],He=L("toggle-right",Ge);function We({percentage:n,size:a=64,strokeWidth:t=4,color:l="#553DE9",trackColor:c="rgba(0,0,0,0.08)",className:s="",children:r}){const o=(a-t)/2,g=2*Math.PI*o,j=Math.max(0,Math.min(100,n)),u=g-j/100*g;return e.jsxs("div",{className:`relative inline-flex items-center justify-center ${s}`,style:{width:a,height:a},children:[e.jsxs("svg",{width:a,height:a,className:"transform -rotate-90",children:[e.jsx("circle",{cx:a/2,cy:a/2,r:o,fill:"none",stroke:c,strokeWidth:t}),e.jsx("circle",{cx:a/2,cy:a/2,r:o,fill:"none",stroke:l,strokeWidth:t,strokeLinecap:"round",strokeDasharray:g,strokeDashoffset:u,style:{transition:"stroke-dashoffset 0.6s ease-out"}})]}),r&&e.jsx("div",{className:"absolute inset-0 flex items-center justify-center",children:r})]})}function Ve(){const n=Date.now(),a=864e5,t=Array.from({length:30},(r,o)=>({date:new Date(n-(29-o)*a).toISOString().split("T")[0],tokens:Math.floor(Math.random()*5e5)+1e5})),l=[{provider:"Claude",cost:124.5,percentage:62,color:"#553DE9"},{provider:"Codex",cost:48.3,percentage:24,color:"#1FC5A8"},{provider:"Gemini",cost:28.2,percentage:14,color:"#F59E0B"}],c=[{name:"SaaS App",count:45},{name:"CLI Tool",count:32},{name:"Discord Bot",count:28},{name:"REST API",count:21},{name:"Chrome Extension",count:14}],s=Array.from({length:7},()=>Array.from({length:24},(r,o)=>o>=9&&o<=17?Math.floor(Math.random()*80)+20:o>=18&&o<=22?Math.floor(Math.random()*40)+10:Math.floor(Math.random()*15)));return{tokenUsage:t,costBreakdown:l,buildSuccessRate:87,totalBuilds:342,successfulBuilds:298,failedBuilds:44,templateUsage:c,peakHours:s}}function Je({data:n}){const[a,t]=m.useState(null);if(n.length===0)return null;const l=600,c=200,s={top:20,right:20,bottom:30,left:60},r=l-s.left-s.right,o=c-s.top-s.bottom,g=Math.max(...n.map(x=>x.tokens)),j=0,u=g-j||1,b=n.map((x,N)=>({x:s.left+N/(n.length-1)*r,y:s.top+o-(x.tokens-j)/u*o,...x})),h=b.map((x,N)=>`${N===0?"M":"L"} ${x.x} ${x.y}`).join(" "),y=`${h} L ${b[b.length-1].x} ${s.top+o} L ${b[0].x} ${s.top+o} Z`,v=x=>x>=1e6?`${(x/1e6).toFixed(1)}M`:x>=1e3?`${(x/1e3).toFixed(0)}K`:String(x),E=Array.from({length:5},(x,N)=>({value:j+u*N/4,y:s.top+o-N/4*o}));return e.jsxs("svg",{viewBox:`0 0 ${l} ${c}`,className:"w-full",preserveAspectRatio:"xMidYMid meet",children:[E.map((x,N)=>e.jsxs("g",{children:[e.jsx("line",{x1:s.left,y1:x.y,x2:l-s.right,y2:x.y,stroke:"currentColor",strokeOpacity:"0.08",strokeDasharray:"4 4"}),e.jsx("text",{x:s.left-8,y:x.y+4,textAnchor:"end",className:"text-[10px] fill-[#939084]",children:v(x.value)})]},N)),e.jsx("path",{d:y,fill:"url(#tokenGradient)",opacity:"0.15"}),e.jsx("path",{d:h,fill:"none",stroke:"#553DE9",strokeWidth:"2",strokeLinejoin:"round"}),e.jsx("defs",{children:e.jsxs("linearGradient",{id:"tokenGradient",x1:"0",y1:"0",x2:"0",y2:"1",children:[e.jsx("stop",{offset:"0%",stopColor:"#553DE9"}),e.jsx("stop",{offset:"100%",stopColor:"#553DE9",stopOpacity:"0"})]})}),b.map((x,N)=>e.jsxs("g",{children:[e.jsx("circle",{cx:x.x,cy:x.y,r:a===N?5:3,fill:a===N?"#553DE9":"white",stroke:"#553DE9",strokeWidth:"2",className:"cursor-pointer transition-all",onMouseEnter:()=>t(N),onMouseLeave:()=>t(null)}),a===N&&e.jsxs("g",{children:[e.jsx("rect",{x:x.x-50,y:x.y-36,width:"100",height:"24",rx:"4",fill:"#36342E"}),e.jsxs("text",{x:x.x,y:x.y-20,textAnchor:"middle",className:"text-[10px] fill-white font-mono",children:[v(x.tokens)," | ",x.date.slice(5)]})]})]},N)),[0,Math.floor(n.length/2),n.length-1].map(x=>e.jsx("text",{x:b[x].x,y:c-4,textAnchor:"middle",className:"text-[10px] fill-[#939084]",children:n[x].date.slice(5)},x))]})}function qe({data:n}){const[a,t]=m.useState(null),l=n.reduce((u,b)=>u+b.cost,0),c=160,s=c/2,r=60,o=40;let g=-90;const j=n.map((u,b)=>{const h=u.percentage/100*360,y=g,v=g+h;g=v;const E=y*Math.PI/180,x=v*Math.PI/180,N=h>180?1:0,f=a===b?r+4:r,D=[`M ${s+o*Math.cos(E)} ${s+o*Math.sin(E)}`,`L ${s+f*Math.cos(E)} ${s+f*Math.sin(E)}`,`A ${f} ${f} 0 ${N} 1 ${s+f*Math.cos(x)} ${s+f*Math.sin(x)}`,`L ${s+o*Math.cos(x)} ${s+o*Math.sin(x)}`,`A ${o} ${o} 0 ${N} 0 ${s+o*Math.cos(E)} ${s+o*Math.sin(E)}`,"Z"].join(" ");return{...u,path:D,index:b}});return e.jsxs("div",{className:"flex items-center gap-6",children:[e.jsxs("svg",{width:c,height:c,viewBox:`0 0 ${c} ${c}`,children:[j.map(u=>e.jsx("path",{d:u.path,fill:u.color,opacity:a!==null&&a!==u.index?.4:1,className:"cursor-pointer transition-opacity",onMouseEnter:()=>t(u.index),onMouseLeave:()=>t(null)},u.index)),e.jsx("text",{x:s,y:s-6,textAnchor:"middle",className:"text-xs fill-[#939084]",children:"Total"}),e.jsxs("text",{x:s,y:s+12,textAnchor:"middle",className:"text-sm fill-[#36342E] dark:fill-[#E8E6E3] font-semibold",children:["$",l.toFixed(0)]})]}),e.jsx("div",{className:"flex flex-col gap-2",children:n.map((u,b)=>e.jsxs("div",{className:"flex items-center gap-2 text-sm",onMouseEnter:()=>t(b),onMouseLeave:()=>t(null),children:[e.jsx("span",{className:"w-3 h-3 rounded-sm flex-shrink-0",style:{backgroundColor:u.color}}),e.jsx("span",{className:"text-[#36342E] dark:text-[#E8E6E3]",children:u.provider}),e.jsxs("span",{className:"text-[#939084] ml-auto font-mono",children:["$",u.cost.toFixed(2)]})]},b))})]})}function Ze({data:n}){const a=["Mon","Tue","Wed","Thu","Fri","Sat","Sun"],t=Math.max(...n.flat()),l=c=>{const s=t>0?c/t:0;return s===0?"bg-[#F8F4F0] dark:bg-[#1A1A1E]":s<.25?"bg-[#553DE9]/10":s<.5?"bg-[#553DE9]/25":s<.75?"bg-[#553DE9]/50":"bg-[#553DE9]/80"};return e.jsx("div",{className:"overflow-x-auto",children:e.jsxs("div",{className:"inline-block",children:[e.jsx("div",{className:"flex gap-0.5 mb-1 ml-10",children:Array.from({length:24},(c,s)=>e.jsx("div",{className:"w-4 text-center text-[9px] text-[#939084]",children:s%6===0?`${s}`:""},s))}),n.map((c,s)=>e.jsxs("div",{className:"flex items-center gap-0.5 mb-0.5",children:[e.jsx("span",{className:"w-8 text-right text-[10px] text-[#939084] mr-1",children:a[s]}),c.map((r,o)=>e.jsx("div",{className:`w-4 h-4 rounded-sm ${l(r)} transition-colors`,title:`${a[s]} ${o}:00 - ${r} builds`},o))]},s)),e.jsxs("div",{className:"flex items-center gap-1 mt-2 ml-10",children:[e.jsx("span",{className:"text-[10px] text-[#939084]",children:"Less"}),["bg-[#F8F4F0] dark:bg-[#1A1A1E]","bg-[#553DE9]/10","bg-[#553DE9]/25","bg-[#553DE9]/50","bg-[#553DE9]/80"].map((c,s)=>e.jsx("div",{className:`w-3 h-3 rounded-sm ${c}`},s)),e.jsx("span",{className:"text-[10px] text-[#939084]",children:"More"})]})]})})}function Ye({data:n,className:a=""}){const t=n||Ve(),l=m.useCallback(()=>{const c=new Blob([JSON.stringify(t,null,2)],{type:"application/json"}),s=URL.createObjectURL(c),r=document.createElement("a");r.href=s,r.download=`usage-analytics-${new Date().toISOString().split("T")[0]}.json`,r.click(),URL.revokeObjectURL(s)},[t]);return e.jsxs("div",{className:`space-y-6 ${a}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(_,{size:18,className:"text-[#553DE9]"}),e.jsx("h3",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3] uppercase tracking-wider",children:"Usage Analytics"})]}),e.jsx(w,{size:"sm",variant:"ghost",icon:V,onClick:l,children:"Export"})]}),e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx(ye,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Token Usage (30 Days)"})]}),e.jsx(Je,{data:t.tokenUsage})]}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-4",children:[e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx(Re,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Cost by Provider"})]}),e.jsx(qe,{data:t.costBreakdown})]}),e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx(X,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Build Success Rate"})]}),e.jsxs("div",{className:"flex items-center gap-4 mb-4",children:[e.jsxs("span",{className:"text-3xl font-bold text-[#36342E] dark:text-[#E8E6E3]",children:[t.buildSuccessRate,"%"]}),e.jsxs("div",{className:"text-xs text-[#939084]",children:[e.jsxs("div",{children:[t.successfulBuilds," passed"]}),e.jsxs("div",{children:[t.failedBuilds," failed"]}),e.jsxs("div",{children:[t.totalBuilds," total"]})]})]}),e.jsx("div",{className:"h-3 bg-[#F8F4F0] dark:bg-[#1A1A1E] rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full transition-all",style:{width:`${t.buildSuccessRate}%`,background:t.buildSuccessRate>=80?"#1FC5A8":t.buildSuccessRate>=60?"#F59E0B":"#C45B5B"}})}),e.jsxs("div",{className:"mt-6",children:[e.jsx("h4",{className:"text-xs font-medium text-[#939084] uppercase tracking-wider mb-2",children:"Top Templates"}),e.jsx("div",{className:"space-y-2",children:t.templateUsage.slice(0,5).map((c,s)=>e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-sm text-[#36342E] dark:text-[#E8E6E3]",children:[s+1,". ",c.name]}),e.jsxs("span",{className:"text-xs font-mono text-[#939084]",children:[c.count," builds"]})]},s))})]})]})]}),e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-3",children:[e.jsx(I,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Peak Usage Hours"})]}),e.jsx(Ze,{data:t.peakHours})]})]})}const Z=["user.login","user.logout","user.invited","project.created","project.deleted","project.deployed","key.created","key.revoked","role.changed","settings.updated","build.started","build.completed","build.failed"];function Ke(){const n=["admin@company.com","dev@company.com","lead@company.com","ops@company.com"],a=["192.168.1.10","10.0.0.42","172.16.0.5","192.168.1.22"],t=["my-saas-app","cli-tool","api-service","staging","production"],l=["auth","project","admin","deploy","api","system"];return Array.from({length:50},(c,s)=>{const r=Z[Math.floor(Math.random()*Z.length)],o=r.startsWith("user")?"auth":r.startsWith("project")?"project":r.startsWith("key")?"api":r.startsWith("build")?"deploy":r.startsWith("role")||r.startsWith("settings")?"admin":l[Math.floor(Math.random()*l.length)];return{id:`evt-${s}`,timestamp:new Date(Date.now()-s*18e5-Math.random()*6e5).toISOString(),user:n[Math.floor(Math.random()*n.length)],action:r,target:t[Math.floor(Math.random()*t.length)],details:`${r.replace(".",": ")} by user on ${t[Math.floor(Math.random()*t.length)]}`,ip:a[Math.floor(Math.random()*a.length)],category:o}})}const Y={auth:"#553DE9",project:"#1FC5A8",admin:"#F59E0B",deploy:"#C45B5B",api:"#6366F1",system:"#939084"};function Xe({category:n}){return e.jsx("span",{className:"px-1.5 py-0.5 rounded text-[10px] font-medium uppercase tracking-wide",style:{backgroundColor:`${Y[n]}15`,color:Y[n]},children:n})}function Qe({events:n,onFetch:a,pollInterval:t=3e4,className:l=""}){const[c,s]=m.useState(n||Ke()),[r,o]=m.useState(""),[g,j]=m.useState(""),[u,b]=m.useState(""),[h,y]=m.useState(""),[v,E]=m.useState(""),[x,N]=m.useState(!1),[f,D]=m.useState(null),[d,p]=m.useState(1),[A,C]=m.useState(!1),B=m.useRef(null),T=15;m.useEffect(()=>{if(!a||t<=0)return;const i=async()=>{try{const k=await a({search:r,user:g,actionType:u,dateFrom:h,dateTo:v});s(k)}catch{}};return B.current=setInterval(i,t),()=>{B.current&&clearInterval(B.current)}},[a,t,r,g,u,h,v]);const de=[...new Set(c.map(i=>i.user))].sort(),xe=[...new Set(c.map(i=>i.action))].sort(),M=c.filter(i=>{if(r){const k=r.toLowerCase();if(!(i.action.toLowerCase().includes(k)||i.user.toLowerCase().includes(k)||i.target.toLowerCase().includes(k)||i.details.toLowerCase().includes(k)||i.ip.includes(k)))return!1}return!(g&&i.user!==g||u&&i.action!==u||h&&i.timestamp<h||v&&i.timestamp>v+"T23:59:59")}),$=Math.ceil(M.length/T),J=M.slice((d-1)*T,d*T);m.useEffect(()=>{p(1)},[r,g,u,h,v]);const me=m.useCallback(()=>{const i=["Timestamp","User","Action","Target","Details","IP","Category"],k=M.map(F=>[F.timestamp,F.user,F.action,F.target,`"${F.details.replace(/"/g,'""')}"`,F.ip,F.category]),S=[i.join(","),...k.map(F=>F.join(","))].join(`
|
|
32
|
-
`),pe=new Blob([S],{type:"text/csv"}),q=URL.createObjectURL(pe),U=document.createElement("a");U.href=q,U.download=`audit-log-${new Date().toISOString().split("T")[0]}.csv`,U.click(),URL.revokeObjectURL(q)},[M]),he=m.useCallback(async()=>{if(a){C(!0);try{const i=await a({search:r,user:g,actionType:u,dateFrom:h,dateTo:v});s(i)}catch{}finally{C(!1)}}},[a,r,g,u,h,v]),ue=i=>new Date(i).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1});return e.jsxs("div",{className:`space-y-4 ${l}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(H,{size:18,className:"text-[#553DE9]"}),e.jsx("h3",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3] uppercase tracking-wider",children:"Audit Trail"}),e.jsxs("span",{className:"text-xs text-[#939084] ml-1",children:[M.length," event",M.length!==1?"s":""]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(w,{size:"sm",variant:"ghost",icon:le,onClick:he,loading:A,children:"Refresh"}),e.jsx(w,{size:"sm",variant:"ghost",icon:V,onClick:me,children:"CSV"})]})]}),e.jsxs("div",{className:"space-y-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{className:"relative flex-1",children:[e.jsx(Q,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-[#939084]"}),e.jsx("input",{type:"text",value:r,onChange:i=>o(i.target.value),placeholder:"Search events...",className:"w-full pl-9 pr-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3] placeholder-[#939084]"})]}),e.jsx(w,{size:"sm",variant:x?"secondary":"ghost",icon:Pe,onClick:()=>N(!x),children:"Filters"})]}),x&&e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3 p-3 border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg bg-[#F8F4F0] dark:bg-[#222228]",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"User"}),e.jsxs("select",{value:g,onChange:i=>j(i.target.value),className:"w-full px-2 py-1.5 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]",children:[e.jsx("option",{value:"",children:"All Users"}),de.map(i=>e.jsx("option",{value:i,children:i},i))]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"Action"}),e.jsxs("select",{value:u,onChange:i=>b(i.target.value),className:"w-full px-2 py-1.5 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]",children:[e.jsx("option",{value:"",children:"All Actions"}),xe.map(i=>e.jsx("option",{value:i,children:i},i))]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"From"}),e.jsx("input",{type:"date",value:h,onChange:i=>y(i.target.value),className:"w-full px-2 py-1.5 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"To"}),e.jsx("input",{type:"date",value:v,onChange:i=>E(i.target.value),className:"w-full px-2 py-1.5 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]"})]})]})]}),e.jsx("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-[#F8F4F0] dark:bg-[#222228]",children:[e.jsx("th",{className:"w-8 px-2 py-2.5"}),e.jsx("th",{className:"text-left px-3 py-2.5 text-xs font-medium text-[#6B6960]",children:"Timestamp"}),e.jsx("th",{className:"text-left px-3 py-2.5 text-xs font-medium text-[#6B6960]",children:"User"}),e.jsx("th",{className:"text-left px-3 py-2.5 text-xs font-medium text-[#6B6960]",children:"Action"}),e.jsx("th",{className:"text-left px-3 py-2.5 text-xs font-medium text-[#6B6960]",children:"Target"}),e.jsx("th",{className:"text-left px-3 py-2.5 text-xs font-medium text-[#6B6960] hidden md:table-cell",children:"IP"})]})}),e.jsxs("tbody",{children:[J.map(i=>e.jsxs(e.Fragment,{children:[e.jsxs("tr",{className:"border-t border-[#ECEAE3] dark:border-[#2A2A30] hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors cursor-pointer",onClick:()=>D(f===i.id?null:i.id),children:[e.jsx("td",{className:"px-2 py-2.5 text-center",children:f===i.id?e.jsx(ee,{size:12,className:"text-[#939084]"}):e.jsx(R,{size:12,className:"text-[#939084]"})}),e.jsx("td",{className:"px-3 py-2.5",children:e.jsxs("div",{className:"flex items-center gap-1.5 text-xs text-[#939084]",children:[e.jsx(I,{size:12}),e.jsx("span",{className:"font-mono",children:ue(i.timestamp)})]})}),e.jsx("td",{className:"px-3 py-2.5",children:e.jsxs("div",{className:"flex items-center gap-1.5",children:[e.jsx(ke,{name:i.user.split("@")[0],size:"sm"}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] truncate max-w-[150px]",children:i.user})]})}),e.jsx("td",{className:"px-3 py-2.5",children:e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Xe,{category:i.category}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:i.action})]})}),e.jsx("td",{className:"px-3 py-2.5",children:e.jsx("span",{className:"text-[#6B6960] truncate max-w-[120px] block",children:i.target||"--"})}),e.jsx("td",{className:"px-3 py-2.5 hidden md:table-cell",children:e.jsxs("div",{className:"flex items-center gap-1 text-xs text-[#939084]",children:[e.jsx(Ae,{size:12}),e.jsx("span",{className:"font-mono",children:i.ip})]})})]},i.id),f===i.id&&e.jsx("tr",{className:"bg-[#F8F4F0] dark:bg-[#222228]",children:e.jsx("td",{colSpan:6,className:"px-6 py-3",children:e.jsxs("div",{className:"flex items-start gap-2",children:[e.jsx(ge,{size:14,className:"text-[#939084] flex-shrink-0 mt-0.5"}),e.jsxs("div",{className:"text-sm text-[#6B6960]",children:[e.jsx("p",{className:"font-medium text-[#201515] dark:text-[#E8E6E3] mb-1",children:"Details"}),e.jsx("p",{children:i.details}),e.jsxs("div",{className:"flex items-center gap-4 mt-2 text-xs text-[#939084]",children:[e.jsxs("span",{children:["Event ID: ",i.id]}),e.jsxs("span",{children:["Full timestamp: ",new Date(i.timestamp).toISOString()]}),e.jsxs("span",{children:["IP: ",i.ip]})]})]})]})})},`${i.id}-details`)]})),J.length===0&&e.jsx("tr",{children:e.jsx("td",{colSpan:6,className:"px-4 py-8 text-center text-sm text-[#939084]",children:"No events match your filters."})})]})]})}),$>1&&e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("span",{className:"text-xs text-[#939084]",children:["Showing ",(d-1)*T+1,"-",Math.min(d*T,M.length)," of ",M.length]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx("button",{onClick:()=>p(i=>Math.max(1,i-1)),disabled:d===1,className:"p-1.5 rounded-lg text-[#939084] hover:bg-[#F8F4F0] dark:hover:bg-[#222228] disabled:opacity-40 transition-colors",children:e.jsx(we,{size:16})}),Array.from({length:Math.min($,5)},(i,k)=>{let S;return $<=5||d<=3?S=k+1:d>=$-2?S=$-4+k:S=d-2+k,e.jsx("button",{onClick:()=>p(S),className:`w-8 h-8 rounded-lg text-xs font-medium transition-colors ${d===S?"bg-[#553DE9] text-white":"text-[#939084] hover:bg-[#F8F4F0] dark:hover:bg-[#222228]"}`,children:S},S)}),e.jsx("button",{onClick:()=>p(i=>Math.min($,i+1)),disabled:d===$,className:"p-1.5 rounded-lg text-[#939084] hover:bg-[#F8F4F0] dark:hover:bg-[#222228] disabled:opacity-40 transition-colors",children:e.jsx(R,{size:16})})]})]})]})}const et=[{id:"u-1",name:"Alex Chen",email:"alex@company.com",role:"admin",status:"active",lastActive:new Date(Date.now()-3e5).toISOString(),joinedAt:new Date(Date.now()-180*864e5).toISOString()},{id:"u-2",name:"Sarah Johnson",email:"sarah@company.com",role:"editor",status:"active",lastActive:new Date(Date.now()-36e5).toISOString(),joinedAt:new Date(Date.now()-90*864e5).toISOString()},{id:"u-3",name:"Mike Davis",email:"mike@company.com",role:"editor",status:"active",lastActive:new Date(Date.now()-864e5).toISOString(),joinedAt:new Date(Date.now()-60*864e5).toISOString()},{id:"u-4",name:"Emily Park",email:"emily@company.com",role:"viewer",status:"inactive",lastActive:new Date(Date.now()-30*864e5).toISOString(),joinedAt:new Date(Date.now()-120*864e5).toISOString()},{id:"u-5",name:"Jordan Lee",email:"jordan@company.com",role:"editor",status:"invited",lastActive:null,joinedAt:new Date(Date.now()-2*864e5).toISOString()}],P={admin:{label:"Admin",color:"#553DE9"},editor:{label:"Editor",color:"#1FC5A8"},viewer:{label:"Viewer",color:"#939084"}},K={active:{label:"Active",color:"#1FC5A8"},inactive:{label:"Inactive",color:"#939084"},invited:{label:"Invited",color:"#F59E0B"}};function O(n){if(!n)return"Never";const a=Date.now()-new Date(n).getTime(),t=Math.floor(a/6e4);if(t<1)return"Just now";if(t<60)return`${t}m ago`;const l=Math.floor(t/60);if(l<24)return`${l}h ago`;const c=Math.floor(l/24);return c<30?`${c}d ago`:`${Math.floor(c/30)}mo ago`}function oe(n){return n.split(" ").map(a=>a[0]).join("").toUpperCase().slice(0,2)}function tt({onSubmit:n,onCancel:a}){const[t,l]=m.useState(""),[c,s]=m.useState("editor");return e.jsxs("div",{className:"border border-[#553DE9]/20 bg-[#553DE9]/5 dark:bg-[#553DE9]/10 rounded-lg p-4 space-y-3",children:[e.jsx("h4",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3]",children:"Invite New User"}),e.jsxs("div",{className:"grid grid-cols-1 md:grid-cols-3 gap-3",children:[e.jsxs("div",{className:"md:col-span-2",children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"Email"}),e.jsxs("div",{className:"relative",children:[e.jsx(Ce,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-[#939084]"}),e.jsx("input",{type:"email",value:t,onChange:r=>l(r.target.value),placeholder:"colleague@company.com",className:"w-full pl-9 pr-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3] placeholder-[#939084]",autoFocus:!0})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"Role"}),e.jsxs("select",{value:c,onChange:r=>s(r.target.value),className:"w-full px-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]",children:[e.jsx("option",{value:"admin",children:"Admin"}),e.jsx("option",{value:"editor",children:"Editor"}),e.jsx("option",{value:"viewer",children:"Viewer"})]})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(w,{size:"sm",icon:ie,onClick:()=>n(t,c),disabled:!t.trim()||!t.includes("@"),children:"Send Invite"}),e.jsx(w,{size:"sm",variant:"ghost",onClick:a,children:"Cancel"})]})]})}function st({user:n,onClose:a,onChangeRole:t,onToggleStatus:l}){return e.jsxs("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg p-4 space-y-4",children:[e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{className:"flex items-center gap-3",children:[n.avatar_url?e.jsx("img",{src:n.avatar_url,alt:"",className:"w-10 h-10 rounded-full"}):e.jsx("div",{className:"w-10 h-10 rounded-full bg-[#553DE9] flex items-center justify-center text-white text-sm font-bold",children:oe(n.name)}),e.jsxs("div",{children:[e.jsx("p",{className:"text-sm font-medium text-[#201515] dark:text-[#E8E6E3]",children:n.name}),e.jsx("p",{className:"text-xs text-[#939084]",children:n.email})]})]}),e.jsx("button",{onClick:a,className:"p-1 rounded-lg text-[#939084] hover:bg-[#F8F4F0] dark:hover:bg-[#222228]",children:e.jsx(te,{size:16})})]}),e.jsxs("div",{className:"grid grid-cols-2 gap-4 text-sm",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"Role"}),e.jsxs("select",{value:n.role,onChange:c=>t(c.target.value),className:"w-full px-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]",children:[e.jsx("option",{value:"admin",children:"Admin"}),e.jsx("option",{value:"editor",children:"Editor"}),e.jsx("option",{value:"viewer",children:"Viewer"})]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs font-medium text-[#6B6960] mb-1",children:"Status"}),e.jsx("button",{onClick:l,className:"flex items-center gap-2 px-3 py-2 w-full rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-sm text-left hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors",children:n.status==="active"?e.jsxs(e.Fragment,{children:[e.jsx(He,{size:16,className:"text-[#1FC5A8]"}),e.jsx("span",{className:"text-[#1FC5A8]",children:"Active"})]}):e.jsxs(e.Fragment,{children:[e.jsx(_e,{size:16,className:"text-[#939084]"}),e.jsx("span",{className:"text-[#939084]",children:"Inactive"})]})})]})]}),e.jsxs("div",{className:"flex items-center gap-6 text-xs text-[#939084] pt-2 border-t border-[#ECEAE3] dark:border-[#2A2A30]",children:[e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(I,{size:12}),e.jsxs("span",{children:["Last active: ",O(n.lastActive)]})]}),e.jsxs("div",{className:"flex items-center gap-1",children:[e.jsx(I,{size:12}),e.jsxs("span",{children:["Joined: ",O(n.joinedAt)]})]})]})]})}function at({users:n,onChangeRole:a,onToggleStatus:t,onInviteUser:l,className:c=""}){const[s,r]=m.useState(n||et),[o,g]=m.useState(""),[j,u]=m.useState(""),[b,h]=m.useState(!1),[y,v]=m.useState(null),E=s.filter(d=>{if(o){const p=o.toLowerCase();if(!d.name.toLowerCase().includes(p)&&!d.email.toLowerCase().includes(p))return!1}return!(j&&d.role!==j)}),x=s.find(d=>d.id===y)||null,N=m.useCallback(async(d,p)=>{l&&await l(d,p);const A={id:`u-${Date.now()}`,name:d.split("@")[0],email:d,role:p,status:"invited",lastActive:null,joinedAt:new Date().toISOString()};r(C=>[...C,A]),h(!1)},[l]),f=m.useCallback(async(d,p)=>{a&&await a(d,p),r(A=>A.map(C=>C.id===d?{...C,role:p}:C))},[a]),D=m.useCallback(async d=>{const p=s.find(C=>C.id===d);if(!p)return;const A=p.status!=="active";t&&await t(d,A),r(C=>C.map(B=>B.id===d?{...B,status:A?"active":"inactive"}:B))},[s,t]);return e.jsxs("div",{className:`space-y-4 ${c}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(z,{size:18,className:"text-[#553DE9]"}),e.jsx("h3",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3] uppercase tracking-wider",children:"User Management"}),e.jsxs("span",{className:"text-xs text-[#939084]",children:[s.length," users"]})]}),!b&&e.jsx(w,{size:"sm",variant:"secondary",icon:ie,onClick:()=>h(!0),children:"Invite User"})]}),b&&e.jsx(tt,{onSubmit:N,onCancel:()=>h(!1)}),x&&e.jsx(st,{user:x,onClose:()=>v(null),onChangeRole:d=>f(x.id,d),onToggleStatus:()=>D(x.id)}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsxs("div",{className:"relative flex-1",children:[e.jsx(Q,{size:14,className:"absolute left-3 top-1/2 -translate-y-1/2 text-[#939084]"}),e.jsx("input",{type:"text",value:o,onChange:d=>g(d.target.value),placeholder:"Search users...",className:"w-full pl-9 pr-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3] placeholder-[#939084]"})]}),e.jsxs("select",{value:j,onChange:d=>u(d.target.value),className:"px-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3]",children:[e.jsx("option",{value:"",children:"All Roles"}),e.jsx("option",{value:"admin",children:"Admin"}),e.jsx("option",{value:"editor",children:"Editor"}),e.jsx("option",{value:"viewer",children:"Viewer"})]})]}),e.jsxs("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg overflow-hidden divide-y divide-[#ECEAE3] dark:divide-[#2A2A30]",children:[E.map(d=>e.jsxs("button",{type:"button",onClick:()=>v(y===d.id?null:d.id),className:`w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors ${y===d.id?"bg-[#F8F4F0] dark:bg-[#222228]":""}`,children:[d.avatar_url?e.jsx("img",{src:d.avatar_url,alt:"",className:"w-8 h-8 rounded-full flex-shrink-0"}):e.jsx("div",{className:"w-8 h-8 rounded-full bg-[#553DE9] flex items-center justify-center text-white text-xs font-bold flex-shrink-0",children:oe(d.name)}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:"text-sm font-medium text-[#201515] dark:text-[#E8E6E3] truncate",children:d.name}),e.jsx("p",{className:"text-xs text-[#939084] truncate",children:d.email})]}),e.jsx("span",{className:"px-2 py-0.5 rounded text-[10px] font-medium flex-shrink-0",style:{backgroundColor:`${P[d.role].color}15`,color:P[d.role].color},children:P[d.role].label}),e.jsxs("div",{className:"flex items-center gap-1.5 flex-shrink-0",children:[e.jsx("span",{className:"w-2 h-2 rounded-full",style:{backgroundColor:K[d.status].color}}),e.jsx("span",{className:"text-xs text-[#939084] hidden md:inline",children:K[d.status].label})]}),e.jsx("span",{className:"text-xs text-[#939084] flex-shrink-0 hidden lg:inline w-16 text-right",children:O(d.lastActive)}),e.jsx(R,{size:14,className:"text-[#939084] flex-shrink-0"})]},d.id)),E.length===0&&e.jsx("div",{className:"px-4 py-8 text-center text-sm text-[#939084]",children:"No users match your search."})]})]})}const nt=[{id:"proj-1",name:"Customer Portal v2",submittedBy:"sarah@company.com",template:"SaaS App",provider:"Claude",estimatedCost:12.5,estimatedIterations:15,submittedAt:new Date(Date.now()-36e5).toISOString(),description:"Complete redesign of the customer-facing portal with new billing integration.",status:"pending"},{id:"proj-2",name:"Internal CLI Tools",submittedBy:"mike@company.com",template:"CLI Tool",provider:"Claude",estimatedCost:4.2,estimatedIterations:8,submittedAt:new Date(Date.now()-72e5).toISOString(),description:"Set of internal CLI tools for deployment automation.",status:"pending"},{id:"proj-3",name:"Analytics Dashboard",submittedBy:"jordan@company.com",template:"SaaS App",provider:"Codex",estimatedCost:8.9,estimatedIterations:12,submittedAt:new Date(Date.now()-144e5).toISOString(),description:"Real-time analytics dashboard for product metrics.",status:"pending"}],rt=[{projectId:"p-1",projectName:"Main Platform",budgetLimit:100,budgetUsed:67.4,alertThreshold:80},{projectId:"p-2",projectName:"Mobile App",budgetLimit:50,budgetUsed:12.3,alertThreshold:75},{projectId:"p-3",projectName:"Data Pipeline",budgetLimit:30,budgetUsed:28.5,alertThreshold:90}],it=["SaaS App","CLI Tool","REST API","Discord Bot","Chrome Extension","Mobile App","Data Pipeline","Landing Page","Docs Site"];function lt({project:n,onApprove:a,onReject:t}){const[l,c]=m.useState(!1),[s,r]=m.useState(""),[o,g]=m.useState(!1);return e.jsxs("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg overflow-hidden",children:[e.jsxs("button",{type:"button",onClick:()=>c(!l),className:"w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors",children:[l?e.jsx(ee,{size:14,className:"text-[#939084]"}):e.jsx(R,{size:14,className:"text-[#939084]"}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"text-sm font-medium text-[#201515] dark:text-[#E8E6E3]",children:n.name}),e.jsx("span",{className:"px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#F59E0B]/10 text-[#F59E0B]",children:"Pending"})]}),e.jsxs("div",{className:"flex items-center gap-3 text-xs text-[#939084] mt-0.5",children:[e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(Se,{size:10}),n.submittedBy]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(se,{size:10}),n.template]}),e.jsxs("span",{className:"flex items-center gap-1",children:[e.jsx(W,{size:10}),"~$",n.estimatedCost.toFixed(2)]})]})]}),e.jsx("span",{className:"text-xs text-[#939084] flex-shrink-0",children:new Date(n.submittedAt).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})})]}),l&&e.jsxs("div",{className:"px-4 pb-4 pt-2 border-t border-[#ECEAE3] dark:border-[#2A2A30] space-y-3",children:[e.jsxs("div",{className:"grid grid-cols-2 md:grid-cols-4 gap-3 text-sm",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-[#6B6960]",children:"Template"}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:n.template})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-[#6B6960]",children:"Provider"}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:n.provider})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-[#6B6960]",children:"Est. Cost"}),e.jsxs("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:["$",n.estimatedCost.toFixed(2)]})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-xs text-[#6B6960]",children:"Est. Iterations"}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:n.estimatedIterations})]})]}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:n.description}),o?e.jsxs("div",{className:"space-y-2",children:[e.jsx("textarea",{value:s,onChange:j=>r(j.target.value),placeholder:"Add a comment (optional)...",rows:2,className:"w-full px-3 py-2 text-sm rounded-lg border border-[#ECEAE3] dark:border-[#2A2A30] bg-white dark:bg-[#1A1A1E] text-[#201515] dark:text-[#E8E6E3] placeholder-[#939084] resize-none"}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(w,{size:"sm",icon:ce,onClick:()=>a(s),children:"Approve"}),e.jsx(w,{size:"sm",variant:"danger",icon:te,onClick:()=>t(s),children:"Reject"}),e.jsx(w,{size:"sm",variant:"ghost",onClick:()=>g(!1),children:"Cancel"})]})]}):e.jsx("div",{className:"flex items-center gap-2",children:e.jsx(w,{size:"sm",variant:"secondary",icon:fe,onClick:()=>g(!0),children:"Review"})})]})]})}function ct({configs:n}){return e.jsx("div",{className:"space-y-3",children:n.map(a=>{const t=a.budgetLimit>0?a.budgetUsed/a.budgetLimit*100:0,l=t>=a.alertThreshold,c=t>=100;return e.jsxs("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg p-3",children:[e.jsxs("div",{className:"flex items-center justify-between mb-2",children:[e.jsx("span",{className:"text-sm font-medium text-[#201515] dark:text-[#E8E6E3]",children:a.projectName}),e.jsxs("div",{className:"flex items-center gap-2",children:[l&&e.jsx(G,{size:14,className:c?"text-[#C45B5B]":"text-[#F59E0B]"}),e.jsxs("span",{className:"text-xs font-mono text-[#939084]",children:["$",a.budgetUsed.toFixed(2)," / $",a.budgetLimit.toFixed(2)]})]})]}),e.jsx("div",{className:"h-2 bg-[#F8F4F0] dark:bg-[#1A1A1E] rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full transition-all",style:{width:`${Math.min(t,100)}%`,background:c?"#C45B5B":l?"#F59E0B":"#1FC5A8"}})}),e.jsxs("div",{className:"flex items-center justify-between mt-1",children:[e.jsxs("span",{className:"text-[10px] text-[#939084]",children:[t.toFixed(0),"% used"]}),e.jsxs("span",{className:"text-[10px] text-[#939084]",children:["Alert at ",a.alertThreshold,"%"]})]})]},a.projectId)})})}function ot({allTemplates:n}){const[a,t]=m.useState({Engineering:new Set(["SaaS App","CLI Tool","REST API","Data Pipeline"]),Design:new Set(["SaaS App","Landing Page","Mobile App"]),Marketing:new Set(["Landing Page","Docs Site"])}),l=Object.keys(a),c=(s,r)=>{t(o=>{const g={...o},j=new Set(g[s]);return j.has(r)?j.delete(r):j.add(r),g[s]=j,g})};return e.jsx("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg overflow-hidden",children:e.jsxs("table",{className:"w-full text-sm",children:[e.jsx("thead",{children:e.jsxs("tr",{className:"bg-[#F8F4F0] dark:bg-[#222228]",children:[e.jsx("th",{className:"text-left px-3 py-2 text-xs font-medium text-[#6B6960]",children:"Template"}),l.map(s=>e.jsx("th",{className:"text-center px-3 py-2 text-xs font-medium text-[#6B6960]",children:s},s))]})}),e.jsx("tbody",{children:n.map(s=>e.jsxs("tr",{className:"border-t border-[#ECEAE3] dark:border-[#2A2A30] hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors",children:[e.jsx("td",{className:"px-3 py-2 text-[#201515] dark:text-[#E8E6E3]",children:s}),l.map(r=>{var o;return e.jsx("td",{className:"text-center px-3 py-2",children:e.jsx("input",{type:"checkbox",checked:((o=a[r])==null?void 0:o.has(s))||!1,onChange:()=>c(r,s),className:"rounded border-[#ECEAE3] text-[#553DE9] focus:ring-[#553DE9]"})},r)})]},s))})]})})}function dt({pendingProjects:n,budgetConfigs:a,className:t=""}){const[l,c]=m.useState(n||nt),[s]=m.useState(a||rt),[r,o]=m.useState("approvals"),g=l.filter(h=>h.status==="pending").length,j=m.useCallback((h,y)=>{c(v=>v.map(E=>E.id===h?{...E,status:"approved"}:E))},[]),u=m.useCallback((h,y)=>{c(v=>v.map(E=>E.id===h?{...E,status:"rejected"}:E))},[]),b=[{id:"approvals",label:"Approvals",icon:ce,count:g},{id:"budgets",label:"Budgets",icon:W},{id:"templates",label:"Templates",icon:se}];return e.jsxs("div",{className:`space-y-4 ${t}`,children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(Le,{size:18,className:"text-[#553DE9]"}),e.jsx("h3",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3] uppercase tracking-wider",children:"Project Governance"})]}),e.jsx("div",{className:"flex items-center gap-1 border-b border-[#ECEAE3] dark:border-[#2A2A30]",children:b.map(h=>e.jsxs("button",{onClick:()=>o(h.id),className:`flex items-center gap-1.5 px-3 py-2 text-xs font-medium border-b-2 transition-colors ${r===h.id?"border-[#553DE9] text-[#553DE9]":"border-transparent text-[#939084] hover:text-[#36342E] dark:hover:text-[#E8E6E3]"}`,children:[e.jsx(h.icon,{size:14}),h.label,h.count!==void 0&&h.count>0&&e.jsx("span",{className:"ml-1 px-1.5 py-0.5 rounded-full text-[10px] bg-[#553DE9]/10 text-[#553DE9]",children:h.count})]},h.id))}),r==="approvals"&&e.jsx("div",{className:"space-y-3",children:l.filter(h=>h.status==="pending").length===0?e.jsxs("div",{className:"text-center py-8",children:[e.jsx(je,{size:24,className:"mx-auto text-[#939084] mb-2"}),e.jsx("p",{className:"text-sm text-[#939084]",children:"No pending approvals"}),e.jsx("p",{className:"text-xs text-[#939084] mt-1",children:"All project requests have been reviewed."})]}):l.filter(h=>h.status==="pending").map(h=>e.jsx(lt,{project:h,onApprove:y=>j(h.id,y),onReject:y=>u(h.id,y)},h.id))}),r==="budgets"&&e.jsx(ct,{configs:s}),r==="templates"&&e.jsx(ot,{allTemplates:it})]})}function xt(){return[{id:"c-1",title:"API Keys Rotated Within 90 Days",description:"All active API keys should be rotated at least every 90 days to minimize exposure risk.",status:"warning",category:"security",lastChecked:new Date(Date.now()-36e5).toISOString(),remediationUrl:"/admin/settings",details:"2 of 3 keys are within 90 days. 1 key (Production CI/CD) is 120 days old."},{id:"c-2",title:"All Users Have MFA Enabled",description:"Multi-factor authentication should be enabled for all user accounts.",status:"fail",category:"access",lastChecked:new Date(Date.now()-36e5).toISOString(),remediationUrl:"/admin/settings",details:"3 of 5 users have MFA enabled. 2 users need to enable MFA."},{id:"c-3",title:"Audit Logging Enabled",description:"All user actions should be captured in the audit log.",status:"pass",category:"audit",lastChecked:new Date(Date.now()-36e5).toISOString(),details:"Audit logging is active. 1,247 events captured in the last 30 days."},{id:"c-4",title:"Data Retention Policy Set",description:"A data retention policy must be configured specifying how long logs and data are kept.",status:"pass",category:"data",lastChecked:new Date(Date.now()-36e5).toISOString(),details:"Retention policy: 90 days for logs, 365 days for audit events."},{id:"c-5",title:"Access Review Completed",description:"User access should be reviewed quarterly to ensure least-privilege principle.",status:"warning",category:"access",lastChecked:new Date(Date.now()-36e5).toISOString(),remediationUrl:"/admin",details:"Last access review was 85 days ago. Due for review within 5 days."},{id:"c-6",title:"Encryption at Rest Enabled",description:"All stored data including project files and secrets must be encrypted at rest.",status:"pass",category:"security",lastChecked:new Date(Date.now()-36e5).toISOString(),details:"AES-256 encryption enabled for all storage backends."},{id:"c-7",title:"Session Timeout Configured",description:"User sessions should expire after a configured period of inactivity.",status:"pass",category:"security",lastChecked:new Date(Date.now()-36e5).toISOString(),details:"Session timeout set to 30 minutes of inactivity."},{id:"c-8",title:"Rate Limiting Active",description:"API rate limiting should be enabled to prevent abuse and ensure fair usage.",status:"pass",category:"security",lastChecked:new Date(Date.now()-36e5).toISOString(),details:"Rate limit: 100 requests/minute per API key."}]}function mt({status:n}){switch(n){case"pass":return e.jsx(ae,{size:18,className:"text-[#1FC5A8]"});case"fail":return e.jsx(ne,{size:18,className:"text-[#C45B5B]"});case"warning":return e.jsx(G,{size:18,className:"text-[#F59E0B]"})}}const ht={security:Fe,access:z,data:De,audit:ve};function ut({score:n}){const s=2*Math.PI*56,o=270/360*s,g=n/100*o,j=n>=80?"#1FC5A8":n>=60?"#F59E0B":"#C45B5B",u=135;return e.jsxs("div",{className:"relative inline-flex items-center justify-center",children:[e.jsxs("svg",{width:140,height:140,viewBox:"0 0 140 140",children:[e.jsx("circle",{cx:70,cy:70,r:56,fill:"none",stroke:"currentColor",strokeOpacity:"0.08",strokeWidth:10,strokeDasharray:`${o} ${s}`,strokeLinecap:"round",transform:`rotate(${u} 70 70)`}),e.jsx("circle",{cx:70,cy:70,r:56,fill:"none",stroke:j,strokeWidth:10,strokeDasharray:`${g} ${s}`,strokeLinecap:"round",transform:`rotate(${u} 70 70)`,className:"transition-all duration-700"})]}),e.jsxs("div",{className:"absolute inset-0 flex flex-col items-center justify-center",children:[e.jsxs("span",{className:"text-3xl font-bold text-[#36342E] dark:text-[#E8E6E3]",children:[n,"%"]}),e.jsx("span",{className:"text-xs text-[#939084]",children:"Compliant"})]})]})}function pt({items:n,onRefresh:a,className:t=""}){const[l,c]=m.useState(n||xt()),[s,r]=m.useState(!1),[o,g]=m.useState(null),[j,u]=m.useState(!1),b=l.filter(f=>f.status==="pass").length,h=l.filter(f=>f.status==="warning").length,y=l.filter(f=>f.status==="fail").length,v=l.length>0?Math.round(b/l.length*100):0,E=m.useCallback(async()=>{r(!0);try{if(a){const f=await a();c(f)}}catch{}finally{r(!1)}},[a]),x=m.useCallback(()=>{u(!0);const f={generatedAt:new Date().toISOString(),overallScore:v,summary:{pass:b,warning:h,fail:y,total:l.length},items:l.map(A=>({title:A.title,status:A.status,category:A.category,details:A.details,lastChecked:A.lastChecked}))},D=new Blob([JSON.stringify(f,null,2)],{type:"application/json"}),d=URL.createObjectURL(D),p=document.createElement("a");p.href=d,p.download=`compliance-report-${new Date().toISOString().split("T")[0]}.json`,p.click(),URL.revokeObjectURL(d),setTimeout(()=>u(!1),1e3)},[l,v,b,h,y]),N=["security","access","data","audit"];return e.jsxs("div",{className:`space-y-6 ${t}`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(be,{size:18,className:"text-[#553DE9]"}),e.jsx("h3",{className:"text-sm font-semibold text-[#201515] dark:text-[#E8E6E3] uppercase tracking-wider",children:"Compliance"})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(w,{size:"sm",variant:"ghost",icon:le,onClick:E,loading:s,children:"Refresh"}),e.jsx(w,{size:"sm",variant:"secondary",icon:V,onClick:x,loading:j,children:"Generate Report"})]})]}),e.jsx("div",{className:"card p-6",children:e.jsxs("div",{className:"flex items-center gap-8",children:[e.jsx(ut,{score:v}),e.jsxs("div",{className:"flex-1 space-y-3",children:[e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Compliance Score"}),e.jsxs("div",{className:"grid grid-cols-3 gap-4",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(ae,{size:16,className:"text-[#1FC5A8]"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-lg font-bold text-[#36342E] dark:text-[#E8E6E3]",children:b}),e.jsx("span",{className:"text-xs text-[#939084] ml-1",children:"Passing"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(G,{size:16,className:"text-[#F59E0B]"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-lg font-bold text-[#36342E] dark:text-[#E8E6E3]",children:h}),e.jsx("span",{className:"text-xs text-[#939084] ml-1",children:"Warnings"})]})]}),e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(ne,{size:16,className:"text-[#C45B5B]"}),e.jsxs("div",{children:[e.jsx("span",{className:"text-lg font-bold text-[#36342E] dark:text-[#E8E6E3]",children:y}),e.jsx("span",{className:"text-xs text-[#939084] ml-1",children:"Failing"})]})]})]})]})]})}),N.map(f=>{const D=l.filter(p=>p.category===f);if(D.length===0)return null;const d=ht[f];return e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(d,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-xs font-semibold text-[#6B6960] uppercase tracking-wider",children:f})]}),e.jsx("div",{className:"border border-[#ECEAE3] dark:border-[#2A2A30] rounded-lg overflow-hidden divide-y divide-[#ECEAE3] dark:divide-[#2A2A30]",children:D.map(p=>e.jsxs("div",{children:[e.jsxs("button",{type:"button",onClick:()=>g(o===p.id?null:p.id),className:"w-full flex items-center gap-3 px-4 py-3 text-left hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors",children:[e.jsx(mt,{status:p.status}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("p",{className:"text-sm font-medium text-[#201515] dark:text-[#E8E6E3]",children:p.title}),e.jsx("p",{className:"text-xs text-[#939084] truncate",children:p.description})]}),e.jsxs("div",{className:"flex items-center gap-1 text-[10px] text-[#939084] flex-shrink-0",children:[e.jsx(I,{size:10}),"Checked ",new Date(p.lastChecked).toLocaleString("en-US",{month:"short",day:"numeric",hour:"2-digit",minute:"2-digit",hour12:!1})]})]}),o===p.id&&e.jsxs("div",{className:"px-4 pb-3 pt-1 bg-[#F8F4F0] dark:bg-[#222228]",children:[p.details&&e.jsx("p",{className:"text-sm text-[#6B6960] mb-2",children:p.details}),p.remediationUrl&&p.status!=="pass"&&e.jsxs("a",{href:p.remediationUrl,className:"inline-flex items-center gap-1 text-xs text-[#553DE9] hover:underline",children:[e.jsx(Ee,{size:12}),"Fix this issue"]})]})]},p.id))})]},f)})]})}function gt(){return[{label:"Total Users",value:"24",change:"+3 this month",changePositive:!0,icon:z,color:"#553DE9"},{label:"Active Projects",value:"18",change:"+5 this week",changePositive:!0,icon:re,color:"#1FC5A8"},{label:"Total Builds",value:"342",change:"+47 this week",changePositive:!0,icon:Me,color:"#F59E0B"},{label:"Monthly Cost",value:"$201.00",change:"-12% vs last month",changePositive:!0,icon:W,color:"#C45B5B"}]}function jt(){return[{name:"API Server",status:"healthy",latency:45},{name:"Claude Provider",status:"healthy",latency:230},{name:"Codex Provider",status:"degraded",latency:890},{name:"Gemini Provider",status:"healthy",latency:310},{name:"Task Queue",status:"healthy",latency:12},{name:"WebSocket",status:"healthy",latency:8}]}function ft(){return[{user:"alex@company.com",action:"started build",target:"customer-portal"},{user:"sarah@company.com",action:"deployed",target:"analytics-dashboard"},{user:"mike@company.com",action:"created project",target:"cli-tools"},{user:"jordan@company.com",action:"completed build",target:"api-service"},{user:"alex@company.com",action:"updated settings",target:"provider config"},{user:"emily@company.com",action:"invited user",target:"new-dev@company.com"},{user:"sarah@company.com",action:"rotated API key",target:"production"},{user:"mike@company.com",action:"started build",target:"mobile-app"},{user:"jordan@company.com",action:"approved project",target:"data-pipeline"},{user:"alex@company.com",action:"failed build",target:"legacy-service"},{user:"sarah@company.com",action:"created checkpoint",target:"analytics-dashboard"},{user:"emily@company.com",action:"changed role",target:"mike -> editor"},{user:"mike@company.com",action:"deployed",target:"cli-tools"},{user:"alex@company.com",action:"started build",target:"landing-page"},{user:"jordan@company.com",action:"reviewed project",target:"api-service"},{user:"sarah@company.com",action:"completed build",target:"analytics-dashboard"},{user:"emily@company.com",action:"updated template",target:"saas-app"},{user:"mike@company.com",action:"started build",target:"discord-bot"},{user:"alex@company.com",action:"approved project",target:"chrome-ext"},{user:"jordan@company.com",action:"deployed",target:"data-pipeline"}].map((a,t)=>({id:`act-${t}`,...a,timestamp:new Date(Date.now()-t*9e5).toISOString()}))}function bt(){return[{name:"Alex C.",builds:87},{name:"Sarah J.",builds:64},{name:"Mike D.",builds:52},{name:"Jordan L.",builds:41},{name:"Emily P.",builds:38},{name:"Others",builds:60}]}function vt({cards:n}){return e.jsx("div",{className:"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",children:n.map(a=>e.jsx("div",{className:"card p-4",children:e.jsxs("div",{className:"flex items-start justify-between",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-xs text-[#6B6960] uppercase tracking-wider",children:a.label}),e.jsx("p",{className:"text-2xl font-bold text-[#36342E] dark:text-[#E8E6E3] mt-1",children:a.value}),a.change&&e.jsx("p",{className:`text-xs mt-1 ${a.changePositive?"text-[#1FC5A8]":"text-[#C45B5B]"}`,children:a.change})]}),e.jsx("div",{className:"p-2 rounded-lg",style:{backgroundColor:`${a.color}10`,color:a.color},children:e.jsx(a.icon,{size:20})})]})},a.label))})}function Et({data:n}){const a=Math.max(...n.map(t=>t.builds));return e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-4",children:[e.jsx(_,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Builds per User"})]}),e.jsx("div",{className:"space-y-3",children:n.map(t=>e.jsxs("div",{className:"flex items-center gap-3",children:[e.jsx("span",{className:"text-xs text-[#6B6960] w-16 text-right flex-shrink-0",children:t.name}),e.jsx("div",{className:"flex-1 h-6 bg-[#F8F4F0] dark:bg-[#1A1A1E] rounded-full overflow-hidden",children:e.jsx("div",{className:"h-full rounded-full bg-[#553DE9] transition-all duration-500 flex items-center justify-end pr-2",style:{width:`${t.builds/a*100}%`},children:e.jsx("span",{className:"text-[10px] text-white font-mono",children:t.builds})})})]},t.name))})]})}function Nt({health:n}){const a={healthy:"#1FC5A8",degraded:"#F59E0B",down:"#C45B5B"};return e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-4",children:[e.jsx($e,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"System Health"})]}),e.jsx("div",{className:"space-y-2",children:n.map(t=>e.jsxs("div",{className:"flex items-center justify-between py-1.5",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("span",{className:"w-2 h-2 rounded-full flex-shrink-0",style:{backgroundColor:a[t.status]}}),e.jsx("span",{className:"text-sm text-[#36342E] dark:text-[#E8E6E3]",children:t.name})]}),e.jsxs("div",{className:"flex items-center gap-3",children:[t.latency!==void 0&&e.jsxs("span",{className:"text-xs font-mono text-[#939084]",children:[t.latency,"ms"]}),e.jsx("span",{className:"text-[10px] font-medium uppercase",style:{color:a[t.status]},children:t.status})]})]},t.name))})]})}function yt({actions:n}){const a=t=>new Date(t).toLocaleString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!1});return e.jsxs("div",{className:"card p-4",children:[e.jsxs("div",{className:"flex items-center gap-2 mb-4",children:[e.jsx(H,{size:14,className:"text-[#553DE9]"}),e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3]",children:"Recent Activity"})]}),e.jsx("div",{className:"space-y-1 max-h-[400px] overflow-y-auto terminal-scroll",children:n.map(t=>e.jsxs("div",{className:"flex items-center gap-3 px-2 py-1.5 rounded-lg hover:bg-[#F8F4F0] dark:hover:bg-[#222228] transition-colors",children:[e.jsx("span",{className:"text-[10px] font-mono text-[#939084] flex-shrink-0 w-10",children:a(t.timestamp)}),e.jsx("div",{className:"flex-1 min-w-0",children:e.jsxs("span",{className:"text-xs",children:[e.jsx("span",{className:"text-[#553DE9] font-medium",children:t.user.split("@")[0]}),e.jsxs("span",{className:"text-[#6B6960]",children:[" ",t.action," "]}),e.jsx("span",{className:"text-[#201515] dark:text-[#E8E6E3] font-medium",children:t.target})]})})]},t.id))})]})}function kt(){return e.jsxs("div",{className:"max-w-[500px] mx-auto px-6 py-20 text-center",children:[e.jsx(Be,{size:48,className:"mx-auto text-[#C45B5B] mb-4"}),e.jsx("h1",{className:"font-heading text-h1 text-[#36342E] dark:text-[#E8E6E3] mb-2",children:"Access Denied"}),e.jsx("p",{className:"text-sm text-[#6B6960]",children:"You do not have permission to access the admin dashboard. Contact your organization administrator for access."})]})}const At=[{id:"overview",label:"Overview",icon:H},{id:"users",label:"Users",icon:z},{id:"analytics",label:"Analytics",icon:_},{id:"governance",label:"Governance",icon:re},{id:"audit",label:"Audit Trail",icon:I},{id:"compliance",label:"Compliance",icon:X}];function Ht(){const{user:n,isLocalMode:a}=Ne(),[t,l]=m.useState("overview"),c=a||(n==null?void 0:n.authenticated)===!0,[s]=m.useState(gt),[r]=m.useState(jt),[o]=m.useState(ft),[g]=m.useState(bt);return c?e.jsxs("div",{className:"max-w-[1200px] mx-auto px-6 py-8",children:[e.jsx("div",{className:"flex items-center justify-between mb-6",children:e.jsx("h1",{className:"font-heading text-h1 text-[#36342E] dark:text-[#E8E6E3]",children:"Admin"})}),e.jsx("div",{className:"flex items-center gap-1 border-b border-[#ECEAE3] dark:border-[#2A2A30] mb-6 overflow-x-auto",children:At.map(j=>e.jsxs("button",{onClick:()=>l(j.id),className:`flex items-center gap-1.5 px-3 py-2 text-xs font-medium border-b-2 transition-colors whitespace-nowrap ${t===j.id?"border-[#553DE9] text-[#553DE9]":"border-transparent text-[#939084] hover:text-[#36342E] dark:hover:text-[#E8E6E3]"}`,children:[e.jsx(j.icon,{size:14}),j.label]},j.id))}),t==="overview"&&e.jsxs("div",{className:"space-y-6",children:[e.jsx(vt,{cards:s}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-3 gap-4",children:[e.jsxs("div",{className:"card p-4 flex flex-col items-center justify-center",children:[e.jsx("h4",{className:"text-sm font-medium text-[#36342E] dark:text-[#E8E6E3] mb-3",children:"Build Success Rate"}),e.jsx(We,{percentage:94,size:96,strokeWidth:6,color:"#1FC5A8",children:e.jsx("span",{className:"text-lg font-bold text-[#36342E] dark:text-[#E8E6E3]",children:"94%"})}),e.jsx("p",{className:"text-xs text-[#939084] mt-2",children:"342 total builds"})]}),e.jsx("div",{className:"lg:col-span-2",children:e.jsx(Nt,{health:r})})]}),e.jsxs("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-4",children:[e.jsx(Et,{data:g}),e.jsx(yt,{actions:o})]})]}),t==="users"&&e.jsx(at,{}),t==="analytics"&&e.jsx(Ye,{}),t==="governance"&&e.jsx(dt,{}),t==="audit"&&e.jsx(Qe,{}),t==="compliance"&&e.jsx(pt,{})]}):e.jsx(kt,{})}export{Ht as default};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import{c as h,j as s}from"./index-DvZ0Kk2x.js";/**
|
|
2
|
-
* @license lucide-react v0.577.0 - ISC
|
|
3
|
-
*
|
|
4
|
-
* This source code is licensed under the ISC license.
|
|
5
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
6
|
-
*/const x=[["path",{d:"M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2",key:"1yyitq"}],["circle",{cx:"9",cy:"7",r:"4",key:"nufk8"}],["line",{x1:"19",x2:"19",y1:"8",y2:"14",key:"1bvyxn"}],["line",{x1:"22",x2:"16",y1:"11",y2:"11",key:"1shjgl"}]],p=h("user-plus",x),b={sm:{container:"w-6 h-6",text:"text-[10px]",dot:"w-2 h-2 -bottom-0.5 -right-0.5"},md:{container:"w-8 h-8",text:"text-xs",dot:"w-2.5 h-2.5 -bottom-0.5 -right-0.5"},lg:{container:"w-11 h-11",text:"text-sm",dot:"w-3 h-3 -bottom-0.5 -right-0.5"}},d={online:"bg-[#1FC5A8]",offline:"bg-[#939084]",away:"bg-[#E5A940]"};function g(e){let t=0;for(let n=0;n<e.length;n++)t=e.charCodeAt(n)+((t<<5)-t);return Math.abs(t)}const i=["bg-[#553DE9]","bg-[#D63384]","bg-[#1FC5A8]","bg-[#E5A940]","bg-[#3B82F6]","bg-[#8B5CF6]","bg-[#EC4899]","bg-[#06B6D4]"];function u(e){var n;const t=e.trim().split(/\s+/);return t.length>=2?(t[0][0]+t[t.length-1][0]).toUpperCase():(((n=t[0])==null?void 0:n[0])||"?").toUpperCase()}function m({name:e,image:t,size:n="md",status:r,className:l=""}){const o=b[n],c=i[g(e)%i.length],a=u(e);return s.jsxs("span",{className:`relative inline-flex flex-shrink-0 ${l}`,children:[t?s.jsx("img",{src:t,alt:e,className:`${o.container} rounded-full object-cover`}):s.jsx("span",{className:`${o.container} ${c} rounded-full inline-flex items-center justify-center text-white font-semibold ${o.text}`,title:e,children:a}),r&&s.jsx("span",{className:`absolute ${o.dot} ${d[r]} rounded-full border-2 border-white dark:border-[#1A1A1E]`,title:r})]})}export{m as A,p as U};
|