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.
Files changed (119) hide show
  1. package/SKILL.md +2 -2
  2. package/VERSION +1 -1
  3. package/autonomy/completion-council.sh +28 -5
  4. package/autonomy/council-v2.sh +13 -7
  5. package/autonomy/lib/done-recognition.sh +24 -16
  6. package/autonomy/lib/proof-check.sh +11 -3
  7. package/autonomy/lib/proof-generator.py +36 -5
  8. package/autonomy/lib/voter-agents.sh +8 -3
  9. package/autonomy/lib/workspace_diff.py +106 -12
  10. package/autonomy/prd-checklist.sh +19 -11
  11. package/autonomy/run.sh +503 -52
  12. package/dashboard/__init__.py +1 -1
  13. package/dashboard/api_v2.py +4 -0
  14. package/dashboard/audit.py +9 -1
  15. package/dashboard/auth.py +16 -4
  16. package/dashboard/registry.py +34 -12
  17. package/dashboard/rigour_integration.py +25 -17
  18. package/dashboard/server.py +252 -104
  19. package/dashboard/static/cost.html +55 -26
  20. package/dashboard/static/index.html +475 -1544
  21. package/dashboard/static/proofs.html +1 -1
  22. package/docs/INSTALLATION.md +1 -1
  23. package/docs/air-gapped.md +7 -2
  24. package/docs/image-provenance.md +4 -2
  25. package/docs/v10/BACKLOG.md +82 -2
  26. package/docs/v10/BOARD.md +105 -0
  27. package/docs/v10/DECISIONS.md +71 -0
  28. package/docs/v10/METRICS.md +2 -1
  29. package/docs/v10/PROGRESS.md +60 -0
  30. package/docs/v10/SWARM-PROMPT.md +98 -0
  31. package/docs/v10/SWARM.md +237 -0
  32. package/loki-ts/dist/loki.js +323 -322
  33. package/mcp/__init__.py +1 -1
  34. package/package.json +1 -1
  35. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  36. package/providers/aider.sh +8 -0
  37. package/providers/cline.sh +8 -0
  38. package/providers/codex.sh +9 -0
  39. package/providers/opencode.sh +9 -0
  40. package/web-app/dist/assets/AdminPage-CZrBkzRF.js +32 -0
  41. package/web-app/dist/assets/Avatar-gQIp6FBI.js +11 -0
  42. package/web-app/dist/assets/{Badge-BF3mhT57.js → Badge-D2_fBTFY.js} +1 -1
  43. package/web-app/dist/assets/{Button-BDp9aJ9q.js → Button-D5qDd2ef.js} +1 -1
  44. package/web-app/dist/assets/CockpitPage-DVMk2RUK.js +47 -0
  45. package/web-app/dist/assets/ComparePage-NMLyE1xL.js +1 -0
  46. package/web-app/dist/assets/{ErrorBoundary-ut3wrUmu.js → ErrorBoundary-BRABI-BT.js} +1 -1
  47. package/web-app/dist/assets/EvidenceReceiptPanel-D2LNKqUx.js +1 -0
  48. package/web-app/dist/assets/GitHubIssuesPanel-BC5GzKUN.js +7 -0
  49. package/web-app/dist/assets/{GitHubPRsPanel-DurZgSaj.js → GitHubPRsPanel-BSlmoVTS.js} +3 -3
  50. package/web-app/dist/assets/HomePage-D_CDMoen.js +12 -0
  51. package/web-app/dist/assets/{LoginPage-HeRGJeZy.js → LoginPage-BBNA_N9t.js} +1 -1
  52. package/web-app/dist/assets/{MagicPage-CUKrqsZs.js → MagicPage-CnFRZCMx.js} +1 -1
  53. package/web-app/dist/assets/MetricsPage-CPfLMRlh.js +6 -0
  54. package/web-app/dist/assets/{NotFoundPage-BZDZ-B77.js → NotFoundPage-lA0XSGTz.js} +1 -1
  55. package/web-app/dist/assets/ProjectPage-CNAPbr9z.js +256 -0
  56. package/web-app/dist/assets/ProjectsPage-CMtm8goN.js +6 -0
  57. package/web-app/dist/assets/SettingsPage-BhIjVMYE.js +10 -0
  58. package/web-app/dist/assets/{ShowcasePage-BGS2jTGH.js → ShowcasePage-BndtGeYo.js} +1 -1
  59. package/web-app/dist/assets/SystemSettingsPage-euXI-KXE.js +1 -0
  60. package/web-app/dist/assets/TeamsPage-ClUKecun.js +11 -0
  61. package/web-app/dist/assets/TemplatesPage-BWHWVk0L.js +29 -0
  62. package/web-app/dist/assets/{TerminalOutput-togzv9Zl.js → TerminalOutput-Cv3N3Szf.js} +1 -1
  63. package/web-app/dist/assets/{activity-Dreyb_Kr.js → activity-dEhz-bXR.js} +1 -1
  64. package/web-app/dist/assets/{bell-DWH0drES.js → bell-B3XUMq1t.js} +1 -1
  65. package/web-app/dist/assets/{bot-78MoCuDl.js → bot-X-ssNlwR.js} +1 -1
  66. package/web-app/dist/assets/{check-ikfylPst.js → check-C_9Nx7kb.js} +1 -1
  67. package/web-app/dist/assets/{chevron-left-DXUqkd8J.js → chevron-left-tvNswo3A.js} +1 -1
  68. package/web-app/dist/assets/{circle-alert-Da74VO8P.js → circle-alert-CSB88nEs.js} +1 -1
  69. package/web-app/dist/assets/{clock-mlGCFq8l.js → clock-C8p2e4NI.js} +1 -1
  70. package/web-app/dist/assets/{cloud-B_Y5ZrIO.js → cloud-CKp2ZvhG.js} +1 -1
  71. package/web-app/dist/assets/{code-xml-Bbom7YWV.js → code-xml-dY70LCHD.js} +1 -1
  72. package/web-app/dist/assets/{database-CLcofiVp.js → database-DObn5fmn.js} +1 -1
  73. package/web-app/dist/assets/{dollar-sign-C5mkjXIM.js → dollar-sign-DSu6n3K3.js} +1 -1
  74. package/web-app/dist/assets/{file-code-corner-CVrkaOQP.js → file-code-corner-DpCnS73z.js} +1 -1
  75. package/web-app/dist/assets/{file-plus-BV9240gX.js → file-plus-D3IPKCf1.js} +1 -1
  76. package/web-app/dist/assets/{globe-aZ0Zso4I.js → globe-C9QXkgH_.js} +1 -1
  77. package/web-app/dist/assets/{hammer-EgOQNukG.js → hammer-D_FtWe1y.js} +1 -1
  78. package/web-app/dist/assets/index-CI54rara.css +1 -0
  79. package/web-app/dist/assets/{index-DvZ0Kk2x.js → index-Cz7q1bvC.js} +2 -2
  80. package/web-app/dist/assets/{layers-BOVKmepE.js → layers-DsW7jnJX.js} +1 -1
  81. package/web-app/dist/assets/{loader-circle-0K8yLgHl.js → loader-circle-B3HspjKR.js} +1 -1
  82. package/web-app/dist/assets/{lock-XzlDMy8M.js → lock-DSI29pct.js} +1 -1
  83. package/web-app/dist/assets/{package-BtXTKusI.js → package-CLNxtA2x.js} +1 -1
  84. package/web-app/dist/assets/{plus-PxeiD8ju.js → plus-DyI4WC4_.js} +1 -1
  85. package/web-app/dist/assets/{refresh-cw-CjqzYczt.js → refresh-cw-D2dv0xA0.js} +1 -1
  86. package/web-app/dist/assets/{rotate-ccw-CY0os4DH.js → rotate-ccw-CBvihA-a.js} +1 -1
  87. package/web-app/dist/assets/scroll-text-DvoOvAl_.js +6 -0
  88. package/web-app/dist/assets/{server-pIIs9vNp.js → server-DenWvSUt.js} +1 -1
  89. package/web-app/dist/assets/{shield-alert-CsDqKXwA.js → shield-alert-40cX_FvG.js} +1 -1
  90. package/web-app/dist/assets/{thumbs-up-CAh3HVzH.js → thumbs-up-BrjZ6QuV.js} +12 -7
  91. package/web-app/dist/assets/trash-2-C6evwkEH.js +16 -0
  92. package/web-app/dist/assets/{trending-down-DrtBd_C9.js → trending-down-CfbZis3O.js} +1 -1
  93. package/web-app/dist/assets/{trending-up-D-DbCHXm.js → trending-up-DWuEK3WC.js} +1 -1
  94. package/web-app/dist/assets/{upload-D4l6DCOt.js → upload-B9DFBcPG.js} +1 -1
  95. package/web-app/dist/assets/{usePolling-D0aoTX80.js → usePolling-BryVC-T1.js} +1 -1
  96. package/web-app/dist/assets/{user-BQf_XJO_.js → user-DMRXwZiw.js} +1 -1
  97. package/web-app/dist/index.html +2 -2
  98. package/web-app/server.py +122 -47
  99. package/web-app/dist/assets/AdminPage-y6_b2Xo4.js +0 -32
  100. package/web-app/dist/assets/Avatar-BFPkXhIJ.js +0 -6
  101. package/web-app/dist/assets/CockpitPage-5TKICCoA.js +0 -47
  102. package/web-app/dist/assets/ComparePage-BSuNsUHy.js +0 -1
  103. package/web-app/dist/assets/EvidenceReceiptPanel-CcaBwEI-.js +0 -1
  104. package/web-app/dist/assets/GitHubIssuesPanel-DJeYgzAa.js +0 -12
  105. package/web-app/dist/assets/HomePage-Bs451LAy.js +0 -28
  106. package/web-app/dist/assets/MetricsPage-DvY1csgz.js +0 -6
  107. package/web-app/dist/assets/ProjectPage-CqHkEZkl.js +0 -257
  108. package/web-app/dist/assets/ProjectsPage-CLvCK1Y8.js +0 -6
  109. package/web-app/dist/assets/SettingsPage-DP7OmdwP.js +0 -10
  110. package/web-app/dist/assets/SystemSettingsPage-P_rlLh0x.js +0 -1
  111. package/web-app/dist/assets/TeamsPage-DxtBDcBC.js +0 -11
  112. package/web-app/dist/assets/TemplatesPage-5e-faH3W.js +0 -29
  113. package/web-app/dist/assets/folder-open-D_6yV78G.js +0 -11
  114. package/web-app/dist/assets/index-BSgmPwC0.css +0 -1
  115. package/web-app/dist/assets/lightbulb-DdASgqOG.js +0 -6
  116. package/web-app/dist/assets/mail-COu670_W.js +0 -6
  117. package/web-app/dist/assets/minus-bQpUt9ff.js +0 -6
  118. package/web-app/dist/assets/save-v8qep5cY.js +0 -6
  119. 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 = 0
2928
- complexity = "standard"
2956
+ iteration = None
2957
+ complexity = None
2929
2958
  current_task = ""
2930
- pending_tasks = 0
2959
+ pending_tasks = None
2931
2960
  max_iterations = 0
2932
- cost_usd = 0.0
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
- # Extract cost from tokens object if present
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
- if max_iterations <= 0:
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
- "running_agents": 0,
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
- "total_tokens": 0,
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": 0,
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
- "difficulty": _difficulty_map.get(f.stem, "intermediate"),
3318
- "build_time": _build_time_map.get(f.stem, "5-10 min"),
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
- complexity = "standard"
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 = 5
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", "standard")
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", 5)
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 if phases else ["planning", "implementation", "testing"],
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": 0,
3582
- "quality_gate_pass_rate": 0.0,
3618
+ "iterations": None,
3619
+ "quality_gate_pass_rate": None,
3583
3620
  "time_elapsed": "",
3584
- "tokens_used": 0,
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 = 0
6288
- _complexity = "standard"
6325
+ _iteration = None
6326
+ _complexity = None
6289
6327
  _current_task = ""
6290
- _pending_tasks = 0
6328
+ _pending_tasks = None
6291
6329
  _agents = []
6292
- _cost_usd = 0.0
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
- # Extract cost from tokens object if present
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
- if _max_iterations <= 0:
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": 0,
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
- "iteration": meta.get("iteration", 0),
6749
- "files_changed": meta.get("files_changed", 0),
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": 0,
6758
- "files_changed": 0,
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": 0,
6770
- "files_changed": 0,
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};