@acarmisc/backstage-plugin-litellm 0.12.3 → 0.12.4

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/dist/index.esm.js CHANGED
@@ -154,7 +154,13 @@ var init_api = __esm({
154
154
 
155
155
  // src/components/DashboardHeader.tsx
156
156
  import React, { useMemo } from "react";
157
- import { Box, Typography, LinearProgress, Paper, Chip, Button, Stack } from "@mui/material";
157
+ import Box from "@mui/material/Box";
158
+ import Typography from "@mui/material/Typography";
159
+ import LinearProgress from "@mui/material/LinearProgress";
160
+ import Paper from "@mui/material/Paper";
161
+ import Chip from "@mui/material/Chip";
162
+ import Button from "@mui/material/Button";
163
+ import Stack from "@mui/material/Stack";
158
164
  import { Add, Key, Warning, Schedule } from "@mui/icons-material";
159
165
  var DashboardHeader;
160
166
  var init_DashboardHeader = __esm({
@@ -218,42 +224,60 @@ var init_DashboardHeader = __esm({
218
224
 
219
225
  // src/components/KeysTable.tsx
220
226
  import React2, { useState, useMemo as useMemo2 } from "react";
221
- import {
222
- Paper as Paper2,
223
- Table,
224
- TableBody,
225
- TableCell,
226
- TableContainer,
227
- TableHead,
228
- TableRow,
229
- Button as Button2,
230
- IconButton,
231
- Box as Box2,
232
- Typography as Typography2,
233
- Dialog,
234
- DialogTitle,
235
- DialogContent,
236
- DialogActions,
237
- TextField,
238
- Chip as Chip2,
239
- CircularProgress,
240
- Autocomplete,
241
- LinearProgress as LinearProgress2,
242
- InputAdornment
243
- } from "@mui/material";
227
+ import Paper2 from "@mui/material/Paper";
228
+ import Table from "@mui/material/Table";
229
+ import TableBody from "@mui/material/TableBody";
230
+ import TableCell from "@mui/material/TableCell";
231
+ import TableContainer from "@mui/material/TableContainer";
232
+ import TableHead from "@mui/material/TableHead";
233
+ import TableRow from "@mui/material/TableRow";
234
+ import Button2 from "@mui/material/Button";
235
+ import IconButton from "@mui/material/IconButton";
236
+ import Box2 from "@mui/material/Box";
237
+ import Typography2 from "@mui/material/Typography";
238
+ import Dialog from "@mui/material/Dialog";
239
+ import DialogTitle from "@mui/material/DialogTitle";
240
+ import DialogContent from "@mui/material/DialogContent";
241
+ import DialogActions from "@mui/material/DialogActions";
242
+ import TextField from "@mui/material/TextField";
243
+ import Chip2 from "@mui/material/Chip";
244
+ import CircularProgress from "@mui/material/CircularProgress";
245
+ import Autocomplete from "@mui/material/Autocomplete";
246
+ import LinearProgress2 from "@mui/material/LinearProgress";
247
+ import InputAdornment from "@mui/material/InputAdornment";
244
248
  import { ContentCopy, Delete, Add as Add2, Edit, Autorenew, Search, Warning as Warning2, Lock, LockOpen } from "@mui/icons-material";
249
+ function expiryChipLabel(status, expiresAt) {
250
+ if (status === "expired") return "Expired";
251
+ if (status === "soon") return `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left`;
252
+ return formatDate(expiresAt);
253
+ }
254
+ function expiryChipColor(status) {
255
+ if (status === "expired") return "error";
256
+ if (status === "soon") return "warning";
257
+ return "default";
258
+ }
245
259
  function ExpiryChip({ expiresAt }) {
246
260
  const status = expiryStatus(expiresAt);
247
261
  if (!status) return /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, "-");
248
- const label = status === "expired" ? "Expired" : status === "soon" ? `${Math.ceil((new Date(expiresAt).getTime() - Date.now()) / 864e5)}d left` : formatDate(expiresAt);
249
- const color = status === "expired" ? "error" : status === "soon" ? "warning" : "default";
262
+ const label = expiryChipLabel(status, expiresAt);
263
+ const color = expiryChipColor(status);
250
264
  const icon = status === "expired" || status === "soon" ? /* @__PURE__ */ React2.createElement(Warning2, { fontSize: "small" }) : void 0;
251
265
  return /* @__PURE__ */ React2.createElement(Chip2, { label, color, size: "small", icon });
252
266
  }
267
+ function budgetColor(pct) {
268
+ if (pct >= 100) return "error";
269
+ if (pct >= 80) return "warning";
270
+ return "primary";
271
+ }
272
+ function keyBlockIcon(isBlocking, blocked) {
273
+ if (isBlocking) return /* @__PURE__ */ React2.createElement(CircularProgress, { size: 18 });
274
+ if (blocked) return /* @__PURE__ */ React2.createElement(LockOpen, { fontSize: "small" });
275
+ return /* @__PURE__ */ React2.createElement(Lock, { fontSize: "small" });
276
+ }
253
277
  function BudgetCell({ spend, maxBudget }) {
254
278
  if (!maxBudget) return /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, "-");
255
279
  const pct = Math.min(100, spend / maxBudget * 100);
256
- const color = pct >= 100 ? "error" : pct >= 80 ? "warning" : "primary";
280
+ const color = budgetColor(pct);
257
281
  return /* @__PURE__ */ React2.createElement(Box2, { minWidth: 100 }, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", justifyContent: "space-between" }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "caption" }, "$", spend.toFixed(2)), /* @__PURE__ */ React2.createElement(Typography2, { variant: "caption", color: "text.secondary" }, "$", maxBudget)), /* @__PURE__ */ React2.createElement(LinearProgress2, { variant: "determinate", value: pct, color, sx: { height: 5, borderRadius: 1, mt: 0.25 } }));
258
282
  }
259
283
  function fmtCost(perToken) {
@@ -414,6 +438,62 @@ var init_KeysTable = __esm({
414
438
  const ctx = formatContextWindow(m.max_input_tokens, m.max_output_tokens);
415
439
  return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ React2.createElement(Typography2, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ React2.createElement(Typography2, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
416
440
  };
441
+ const renderTableBody = () => {
442
+ if (loading) {
443
+ return /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 })));
444
+ }
445
+ if (filteredKeys.length === 0) {
446
+ return /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center", sx: { py: 4 } }, filterText ? /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys match filter") : /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary", gutterBottom: true }, "No keys yet \u2014 generate your first key to start calling models."), /* @__PURE__ */ React2.createElement(
447
+ Button2,
448
+ {
449
+ variant: "contained",
450
+ color: "primary",
451
+ size: "small",
452
+ startIcon: /* @__PURE__ */ React2.createElement(Add2, null),
453
+ onClick: onGenerateKeyClick
454
+ },
455
+ "Generate Your First Key"
456
+ ))));
457
+ }
458
+ return filteredKeys.map((key) => {
459
+ const keyId = key.token ?? key.key;
460
+ const isBlocking = blockingKeyId === keyId;
461
+ return /* @__PURE__ */ React2.createElement(TableRow, { key: keyId, sx: key.blocked ? { bgcolor: "action.disabledBackground" } : void 0 }, /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, key.key_alias || "-", key.blocked && /* @__PURE__ */ React2.createElement(Chip2, { label: "Blocked", color: "error", size: "small" }))), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(
462
+ Typography2,
463
+ {
464
+ variant: "body2",
465
+ component: "code",
466
+ color: "text.secondary",
467
+ title: keyId,
468
+ sx: {
469
+ fontFamily: "monospace",
470
+ backgroundColor: "background.default",
471
+ px: 1,
472
+ py: 0.5,
473
+ borderRadius: 1
474
+ }
475
+ },
476
+ shortKeyId(keyId)
477
+ ), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(ExpiryChip, { expiresAt: key.expires_at })), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2" }, key.tpm_limit ?? "-", " / ", key.rpm_limit ?? "-")), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { onClick: () => handleOpenEdit(key), title: "Edit key" }, /* @__PURE__ */ React2.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React2.createElement(
478
+ IconButton,
479
+ {
480
+ onClick: () => handleToggleBlock(key),
481
+ disabled: isBlocking,
482
+ color: key.blocked ? "warning" : "default",
483
+ title: key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking"
484
+ },
485
+ keyBlockIcon(isBlocking, key.blocked)
486
+ ), /* @__PURE__ */ React2.createElement(
487
+ IconButton,
488
+ {
489
+ color: "error",
490
+ onClick: () => setDeleteConfirmId(keyId),
491
+ title: "Revoke key"
492
+ },
493
+ /* @__PURE__ */ React2.createElement(Delete, null)
494
+ )));
495
+ });
496
+ };
417
497
  return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Paper2, { sx: { mb: 2 } }, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", justifyContent: "space-between", alignItems: "center", p: 2, gap: 2 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "h6" }, "Virtual Keys"), /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 1, alignItems: "center", flex: 1, justifyContent: "flex-end" }, /* @__PURE__ */ React2.createElement(
418
498
  TextField,
419
499
  {
@@ -446,54 +526,7 @@ var init_KeysTable = __esm({
446
526
  onClick: onGenerateKeyClick
447
527
  },
448
528
  "Generate New Key"
449
- ))), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key ID"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Expires"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM / RPM"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, loading ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center" }, /* @__PURE__ */ React2.createElement(CircularProgress, { size: 24 }))) : filteredKeys.length === 0 ? /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, { colSpan: 8, align: "center", sx: { py: 4 } }, filterText ? /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary" }, "No keys match filter") : /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Typography2, { color: "text.secondary", gutterBottom: true }, "No keys yet \u2014 generate your first key to start calling models."), /* @__PURE__ */ React2.createElement(
450
- Button2,
451
- {
452
- variant: "contained",
453
- color: "primary",
454
- size: "small",
455
- startIcon: /* @__PURE__ */ React2.createElement(Add2, null),
456
- onClick: onGenerateKeyClick
457
- },
458
- "Generate Your First Key"
459
- )))) : filteredKeys.map((key) => {
460
- const keyId = key.token ?? key.key;
461
- const isBlocking = blockingKeyId === keyId;
462
- return /* @__PURE__ */ React2.createElement(TableRow, { key: keyId, sx: key.blocked ? { bgcolor: "action.disabledBackground" } : void 0 }, /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, key.key_alias || "-", key.blocked && /* @__PURE__ */ React2.createElement(Chip2, { label: "Blocked", color: "error", size: "small" }))), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", alignItems: "center", gap: 0.5 }, /* @__PURE__ */ React2.createElement(
463
- Typography2,
464
- {
465
- variant: "body2",
466
- component: "code",
467
- color: "text.secondary",
468
- title: keyId,
469
- sx: {
470
- fontFamily: "monospace",
471
- backgroundColor: "background.default",
472
- px: 1,
473
- py: 0.5,
474
- borderRadius: 1
475
- }
476
- },
477
- shortKeyId(keyId)
478
- ), /* @__PURE__ */ React2.createElement(IconButton, { size: "small", onClick: () => copyToClipboard(keyId), title: "Copy Key ID" }, /* @__PURE__ */ React2.createElement(ContentCopy, { fontSize: "small" })))), /* @__PURE__ */ React2.createElement(TableCell, null, formatDate(key.created_at)), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(ExpiryChip, { expiresAt: key.expires_at })), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(BudgetCell, { spend: key.spend ?? 0, maxBudget: key.max_budget })), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2" }, key.tpm_limit ?? "-", " / ", key.rpm_limit ?? "-")), /* @__PURE__ */ React2.createElement(TableCell, null, /* @__PURE__ */ React2.createElement(Box2, { display: "flex", gap: 0.5, flexWrap: "wrap" }, key.models?.slice(0, 2).map((model) => /* @__PURE__ */ React2.createElement(Chip2, { key: model, label: model, size: "small" })), (key.models?.length || 0) > 2 && /* @__PURE__ */ React2.createElement(Chip2, { label: `+${(key.models?.length || 0) - 2}`, size: "small", variant: "outlined" }))), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, /* @__PURE__ */ React2.createElement(IconButton, { onClick: () => handleOpenEdit(key), title: "Edit key" }, /* @__PURE__ */ React2.createElement(Edit, { fontSize: "small" })), /* @__PURE__ */ React2.createElement(
479
- IconButton,
480
- {
481
- onClick: () => handleToggleBlock(key),
482
- disabled: isBlocking,
483
- color: key.blocked ? "warning" : "default",
484
- title: key.blocked ? "Unblock key" : "Block key \u2014 suspends without revoking"
485
- },
486
- isBlocking ? /* @__PURE__ */ React2.createElement(CircularProgress, { size: 18 }) : key.blocked ? /* @__PURE__ */ React2.createElement(LockOpen, { fontSize: "small" }) : /* @__PURE__ */ React2.createElement(Lock, { fontSize: "small" })
487
- ), /* @__PURE__ */ React2.createElement(
488
- IconButton,
489
- {
490
- color: "error",
491
- onClick: () => setDeleteConfirmId(keyId),
492
- title: "Revoke key"
493
- },
494
- /* @__PURE__ */ React2.createElement(Delete, null)
495
- )));
496
- }))))), /* @__PURE__ */ React2.createElement(Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, "Edit Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, editingKey && /* @__PURE__ */ React2.createElement(Box2, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React2.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ React2.createElement(
529
+ ))), /* @__PURE__ */ React2.createElement(TableContainer, null, /* @__PURE__ */ React2.createElement(Table, null, /* @__PURE__ */ React2.createElement(TableHead, null, /* @__PURE__ */ React2.createElement(TableRow, null, /* @__PURE__ */ React2.createElement(TableCell, null, "Alias"), /* @__PURE__ */ React2.createElement(TableCell, null, "Key ID"), /* @__PURE__ */ React2.createElement(TableCell, null, "Created"), /* @__PURE__ */ React2.createElement(TableCell, null, "Expires"), /* @__PURE__ */ React2.createElement(TableCell, null, "Budget"), /* @__PURE__ */ React2.createElement(TableCell, null, "TPM / RPM"), /* @__PURE__ */ React2.createElement(TableCell, null, "Models"), /* @__PURE__ */ React2.createElement(TableCell, { align: "right" }, "Actions"))), /* @__PURE__ */ React2.createElement(TableBody, null, renderTableBody())))), /* @__PURE__ */ React2.createElement(Dialog, { open: !!editingKey, onClose: handleCloseEdit, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React2.createElement(DialogTitle, null, "Edit Key"), /* @__PURE__ */ React2.createElement(DialogContent, null, editingKey && /* @__PURE__ */ React2.createElement(Box2, { display: "flex", flexDirection: "column", gap: 2, mt: 1 }, /* @__PURE__ */ React2.createElement(Typography2, { variant: "body2", color: "text.secondary" }, /* @__PURE__ */ React2.createElement("code", { style: { fontFamily: "monospace", color: "inherit" } }, maskKey(editingKey.key))), /* @__PURE__ */ React2.createElement(
497
530
  TextField,
498
531
  {
499
532
  label: "Alias",
@@ -600,25 +633,23 @@ var init_format = __esm({
600
633
 
601
634
  // src/components/GenerateKeyDialog.tsx
602
635
  import React3, { useState as useState2, useEffect, useMemo as useMemo3 } from "react";
603
- import {
604
- Box as Box3,
605
- Typography as Typography3,
606
- Dialog as Dialog2,
607
- DialogTitle as DialogTitle2,
608
- DialogContent as DialogContent2,
609
- DialogActions as DialogActions2,
610
- TextField as TextField2,
611
- MenuItem,
612
- Button as Button3,
613
- IconButton as IconButton2,
614
- CircularProgress as CircularProgress2,
615
- Autocomplete as Autocomplete2,
616
- Checkbox,
617
- FormControlLabel,
618
- Alert,
619
- Tabs,
620
- Tab
621
- } from "@mui/material";
636
+ import Box3 from "@mui/material/Box";
637
+ import Typography3 from "@mui/material/Typography";
638
+ import Dialog2 from "@mui/material/Dialog";
639
+ import DialogTitle2 from "@mui/material/DialogTitle";
640
+ import DialogContent2 from "@mui/material/DialogContent";
641
+ import DialogActions2 from "@mui/material/DialogActions";
642
+ import TextField2 from "@mui/material/TextField";
643
+ import MenuItem from "@mui/material/MenuItem";
644
+ import Button3 from "@mui/material/Button";
645
+ import IconButton2 from "@mui/material/IconButton";
646
+ import CircularProgress2 from "@mui/material/CircularProgress";
647
+ import Autocomplete2 from "@mui/material/Autocomplete";
648
+ import Checkbox from "@mui/material/Checkbox";
649
+ import FormControlLabel from "@mui/material/FormControlLabel";
650
+ import Alert from "@mui/material/Alert";
651
+ import Tabs from "@mui/material/Tabs";
652
+ import Tab from "@mui/material/Tab";
622
653
  import { ContentCopy as ContentCopy2, Code } from "@mui/icons-material";
623
654
  function trimSlash(url) {
624
655
  return url.replace(/\/+$/, "");
@@ -647,6 +678,21 @@ response = client.chat.completions.create(
647
678
  print(response.choices[0].message.content)`
648
679
  };
649
680
  }
681
+ function aliasHelperText(aliasError, aliasDuplicate) {
682
+ if (aliasError) return "Alias is required";
683
+ if (aliasDuplicate) return "This alias is already used by one of your keys \u2014 LiteLLM requires aliases to be unique across all keys";
684
+ return void 0;
685
+ }
686
+ function teamHelperText(teamError, teamRequired) {
687
+ if (teamError) return "Team is required";
688
+ if (teamRequired) return "Bind this key to a team for scoped access";
689
+ return "Optional: bind this key to a specific team for scoped access";
690
+ }
691
+ function budgetHelperText(budgetInvalid, budgetEstimate) {
692
+ if (budgetInvalid) return 'Enter a positive budget or tick "Unlimited"';
693
+ if (budgetEstimate !== null) return `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate`;
694
+ return void 0;
695
+ }
650
696
  function fmtCost2(perToken) {
651
697
  if (!perToken) return null;
652
698
  const per1k = perToken * 1e3;
@@ -667,7 +713,7 @@ function formatContextWindow2(maxInput, maxOutput) {
667
713
  if (outPart) return `ctx ${outPart} out`;
668
714
  return null;
669
715
  }
670
- var generateDefaultAlias, emptyForm, GenerateKeyDialog, SnippetTabs;
716
+ var generateDefaultAlias, emptyForm, SnippetTabs, GenerateKeyDialog;
671
717
  var init_GenerateKeyDialog = __esm({
672
718
  "src/components/GenerateKeyDialog.tsx"() {
673
719
  "use strict";
@@ -687,6 +733,44 @@ var init_GenerateKeyDialog = __esm({
687
733
  team_id: void 0,
688
734
  key_type: "llm_api"
689
735
  });
736
+ SnippetTabs = ({ snippets, model, onCopy }) => {
737
+ const [tab, setTab] = useState2("curl");
738
+ const code = tab === "curl" ? snippets.curl : snippets.openai;
739
+ return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 } }, /* @__PURE__ */ React3.createElement(Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ React3.createElement(Tab, { label: "OpenAI SDK", value: "openai" })), /* @__PURE__ */ React3.createElement(
740
+ Box3,
741
+ {
742
+ position: "relative",
743
+ p: 1.5,
744
+ sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
745
+ },
746
+ /* @__PURE__ */ React3.createElement(
747
+ IconButton2,
748
+ {
749
+ size: "small",
750
+ onClick: () => onCopy(code),
751
+ title: "Copy snippet",
752
+ sx: { position: "absolute", top: 4, right: 4 }
753
+ },
754
+ /* @__PURE__ */ React3.createElement(ContentCopy2, { fontSize: "small" })
755
+ ),
756
+ /* @__PURE__ */ React3.createElement(
757
+ Typography3,
758
+ {
759
+ component: "pre",
760
+ sx: {
761
+ fontFamily: "monospace",
762
+ fontSize: 12,
763
+ whiteSpace: "pre-wrap",
764
+ wordBreak: "break-all",
765
+ mb: 0,
766
+ pr: 4
767
+ }
768
+ },
769
+ code
770
+ ),
771
+ model && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
772
+ ));
773
+ };
690
774
  GenerateKeyDialog = ({
691
775
  open,
692
776
  onClose,
@@ -782,6 +866,38 @@ var init_GenerateKeyDialog = __esm({
782
866
  const ctx = formatContextWindow2(m.max_input_tokens, m.max_output_tokens);
783
867
  return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement("span", null, m.model_name), m.supports_function_calling && " \u{1F527}", m.supports_vision && " \u{1F441}\uFE0F", ctx && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, ctx), (inCost || outCost) && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", sx: { ml: 1 } }, inCost, " in \xB7 ", outCost, " out"));
784
868
  };
869
+ const renderTeamField = () => {
870
+ if (teams.length > 0) {
871
+ return /* @__PURE__ */ React3.createElement(
872
+ Autocomplete2,
873
+ {
874
+ options: teams,
875
+ getOptionLabel: (t) => t.team_alias || t.team_id,
876
+ value: selectedTeam,
877
+ onChange: (_e, team) => {
878
+ const teamModels = team?.models;
879
+ const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
880
+ setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
881
+ },
882
+ renderInput: (params) => /* @__PURE__ */ React3.createElement(
883
+ TextField2,
884
+ {
885
+ ...params,
886
+ label: "Team",
887
+ error: teamError,
888
+ helperText: teamHelperText(teamError, teamRequired),
889
+ required: teamRequired,
890
+ fullWidth: true
891
+ }
892
+ )
893
+ }
894
+ );
895
+ }
896
+ if (teamRequired) {
897
+ return /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.");
898
+ }
899
+ return null;
900
+ };
785
901
  return /* @__PURE__ */ React3.createElement(Dialog2, { open, onClose: handleClose, maxWidth: "sm", fullWidth: true }, /* @__PURE__ */ React3.createElement(DialogTitle2, null, newKeyValue ? "Key Generated" : "Generate New Key"), /* @__PURE__ */ React3.createElement(DialogContent2, null, newKeyValue ? /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "text.secondary", gutterBottom: true }, "Copy this key now. You won't be able to see it again."), /* @__PURE__ */ React3.createElement(
786
902
  Box3,
787
903
  {
@@ -818,7 +934,7 @@ var init_GenerateKeyDialog = __esm({
818
934
  },
819
935
  error: aliasError,
820
936
  color: aliasDuplicate ? "warning" : void 0,
821
- helperText: aliasError ? "Alias is required" : aliasDuplicate ? "This alias is already used by one of your keys \u2014 LiteLLM requires aliases to be unique across all keys" : void 0,
937
+ helperText: aliasHelperText(aliasError, aliasDuplicate),
822
938
  required: true,
823
939
  fullWidth: true
824
940
  }
@@ -836,30 +952,7 @@ var init_GenerateKeyDialog = __esm({
836
952
  /* @__PURE__ */ React3.createElement(MenuItem, { value: "30d" }, "30 Days"),
837
953
  /* @__PURE__ */ React3.createElement(MenuItem, { value: "90d" }, "90 Days"),
838
954
  /* @__PURE__ */ React3.createElement(MenuItem, { value: "1y" }, "1 Year")
839
- ), teams.length > 0 ? /* @__PURE__ */ React3.createElement(
840
- Autocomplete2,
841
- {
842
- options: teams,
843
- getOptionLabel: (t) => t.team_alias || t.team_id,
844
- value: selectedTeam,
845
- onChange: (_e, team) => {
846
- const teamModels = team?.models;
847
- const restrictedModels = teamModels && teamModels.length > 0 ? (formData.models || []).filter((m) => teamModels.includes(m)) : formData.models;
848
- setFormData({ ...formData, team_id: team?.team_id, models: restrictedModels });
849
- },
850
- renderInput: (params) => /* @__PURE__ */ React3.createElement(
851
- TextField2,
852
- {
853
- ...params,
854
- label: "Team",
855
- error: teamError,
856
- helperText: teamError ? "Team is required" : teamRequired ? "Bind this key to a team for scoped access" : "Optional: bind this key to a specific team for scoped access",
857
- required: teamRequired,
858
- fullWidth: true
859
- }
860
- )
861
- }
862
- ) : teamRequired ? /* @__PURE__ */ React3.createElement(Typography3, { variant: "body2", color: "error" }, "Team selection is required, but you don't belong to any team yet \u2014 contact your administrator.") : null, availableModels.length > 0 && /* @__PURE__ */ React3.createElement(
955
+ ), renderTeamField(), availableModels.length > 0 && /* @__PURE__ */ React3.createElement(
863
956
  Autocomplete2,
864
957
  {
865
958
  multiple: true,
@@ -906,7 +999,7 @@ var init_GenerateKeyDialog = __esm({
906
999
  value: unlimitedBudget ? "" : formData.max_budget ?? "",
907
1000
  onChange: (e) => setFormData({ ...formData, max_budget: e.target.value ? Number(e.target.value) : void 0 }),
908
1001
  error: budgetInvalid,
909
- helperText: budgetInvalid ? 'Enter a positive budget or tick "Unlimited"' : budgetEstimate !== null ? `\u2248 ${fmtInt(budgetEstimate)} tokens at the selected model's rate` : void 0,
1002
+ helperText: budgetHelperText(budgetInvalid, budgetEstimate),
910
1003
  disabled: unlimitedBudget,
911
1004
  required: true,
912
1005
  fullWidth: true
@@ -942,71 +1035,31 @@ var init_GenerateKeyDialog = __esm({
942
1035
  submitting ? /* @__PURE__ */ React3.createElement(CircularProgress2, { size: 24 }) : "Generate"
943
1036
  ))));
944
1037
  };
945
- SnippetTabs = ({ snippets, model, onCopy }) => {
946
- const [tab, setTab] = useState2("curl");
947
- const code = tab === "curl" ? snippets.curl : snippets.openai;
948
- return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Tabs, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 1 } }, /* @__PURE__ */ React3.createElement(Tab, { label: "curl", value: "curl" }), /* @__PURE__ */ React3.createElement(Tab, { label: "OpenAI SDK", value: "openai" })), /* @__PURE__ */ React3.createElement(
949
- Box3,
950
- {
951
- position: "relative",
952
- p: 1.5,
953
- sx: { backgroundColor: "action.hover", border: "1px solid", borderColor: "divider", borderRadius: 1 }
954
- },
955
- /* @__PURE__ */ React3.createElement(
956
- IconButton2,
957
- {
958
- size: "small",
959
- onClick: () => onCopy(code),
960
- title: "Copy snippet",
961
- sx: { position: "absolute", top: 4, right: 4 }
962
- },
963
- /* @__PURE__ */ React3.createElement(ContentCopy2, { fontSize: "small" })
964
- ),
965
- /* @__PURE__ */ React3.createElement(
966
- Typography3,
967
- {
968
- component: "pre",
969
- sx: {
970
- fontFamily: "monospace",
971
- fontSize: 12,
972
- whiteSpace: "pre-wrap",
973
- wordBreak: "break-all",
974
- mb: 0,
975
- pr: 4
976
- }
977
- },
978
- code
979
- ),
980
- model && /* @__PURE__ */ React3.createElement(Typography3, { variant: "caption", color: "text.secondary", display: "block", mt: 1 }, "Using model \u201C", model, "\u201D \u2014 swap it for any model you have access to.")
981
- ));
982
- };
983
1038
  }
984
1039
  });
985
1040
 
986
1041
  // src/components/UsageStats.tsx
987
1042
  import React4, { useMemo as useMemo4, useState as useState3 } from "react";
988
- import {
989
- Paper as Paper3,
990
- Box as Box4,
991
- Typography as Typography4,
992
- FormControl,
993
- InputLabel,
994
- Select,
995
- MenuItem as MenuItem2,
996
- Grid,
997
- Tabs as Tabs2,
998
- Tab as Tab2,
999
- Table as Table2,
1000
- TableBody as TableBody2,
1001
- TableCell as TableCell2,
1002
- TableContainer as TableContainer2,
1003
- TableHead as TableHead2,
1004
- TableRow as TableRow2,
1005
- Chip as Chip3,
1006
- LinearProgress as LinearProgress3,
1007
- Skeleton,
1008
- useTheme
1009
- } from "@mui/material";
1043
+ import Paper3 from "@mui/material/Paper";
1044
+ import Box4 from "@mui/material/Box";
1045
+ import Typography4 from "@mui/material/Typography";
1046
+ import FormControl from "@mui/material/FormControl";
1047
+ import InputLabel from "@mui/material/InputLabel";
1048
+ import Select from "@mui/material/Select";
1049
+ import MenuItem2 from "@mui/material/MenuItem";
1050
+ import Grid from "@mui/material/Grid";
1051
+ import Tabs2 from "@mui/material/Tabs";
1052
+ import Tab2 from "@mui/material/Tab";
1053
+ import Table2 from "@mui/material/Table";
1054
+ import TableBody2 from "@mui/material/TableBody";
1055
+ import TableCell2 from "@mui/material/TableCell";
1056
+ import TableContainer2 from "@mui/material/TableContainer";
1057
+ import TableHead2 from "@mui/material/TableHead";
1058
+ import TableRow2 from "@mui/material/TableRow";
1059
+ import Chip3 from "@mui/material/Chip";
1060
+ import LinearProgress3 from "@mui/material/LinearProgress";
1061
+ import Skeleton from "@mui/material/Skeleton";
1062
+ import { useTheme } from "@mui/material/styles";
1010
1063
  import {
1011
1064
  AreaChart,
1012
1065
  Area,
@@ -1027,7 +1080,7 @@ function modelColor(model) {
1027
1080
  for (let i = 0; i < model.length; i++) h = (h << 5) + h + model.charCodeAt(i) >>> 0;
1028
1081
  return MODEL_COLORS[h % MODEL_COLORS.length];
1029
1082
  }
1030
- var TOP_N_MODELS, PERIOD_LS_KEY, MODEL_COLORS, fmtPct, KpiCard, ChartSkeleton, EmptyChart, UsageStats;
1083
+ var TOP_N_MODELS, PERIOD_LS_KEY, MODEL_COLORS, fmtPct, KpiCard, ChartSkeleton, EmptyChart, ChartOrFallback, UsageStats;
1031
1084
  var init_UsageStats = __esm({
1032
1085
  "src/components/UsageStats.tsx"() {
1033
1086
  "use strict";
@@ -1053,6 +1106,16 @@ var init_UsageStats = __esm({
1053
1106
  height = 260,
1054
1107
  message = "No data for this period"
1055
1108
  }) => /* @__PURE__ */ React4.createElement(Box4, { height, display: "flex", alignItems: "center", justifyContent: "center" }, /* @__PURE__ */ React4.createElement(Typography4, { color: "text.secondary", variant: "body2" }, message));
1109
+ ChartOrFallback = ({
1110
+ loading,
1111
+ empty,
1112
+ height,
1113
+ children
1114
+ }) => {
1115
+ if (loading) return /* @__PURE__ */ React4.createElement(ChartSkeleton, { height });
1116
+ if (empty) return /* @__PURE__ */ React4.createElement(EmptyChart, { height });
1117
+ return /* @__PURE__ */ React4.createElement(React4.Fragment, null, children);
1118
+ };
1056
1119
  UsageStats = ({
1057
1120
  usage,
1058
1121
  models,
@@ -1176,6 +1239,38 @@ var init_UsageStats = __esm({
1176
1239
  const overallSuccessRate = totalRequests > 0 ? (usage?.successful_requests ?? 0) / totalRequests : 0;
1177
1240
  const maxBudget = userInfo?.max_budget ?? 0;
1178
1241
  const totalCumSpend = cumulativeData[cumulativeData.length - 1]?.cumulative ?? 0;
1242
+ const renderKeyRows = () => {
1243
+ if (loading) {
1244
+ return /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React4.createElement(LinearProgress3, null)));
1245
+ }
1246
+ if (keyRows.length === 0) {
1247
+ return /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 7, align: "center" }, "No key activity"));
1248
+ }
1249
+ return [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React4.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2" }, r.keyAlias), r.teamId ? /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.map((m) => /* @__PURE__ */ React4.createElement(Chip3, { key: m, label: m, size: "small", variant: "outlined" })))), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.totalTokens)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React4.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(
1250
+ LinearProgress3,
1251
+ {
1252
+ variant: "determinate",
1253
+ value: r.successRate * 100,
1254
+ sx: { flex: 1, height: 6, borderRadius: 3 }
1255
+ }
1256
+ ), /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")));
1257
+ };
1258
+ const renderModelRows = () => {
1259
+ if (loading) {
1260
+ return /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React4.createElement(LinearProgress3, null)));
1261
+ }
1262
+ if (modelRows.length === 0) {
1263
+ return /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 8, align: "center" }, "No model activity"));
1264
+ }
1265
+ return [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React4.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 0.75 }, /* @__PURE__ */ React4.createElement(Box4, { sx: { width: 10, height: 10, borderRadius: "50%", bgcolor: modelColor(r.model), flexShrink: 0 } }), r.model)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.promptTokens)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.completionTokens)), /* @__PURE__ */ React4.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(
1266
+ LinearProgress3,
1267
+ {
1268
+ variant: "determinate",
1269
+ value: r.successRate * 100,
1270
+ sx: { flex: 1, height: 6, borderRadius: 3 }
1271
+ }
1272
+ ), /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")));
1273
+ };
1179
1274
  return /* @__PURE__ */ React4.createElement(Paper3, { sx: { p: 2 } }, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, flexWrap: "wrap", gap: 2 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "h6" }, "Usage Analytics"), /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 2, alignItems: "center", flexWrap: "wrap" }, /* @__PURE__ */ React4.createElement(FormControl, { size: "small", sx: { minWidth: 140 } }, /* @__PURE__ */ React4.createElement(InputLabel, null, "Period"), /* @__PURE__ */ React4.createElement(
1180
1275
  Select,
1181
1276
  {
@@ -1203,7 +1298,7 @@ var init_UsageStats = __esm({
1203
1298
  value: fmtInt(usage?.total_tokens ?? 0),
1204
1299
  hint: `${fmtInt(usage?.prompt_tokens ?? 0)} in \xB7 ${fmtInt(usage?.completion_tokens ?? 0)} out`
1205
1300
  }
1206
- ))), /* @__PURE__ */ React4.createElement(Tabs2, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 2 } }, /* @__PURE__ */ React4.createElement(Tab2, { value: "costs", label: "Costs" }), /* @__PURE__ */ React4.createElement(Tab2, { value: "models", label: "Model Activity" }), /* @__PURE__ */ React4.createElement(Tab2, { value: "keys", label: "Key Activity" })), tab === "costs" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend by Model"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : modelSpendByDate.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: modelSpendByDate }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, void 0] }), /* @__PURE__ */ React4.createElement(Legend, null), topSpendModels.map((m) => /* @__PURE__ */ React4.createElement(
1301
+ ))), /* @__PURE__ */ React4.createElement(Tabs2, { value: tab, onChange: (_, v) => setTab(v), sx: { mb: 2 } }, /* @__PURE__ */ React4.createElement(Tab2, { value: "costs", label: "Costs" }), /* @__PURE__ */ React4.createElement(Tab2, { value: "models", label: "Model Activity" }), /* @__PURE__ */ React4.createElement(Tab2, { value: "keys", label: "Key Activity" })), tab === "costs" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend by Model"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: modelSpendByDate.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: modelSpendByDate }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, void 0] }), /* @__PURE__ */ React4.createElement(Legend, null), topSpendModels.map((m) => /* @__PURE__ */ React4.createElement(
1207
1302
  Area,
1208
1303
  {
1209
1304
  key: m,
@@ -1214,46 +1309,30 @@ var init_UsageStats = __esm({
1214
1309
  fill: modelColor(m),
1215
1310
  fillOpacity: 0.6
1216
1311
  }
1217
- )))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Token Usage"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : dailyData.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: fmtInt }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtInt(v), void 0] }), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "promptTokens", name: "Input (prompt)", stackId: "tok", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.5 }), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "completionTokens", name: "Output (completion)", stackId: "tok", stroke: "#82ca9d", fill: "#82ca9d", fillOpacity: 0.5 }))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Requests"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : dailyData.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle }), /* @__PURE__ */ React4.createElement(Tooltip, null), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: "#82ca9d", stackId: "r" }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: "#e57373", stackId: "r" }))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Success Rate"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : successRateData.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(LineChart, { data: successRateData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { domain: [0, 100], tick: tickStyle, tickFormatter: (v) => `${v}%` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`${v}%`, "Success rate"] }), /* @__PURE__ */ React4.createElement(ReferenceLine, { y: 100, stroke: "#82ca9d", strokeDasharray: "4 2" }), /* @__PURE__ */ React4.createElement(Line, { type: "monotone", dataKey: "successRate", name: "Success rate", stroke: "#8884d8", dot: { r: 3 } }))))), (maxBudget > 0 || cumulativeData.length > 0) && /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Cumulative Spend", maxBudget > 0 ? ` vs Budget (${fmtUsd(maxBudget)})` : "", maxBudget > 0 && /* @__PURE__ */ React4.createElement(Typography4, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 1 } }, fmtUsd(totalCumSpend), " used \xB7 ", fmtUsd(Math.max(0, maxBudget - totalCumSpend)), " remaining")), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, { height: 180 }) : cumulativeData.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, { height: 180 }) : /* @__PURE__ */ React4.createElement(Box4, { height: 180 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: cumulativeData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Cumulative spend"] }), maxBudget > 0 && /* @__PURE__ */ React4.createElement(ReferenceLine, { y: maxBudget, stroke: "#e57373", strokeDasharray: "6 3", label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 11 } }), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "cumulative", name: "Cumulative spend", stroke: "#ffc658", fill: "#ffc658", fillOpacity: 0.3 })))))), tab === "models" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Model"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : topModelSpendBars.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: Math.max(200, topModelSpendBars.length * 36) }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(YAxis, { type: "category", dataKey: "model", tick: tickStyleSmall, width: 200 }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "spend", name: "Spend", radius: [0, 4, 4, 0] }, topModelSpendBars.map((r) => /* @__PURE__ */ React4.createElement("rect", { key: r.model, fill: modelColor(r.model) }))))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Tokens by Model"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : modelRows.length === 0 ? /* @__PURE__ */ React4.createElement(EmptyChart, null) : /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: modelRows }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "model", tick: tickStyleTiny, interval: 0, angle: -15, textAnchor: "end", height: 60 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle }), /* @__PURE__ */ React4.createElement(Tooltip, null), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "promptTokens", name: "Prompt", fill: "#8884d8", stackId: "t" }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "completionTokens", name: "Completion", fill: "#82ca9d", stackId: "t" }))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React4.createElement(Table2, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead2, null, /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Prompt"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Completion"), /* @__PURE__ */ React4.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React4.createElement(TableBody2, null, loading ? /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 8 }, /* @__PURE__ */ React4.createElement(LinearProgress3, null))) : modelRows.length === 0 ? /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 8, align: "center" }, "No model activity")) : [...modelRows].sort((a, b) => b.spend - a.spend || b.totalTokens - a.totalTokens).map((r) => /* @__PURE__ */ React4.createElement(TableRow2, { key: r.model }, /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 0.75 }, /* @__PURE__ */ React4.createElement(Box4, { sx: { width: 10, height: 10, borderRadius: "50%", bgcolor: modelColor(r.model), flexShrink: 0 } }), r.model)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.successfulRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.promptTokens)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.completionTokens)), /* @__PURE__ */ React4.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(
1218
- LinearProgress3,
1219
- {
1220
- variant: "determinate",
1221
- value: r.successRate * 100,
1222
- sx: { flex: 1, height: 6, borderRadius: 3 }
1223
- }
1224
- ), /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))))), tab === "keys" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Key"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : /* @__PURE__ */ React4.createElement(Box4, { height: Math.max(160, topKeySpendBars.length * 36) }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(YAxis, { type: "category", dataKey: "keyAlias", tick: tickStyleSmall, width: 160 }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "spend", name: "Spend", fill: "#8884d8", radius: [0, 4, 4, 0] }))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React4.createElement(Table2, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead2, null, /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React4.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React4.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React4.createElement(TableBody2, null, loading ? /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 7 }, /* @__PURE__ */ React4.createElement(LinearProgress3, null))) : keyRows.length === 0 ? /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, { colSpan: 7, align: "center" }, "No key activity")) : [...keyRows].sort((a, b) => b.spend - a.spend || b.apiRequests - a.apiRequests).map((r) => /* @__PURE__ */ React4.createElement(TableRow2, { key: r.keyHash }, /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Typography4, { variant: "body2" }, r.keyAlias), r.teamId ? /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption", color: "text.secondary" }, "team: ", r.teamId) : null), /* @__PURE__ */ React4.createElement(TableCell2, null, /* @__PURE__ */ React4.createElement(Box4, { display: "flex", gap: 0.5, flexWrap: "wrap" }, r.models.map((m) => /* @__PURE__ */ React4.createElement(Chip3, { key: m, label: m, size: "small", variant: "outlined" })))), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtUsd(r.spend)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.apiRequests)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.totalTokens)), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, fmtInt(r.failedRequests)), /* @__PURE__ */ React4.createElement(TableCell2, null, r.apiRequests > 0 ? /* @__PURE__ */ React4.createElement(Box4, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React4.createElement(
1225
- LinearProgress3,
1226
- {
1227
- variant: "determinate",
1228
- value: r.successRate * 100,
1229
- sx: { flex: 1, height: 6, borderRadius: 3 }
1230
- }
1231
- ), /* @__PURE__ */ React4.createElement(Typography4, { variant: "caption" }, fmtPct(r.successRate))) : "\u2014")))))))));
1312
+ ))))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Token Usage"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: fmtInt }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtInt(v), void 0] }), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "promptTokens", name: "Input (prompt)", stackId: "tok", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.5 }), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "completionTokens", name: "Output (completion)", stackId: "tok", stroke: "#82ca9d", fill: "#82ca9d", fillOpacity: 0.5 })))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Requests"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: dailyData.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: dailyData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle }), /* @__PURE__ */ React4.createElement(Tooltip, null), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "successfulRequests", name: "Successful", fill: "#82ca9d", stackId: "r" }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "failedRequests", name: "Failed", fill: "#e57373", stackId: "r" })))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Success Rate"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: successRateData.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(LineChart, { data: successRateData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { domain: [0, 100], tick: tickStyle, tickFormatter: (v) => `${v}%` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`${v}%`, "Success rate"] }), /* @__PURE__ */ React4.createElement(ReferenceLine, { y: 100, stroke: "#82ca9d", strokeDasharray: "4 2" }), /* @__PURE__ */ React4.createElement(Line, { type: "monotone", dataKey: "successRate", name: "Success rate", stroke: "#8884d8", dot: { r: 3 } })))))), (maxBudget > 0 || cumulativeData.length > 0) && /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Cumulative Spend", maxBudget > 0 ? ` vs Budget (${fmtUsd(maxBudget)})` : "", maxBudget > 0 && /* @__PURE__ */ React4.createElement(Typography4, { component: "span", variant: "caption", color: "text.secondary", sx: { ml: 1 } }, fmtUsd(totalCumSpend), " used \xB7 ", fmtUsd(Math.max(0, maxBudget - totalCumSpend)), " remaining")), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: cumulativeData.length === 0, height: 180 }, /* @__PURE__ */ React4.createElement(Box4, { height: 180 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(AreaChart, { data: cumulativeData }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "date", tick: tickStyle }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [`$${v.toFixed(4)}`, "Cumulative spend"] }), maxBudget > 0 && /* @__PURE__ */ React4.createElement(ReferenceLine, { y: maxBudget, stroke: "#e57373", strokeDasharray: "6 3", label: { value: `Budget ${fmtUsd(maxBudget)}`, position: "insideTopRight", fontSize: 11 } }), /* @__PURE__ */ React4.createElement(Area, { type: "monotone", dataKey: "cumulative", name: "Cumulative spend", stroke: "#ffc658", fill: "#ffc658", fillOpacity: 0.3 }))))))), tab === "models" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Model"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: topModelSpendBars.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: Math.max(200, topModelSpendBars.length * 36) }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: topModelSpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(YAxis, { type: "category", dataKey: "model", tick: tickStyleSmall, width: 200 }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "spend", name: "Spend", radius: [0, 4, 4, 0] }, topModelSpendBars.map((r) => /* @__PURE__ */ React4.createElement("rect", { key: r.model, fill: modelColor(r.model) })))))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Tokens by Model"), /* @__PURE__ */ React4.createElement(ChartOrFallback, { loading, empty: modelRows.length === 0 }, /* @__PURE__ */ React4.createElement(Box4, { height: 260 }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: modelRows }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { dataKey: "model", tick: tickStyleTiny, interval: 0, angle: -15, textAnchor: "end", height: 60 }), /* @__PURE__ */ React4.createElement(YAxis, { tick: tickStyle }), /* @__PURE__ */ React4.createElement(Tooltip, null), /* @__PURE__ */ React4.createElement(Legend, null), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "promptTokens", name: "Prompt", fill: "#8884d8", stackId: "t" }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "completionTokens", name: "Completion", fill: "#82ca9d", stackId: "t" })))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React4.createElement(Table2, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead2, null, /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, null, "Model"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Success"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Prompt"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Completion"), /* @__PURE__ */ React4.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React4.createElement(TableBody2, null, renderModelRows()))))), tab === "keys" && /* @__PURE__ */ React4.createElement(Grid, { container: true, spacing: 3 }, (loading || topKeySpendBars.length > 0) && /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(Typography4, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Spend by Key"), loading ? /* @__PURE__ */ React4.createElement(ChartSkeleton, null) : /* @__PURE__ */ React4.createElement(Box4, { height: Math.max(160, topKeySpendBars.length * 36) }, /* @__PURE__ */ React4.createElement(ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ React4.createElement(BarChart, { data: topKeySpendBars, layout: "vertical", margin: { left: 20 } }, /* @__PURE__ */ React4.createElement(CartesianGrid, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React4.createElement(XAxis, { type: "number", tick: tickStyle, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React4.createElement(YAxis, { type: "category", dataKey: "keyAlias", tick: tickStyleSmall, width: 160 }), /* @__PURE__ */ React4.createElement(Tooltip, { formatter: (v) => [fmtUsd(v), "Spend"] }), /* @__PURE__ */ React4.createElement(Bar, { dataKey: "spend", name: "Spend", fill: "#8884d8", radius: [0, 4, 4, 0] }))))), /* @__PURE__ */ React4.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React4.createElement(TableContainer2, { component: Paper3, variant: "outlined" }, /* @__PURE__ */ React4.createElement(Table2, { size: "small" }, /* @__PURE__ */ React4.createElement(TableHead2, null, /* @__PURE__ */ React4.createElement(TableRow2, null, /* @__PURE__ */ React4.createElement(TableCell2, null, "Key"), /* @__PURE__ */ React4.createElement(TableCell2, null, "Models"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Spend"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Requests"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Tokens"), /* @__PURE__ */ React4.createElement(TableCell2, { align: "right" }, "Failed"), /* @__PURE__ */ React4.createElement(TableCell2, { sx: { minWidth: 120 } }, "Success rate"))), /* @__PURE__ */ React4.createElement(TableBody2, null, renderKeyRows()))))));
1232
1313
  };
1233
1314
  }
1234
1315
  });
1235
1316
 
1236
1317
  // src/components/TeamUsage.tsx
1237
1318
  import React5, { useState as useState4 } from "react";
1238
- import {
1239
- Paper as Paper4,
1240
- Box as Box5,
1241
- Typography as Typography5,
1242
- LinearProgress as LinearProgress4,
1243
- Chip as Chip4,
1244
- Divider,
1245
- Stack as Stack2,
1246
- Table as Table3,
1247
- TableBody as TableBody3,
1248
- TableCell as TableCell3,
1249
- TableHead as TableHead3,
1250
- TableRow as TableRow3,
1251
- TableContainer as TableContainer3,
1252
- Collapse,
1253
- IconButton as IconButton3,
1254
- CircularProgress as CircularProgress3,
1255
- useTheme as useTheme2
1256
- } from "@mui/material";
1319
+ import Paper4 from "@mui/material/Paper";
1320
+ import Box5 from "@mui/material/Box";
1321
+ import Typography5 from "@mui/material/Typography";
1322
+ import LinearProgress4 from "@mui/material/LinearProgress";
1323
+ import Chip4 from "@mui/material/Chip";
1324
+ import Divider from "@mui/material/Divider";
1325
+ import Stack2 from "@mui/material/Stack";
1326
+ import Table3 from "@mui/material/Table";
1327
+ import TableBody3 from "@mui/material/TableBody";
1328
+ import TableCell3 from "@mui/material/TableCell";
1329
+ import TableHead3 from "@mui/material/TableHead";
1330
+ import TableRow3 from "@mui/material/TableRow";
1331
+ import TableContainer3 from "@mui/material/TableContainer";
1332
+ import Collapse from "@mui/material/Collapse";
1333
+ import IconButton3 from "@mui/material/IconButton";
1334
+ import CircularProgress3 from "@mui/material/CircularProgress";
1335
+ import { useTheme as useTheme2 } from "@mui/material/styles";
1257
1336
  import { ExpandMore, ExpandLess, Group, Speed, Memory } from "@mui/icons-material";
1258
1337
  import {
1259
1338
  AreaChart as AreaChart2,
@@ -1264,6 +1343,11 @@ import {
1264
1343
  Tooltip as Tooltip2,
1265
1344
  ResponsiveContainer as ResponsiveContainer2
1266
1345
  } from "recharts";
1346
+ function budgetBarColor(isOver, isNear) {
1347
+ if (isOver) return "error";
1348
+ if (isNear) return "warning";
1349
+ return "primary";
1350
+ }
1267
1351
  var Stat, TeamCard, TeamUsage;
1268
1352
  var init_TeamUsage = __esm({
1269
1353
  "src/components/TeamUsage.tsx"() {
@@ -1280,6 +1364,15 @@ var init_TeamUsage = __esm({
1280
1364
  const isOver = budget > 0 && spend >= budget;
1281
1365
  const isNear = budget > 0 && spend >= budget * 0.8 && !isOver;
1282
1366
  const dailyData = usage?.daily_usage?.map((d) => ({ date: d.date, spend: d.spend })) ?? [];
1367
+ const renderDailySpendSection = () => {
1368
+ if (usageLoading) {
1369
+ return /* @__PURE__ */ React5.createElement(Box5, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React5.createElement(CircularProgress3, { size: 24 }));
1370
+ }
1371
+ if (dailyData.length > 0) {
1372
+ return /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React5.createElement(Box5, { height: 160, mb: 2 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React5.createElement(CartesianGrid2, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React5.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ React5.createElement(YAxis2, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React5.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React5.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } }));
1373
+ }
1374
+ return null;
1375
+ };
1283
1376
  return /* @__PURE__ */ React5.createElement(Paper4, { variant: "outlined", sx: { p: 2, mb: 2 } }, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1 }, /* @__PURE__ */ React5.createElement(Group, { color: "action" }), /* @__PURE__ */ React5.createElement(Box5, { flexGrow: 1 }, /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle1", fontWeight: 600 }, team.team_alias || "Untitled team"), /* @__PURE__ */ React5.createElement(Typography5, { variant: "caption", color: "text.secondary", sx: { fontFamily: "monospace" } }, team.team_id)), /* @__PURE__ */ React5.createElement(IconButton3, { size: "small", onClick: () => setExpanded((e) => !e), "aria-label": "toggle details" }, expanded ? /* @__PURE__ */ React5.createElement(ExpandLess, null) : /* @__PURE__ */ React5.createElement(ExpandMore, null))), /* @__PURE__ */ React5.createElement(Stack2, { direction: "row", spacing: 3, mt: 1.5, flexWrap: "wrap", useFlexGap: true, gap: 1.5 }, /* @__PURE__ */ React5.createElement(
1284
1377
  Stat,
1285
1378
  {
@@ -1308,23 +1401,23 @@ var init_TeamUsage = __esm({
1308
1401
  Stat,
1309
1402
  {
1310
1403
  label: "TPM",
1311
- value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "\u2014"
1404
+ value: typeof team.tpm_limit === "number" && team.tpm_limit > 0 ? String(team.tpm_limit) : "\u2014"
1312
1405
  }
1313
1406
  ), /* @__PURE__ */ React5.createElement(
1314
1407
  Stat,
1315
1408
  {
1316
1409
  label: "RPM",
1317
- value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "\u2014"
1410
+ value: typeof team.rpm_limit === "number" && team.rpm_limit > 0 ? String(team.rpm_limit) : "\u2014"
1318
1411
  }
1319
1412
  )), budget > 0 && /* @__PURE__ */ React5.createElement(Box5, { mt: 1.5 }, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", justifyContent: "space-between", mb: 0.5 }, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2" }, "$", spend.toFixed(2), " / $", budget.toFixed(2)), isOver && /* @__PURE__ */ React5.createElement(Chip4, { label: "Over Budget", size: "small", color: "error" }), isNear && /* @__PURE__ */ React5.createElement(Chip4, { label: "Near Limit", size: "small", color: "warning" })), /* @__PURE__ */ React5.createElement(
1320
1413
  LinearProgress4,
1321
1414
  {
1322
1415
  variant: "determinate",
1323
1416
  value: budgetPct,
1324
- color: isOver ? "error" : isNear ? "warning" : "primary",
1417
+ color: budgetBarColor(isOver, isNear),
1325
1418
  sx: { height: 6, borderRadius: 1 }
1326
1419
  }
1327
- )), /* @__PURE__ */ React5.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React5.createElement(Box5, { mt: 2 }, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React5.createElement(Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ React5.createElement(Stack2, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ React5.createElement(Stat, { label: "TPM limit", value: team.tpm_limit != null && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ React5.createElement(Stat, { label: "RPM limit", value: team.rpm_limit != null && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ React5.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ React5.createElement(Box5, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ React5.createElement(Chip4, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } }), usageLoading ? /* @__PURE__ */ React5.createElement(Box5, { display: "flex", justifyContent: "center", p: 2 }, /* @__PURE__ */ React5.createElement(CircularProgress3, { size: 24 })) : dailyData.length > 0 ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Daily Spend"), /* @__PURE__ */ React5.createElement(Box5, { height: 160, mb: 2 }, /* @__PURE__ */ React5.createElement(ResponsiveContainer2, { width: "100%", height: "100%" }, /* @__PURE__ */ React5.createElement(AreaChart2, { data: dailyData }, /* @__PURE__ */ React5.createElement(CartesianGrid2, { strokeDasharray: "3 3", stroke: gridStroke }), /* @__PURE__ */ React5.createElement(XAxis2, { dataKey: "date", tick: { fontSize: 11, fill: tickFill } }), /* @__PURE__ */ React5.createElement(YAxis2, { tick: { fontSize: 11, fill: tickFill }, tickFormatter: (v) => `$${v.toFixed(2)}` }), /* @__PURE__ */ React5.createElement(Tooltip2, { formatter: (v) => [`$${v.toFixed(4)}`, "Spend"] }), /* @__PURE__ */ React5.createElement(Area2, { type: "monotone", dataKey: "spend", stroke: "#8884d8", fill: "#8884d8", fillOpacity: 0.3 })))), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } })) : null, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React5.createElement(Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ React5.createElement(TableContainer3, null, /* @__PURE__ */ React5.createElement(Table3, { size: "small" }, /* @__PURE__ */ React5.createElement(TableHead3, null, /* @__PURE__ */ React5.createElement(TableRow3, null, /* @__PURE__ */ React5.createElement(TableCell3, null, "User"), /* @__PURE__ */ React5.createElement(TableCell3, null, "Role"))), /* @__PURE__ */ React5.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React5.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React5.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2" }, m.user_email), /* @__PURE__ */ React5.createElement(
1420
+ )), /* @__PURE__ */ React5.createElement(Collapse, { in: expanded }, /* @__PURE__ */ React5.createElement(Box5, { mt: 2 }, /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React5.createElement(Speed, { fontSize: "small", color: "action" }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary" }, "Rate limits")), /* @__PURE__ */ React5.createElement(Stack2, { direction: "row", spacing: 3, flexWrap: "wrap", useFlexGap: true, gap: 1.5, mb: 2 }, /* @__PURE__ */ React5.createElement(Stat, { label: "TPM limit", value: typeof team.tpm_limit === "number" && team.tpm_limit > 0 ? String(team.tpm_limit) : "Unlimited" }), /* @__PURE__ */ React5.createElement(Stat, { label: "RPM limit", value: typeof team.rpm_limit === "number" && team.rpm_limit > 0 ? String(team.rpm_limit) : "Unlimited" }), /* @__PURE__ */ React5.createElement(Stat, { label: "Max budget", value: budget > 0 ? `$${budget.toFixed(2)}` : "Unlimited" })), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary", gutterBottom: true }, "Models"), team.models?.length ? /* @__PURE__ */ React5.createElement(Box5, { display: "flex", gap: 0.5, flexWrap: "wrap", mb: 2 }, team.models.map((m) => /* @__PURE__ */ React5.createElement(Chip4, { key: m, label: m, size: "small", variant: "outlined" }))) : /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2", color: "text.secondary", mb: 2 }, "All models allowed"), /* @__PURE__ */ React5.createElement(Divider, { sx: { mb: 2 } }), renderDailySpendSection(), /* @__PURE__ */ React5.createElement(Box5, { display: "flex", alignItems: "center", gap: 1, mb: 1 }, /* @__PURE__ */ React5.createElement(Memory, { fontSize: "small", color: "action" }), /* @__PURE__ */ React5.createElement(Typography5, { variant: "subtitle2", color: "text.secondary" }, "Members")), team.members_with_roles?.length ? /* @__PURE__ */ React5.createElement(TableContainer3, null, /* @__PURE__ */ React5.createElement(Table3, { size: "small" }, /* @__PURE__ */ React5.createElement(TableHead3, null, /* @__PURE__ */ React5.createElement(TableRow3, null, /* @__PURE__ */ React5.createElement(TableCell3, null, "User"), /* @__PURE__ */ React5.createElement(TableCell3, null, "Role"))), /* @__PURE__ */ React5.createElement(TableBody3, null, team.members_with_roles.map((m) => /* @__PURE__ */ React5.createElement(TableRow3, { key: m.user_id }, /* @__PURE__ */ React5.createElement(TableCell3, null, m.user_email ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, /* @__PURE__ */ React5.createElement(Typography5, { variant: "body2" }, m.user_email), /* @__PURE__ */ React5.createElement(
1328
1421
  Typography5,
1329
1422
  {
1330
1423
  variant: "caption",
@@ -1368,25 +1461,23 @@ var init_TeamUsage = __esm({
1368
1461
 
1369
1462
  // src/components/AuditLog.tsx
1370
1463
  import React6, { useState as useState5, useCallback } from "react";
1371
- import {
1372
- Paper as Paper5,
1373
- Box as Box6,
1374
- Typography as Typography6,
1375
- Table as Table4,
1376
- TableBody as TableBody4,
1377
- TableCell as TableCell4,
1378
- TableContainer as TableContainer4,
1379
- TableHead as TableHead4,
1380
- TableRow as TableRow4,
1381
- TablePagination,
1382
- TextField as TextField3,
1383
- MenuItem as MenuItem3,
1384
- Chip as Chip5,
1385
- CircularProgress as CircularProgress4,
1386
- Collapse as Collapse2,
1387
- IconButton as IconButton4,
1388
- Alert as Alert2
1389
- } from "@mui/material";
1464
+ import Paper5 from "@mui/material/Paper";
1465
+ import Box6 from "@mui/material/Box";
1466
+ import Typography6 from "@mui/material/Typography";
1467
+ import Table4 from "@mui/material/Table";
1468
+ import TableBody4 from "@mui/material/TableBody";
1469
+ import TableCell4 from "@mui/material/TableCell";
1470
+ import TableContainer4 from "@mui/material/TableContainer";
1471
+ import TableHead4 from "@mui/material/TableHead";
1472
+ import TableRow4 from "@mui/material/TableRow";
1473
+ import TablePagination from "@mui/material/TablePagination";
1474
+ import TextField3 from "@mui/material/TextField";
1475
+ import MenuItem3 from "@mui/material/MenuItem";
1476
+ import Chip5 from "@mui/material/Chip";
1477
+ import CircularProgress4 from "@mui/material/CircularProgress";
1478
+ import Collapse2 from "@mui/material/Collapse";
1479
+ import IconButton4 from "@mui/material/IconButton";
1480
+ import Alert2 from "@mui/material/Alert";
1390
1481
  import { KeyboardArrowDown, KeyboardArrowUp } from "@mui/icons-material";
1391
1482
  import { useAsync } from "react-use";
1392
1483
  function actionColor(action) {
@@ -1403,6 +1494,15 @@ function formatDateTime(iso) {
1403
1494
  return iso;
1404
1495
  }
1405
1496
  }
1497
+ function renderAuditLogBody(loading, entries) {
1498
+ if (loading) {
1499
+ return /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ React6.createElement(CircularProgress4, { size: 24 })));
1500
+ }
1501
+ if (entries.length === 0) {
1502
+ return /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ React6.createElement(Typography6, { color: "text.secondary" }, "No audit events found")));
1503
+ }
1504
+ return entries.map((entry) => /* @__PURE__ */ React6.createElement(DetailRow, { key: entry.id, entry }));
1505
+ }
1406
1506
  var ACTION_COLORS, DetailRow, AuditLog;
1407
1507
  var init_AuditLog = __esm({
1408
1508
  "src/components/AuditLog.tsx"() {
@@ -1480,7 +1580,7 @@ var init_AuditLog = __esm({
1480
1580
  },
1481
1581
  sx: { minWidth: 200 }
1482
1582
  }
1483
- )), error && /* @__PURE__ */ React6.createElement(Box6, { px: 2, pb: 2 }, /* @__PURE__ */ React6.createElement(Alert2, { severity: "error" }, error.message)), /* @__PURE__ */ React6.createElement(TableContainer4, null, /* @__PURE__ */ React6.createElement(Table4, { size: "small" }, /* @__PURE__ */ React6.createElement(TableHead4, null, /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { sx: { width: 40 } }), /* @__PURE__ */ React6.createElement(TableCell4, null, "Time"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Action"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Table"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Object ID"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Changed By"))), /* @__PURE__ */ React6.createElement(TableBody4, null, loading ? /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ React6.createElement(CircularProgress4, { size: 24 }))) : entries.length === 0 ? /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { colSpan: 6, align: "center", sx: { py: 4 } }, /* @__PURE__ */ React6.createElement(Typography6, { color: "text.secondary" }, "No audit events found"))) : entries.map((entry) => /* @__PURE__ */ React6.createElement(DetailRow, { key: entry.id, entry }))))), /* @__PURE__ */ React6.createElement(
1583
+ )), error && /* @__PURE__ */ React6.createElement(Box6, { px: 2, pb: 2 }, /* @__PURE__ */ React6.createElement(Alert2, { severity: "error" }, error.message)), /* @__PURE__ */ React6.createElement(TableContainer4, null, /* @__PURE__ */ React6.createElement(Table4, { size: "small" }, /* @__PURE__ */ React6.createElement(TableHead4, null, /* @__PURE__ */ React6.createElement(TableRow4, null, /* @__PURE__ */ React6.createElement(TableCell4, { sx: { width: 40 } }), /* @__PURE__ */ React6.createElement(TableCell4, null, "Time"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Action"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Table"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Object ID"), /* @__PURE__ */ React6.createElement(TableCell4, null, "Changed By"))), /* @__PURE__ */ React6.createElement(TableBody4, null, renderAuditLogBody(loading, entries)))), /* @__PURE__ */ React6.createElement(
1484
1584
  TablePagination,
1485
1585
  {
1486
1586
  component: "div",
@@ -1505,7 +1605,14 @@ __export(LiteLLMPage_exports, {
1505
1605
  LiteLLMPage: () => LiteLLMPage
1506
1606
  });
1507
1607
  import React7, { useState as useState6, useCallback as useCallback2, useMemo as useMemo5 } from "react";
1508
- import { Box as Box7, Snackbar, Alert as Alert3, CircularProgress as CircularProgress5, Typography as Typography7, Paper as Paper6, Tabs as Tabs3, Tab as Tab3 } from "@mui/material";
1608
+ import Box7 from "@mui/material/Box";
1609
+ import Snackbar from "@mui/material/Snackbar";
1610
+ import Alert3 from "@mui/material/Alert";
1611
+ import CircularProgress5 from "@mui/material/CircularProgress";
1612
+ import Typography7 from "@mui/material/Typography";
1613
+ import Paper6 from "@mui/material/Paper";
1614
+ import Tabs3 from "@mui/material/Tabs";
1615
+ import Tab3 from "@mui/material/Tab";
1509
1616
  import { useAsync as useAsync2, useAsyncRetry } from "react-use";
1510
1617
  import { useApi } from "@backstage/core-plugin-api";
1511
1618
  function initDateRange() {
@@ -1843,17 +1950,15 @@ init_TeamUsage();
1843
1950
  init_api();
1844
1951
  init_format();
1845
1952
  import React9, { useState as useState7, useEffect as useEffect2 } from "react";
1846
- import {
1847
- Paper as Paper7,
1848
- Box as Box8,
1849
- Typography as Typography8,
1850
- FormControl as FormControl2,
1851
- Select as Select2,
1852
- MenuItem as MenuItem4,
1853
- Grid as Grid2,
1854
- CircularProgress as CircularProgress6,
1855
- Alert as Alert4
1856
- } from "@mui/material";
1953
+ import Paper7 from "@mui/material/Paper";
1954
+ import Box8 from "@mui/material/Box";
1955
+ import Typography8 from "@mui/material/Typography";
1956
+ import FormControl2 from "@mui/material/FormControl";
1957
+ import Select2 from "@mui/material/Select";
1958
+ import MenuItem4 from "@mui/material/MenuItem";
1959
+ import Grid2 from "@mui/material/Grid";
1960
+ import CircularProgress6 from "@mui/material/CircularProgress";
1961
+ import Alert4 from "@mui/material/Alert";
1857
1962
  import { AreaChart as AreaChart3, Area as Area3, ResponsiveContainer as ResponsiveContainer3 } from "recharts";
1858
1963
  import { useApi as useApi2 } from "@backstage/core-plugin-api";
1859
1964
  function presetToDateRange(preset) {