@acarmisc/backstage-plugin-litellm 0.32.0 → 0.33.0
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/components/ManageTeamDialog.d.ts +10 -1
- package/dist/index.cjs.js +261 -181
- package/dist/index.cjs.js.map +3 -3
- package/dist/index.esm.js +261 -181
- package/dist/index.esm.js.map +2 -2
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import { TeamInfo, ModelInfo, LiteLlmConfig, CreateTeamRequest, UpdateTeamRequest, VectorStoreInfo, McpServerInfo } from '../types';
|
|
3
|
+
/** A catalog User entity flattened into what the member picker needs. */
|
|
4
|
+
export interface InitialMember {
|
|
5
|
+
userEntityRef: string;
|
|
6
|
+
maxBudgetInTeam?: number;
|
|
7
|
+
}
|
|
3
8
|
interface ManageTeamDialogProps {
|
|
4
9
|
open: boolean;
|
|
5
10
|
onClose: () => void;
|
|
@@ -7,7 +12,11 @@ interface ManageTeamDialogProps {
|
|
|
7
12
|
team?: TeamInfo;
|
|
8
13
|
allModels: ModelInfo[];
|
|
9
14
|
config?: LiteLlmConfig;
|
|
10
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Saves the team. In create mode, `initialMembers` lists the members picked
|
|
17
|
+
* in the dialog; the parent adds them once the team exists.
|
|
18
|
+
*/
|
|
19
|
+
onSubmit: (payload: CreateTeamRequest | UpdateTeamRequest, initialMembers?: InitialMember[]) => Promise<void>;
|
|
11
20
|
/** When true, the Members section (edit mode only) exposes add/remove controls. */
|
|
12
21
|
canManageMembers?: boolean;
|
|
13
22
|
/** Called to add a member; the parent is expected to refresh `team` on success. */
|
package/dist/index.cjs.js
CHANGED
|
@@ -3204,6 +3204,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3204
3204
|
const [memberBudget, setMemberBudget] = (0, import_react7.useState)("");
|
|
3205
3205
|
const [memberBusy, setMemberBusy] = (0, import_react7.useState)(false);
|
|
3206
3206
|
const [memberError, setMemberError] = (0, import_react7.useState)(null);
|
|
3207
|
+
const [pendingMembers, setPendingMembers] = (0, import_react7.useState)([]);
|
|
3207
3208
|
const [kbIds, setKbIds] = (0, import_react7.useState)([]);
|
|
3208
3209
|
const [kbBusy, setKbBusy] = (0, import_react7.useState)(false);
|
|
3209
3210
|
const [kbError, setKbError] = (0, import_react7.useState)(null);
|
|
@@ -3214,8 +3215,17 @@ var init_ManageTeamDialog = __esm({
|
|
|
3214
3215
|
const maxBudgetCeiling = config?.teamManagement?.maxBudgetCeiling ?? FALLBACK_BUDGET_CEILING;
|
|
3215
3216
|
const hideBudgetForManagers = config?.display?.hideTeamBudgetForManagers ?? false;
|
|
3216
3217
|
const budgetWriteOnly = hideBudgetForManagers && mode === "edit";
|
|
3217
|
-
const
|
|
3218
|
-
const
|
|
3218
|
+
const creating = mode === "create";
|
|
3219
|
+
const members = (0, import_react7.useMemo)(
|
|
3220
|
+
() => creating ? pendingMembers.map((p) => ({
|
|
3221
|
+
user_id: p.userEntityRef,
|
|
3222
|
+
user_email: p.label,
|
|
3223
|
+
role: "user"
|
|
3224
|
+
})) : team?.members_with_roles ?? [],
|
|
3225
|
+
[creating, pendingMembers, team]
|
|
3226
|
+
);
|
|
3227
|
+
const showMembers = !!canManageMembers || !!membersOnly;
|
|
3228
|
+
const wide = showMembers && !membersOnly;
|
|
3219
3229
|
const catalogApi = (0, import_core_plugin_api4.useApi)(import_plugin_catalog_react.catalogApiRef);
|
|
3220
3230
|
const [searchUsers, setSearchUsers] = (0, import_react7.useState)([]);
|
|
3221
3231
|
const [usersLoading, setUsersLoading] = (0, import_react7.useState)(false);
|
|
@@ -3260,8 +3270,8 @@ var init_ManageTeamDialog = __esm({
|
|
|
3260
3270
|
email,
|
|
3261
3271
|
label: email ? `${displayName} (${email})` : displayName
|
|
3262
3272
|
};
|
|
3263
|
-
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).sort((a, b) => a.label.localeCompare(b.label));
|
|
3264
|
-
}, [searchUsers, members]);
|
|
3273
|
+
}).filter((o) => !(o.email && existingEmails.has(o.email.toLowerCase()))).filter((o) => !pendingMembers.some((p) => p.userEntityRef === o.ref)).sort((a, b) => a.label.localeCompare(b.label));
|
|
3274
|
+
}, [searchUsers, members, pendingMembers]);
|
|
3265
3275
|
(0, import_react7.useEffect)(() => {
|
|
3266
3276
|
return () => {
|
|
3267
3277
|
if (debounceTimerRef.current) {
|
|
@@ -3295,6 +3305,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3295
3305
|
setMemberRef("");
|
|
3296
3306
|
setMemberBudget("");
|
|
3297
3307
|
setMemberError(null);
|
|
3308
|
+
setPendingMembers([]);
|
|
3298
3309
|
setKbIds(team?.object_permission?.vector_stores ?? []);
|
|
3299
3310
|
setKbError(null);
|
|
3300
3311
|
setMcpIds(team?.object_permission?.mcp_servers ?? []);
|
|
@@ -3353,7 +3364,13 @@ var init_ManageTeamDialog = __esm({
|
|
|
3353
3364
|
...budgetDuration && { budget_duration: budgetDuration }
|
|
3354
3365
|
};
|
|
3355
3366
|
}
|
|
3356
|
-
await onSubmit(
|
|
3367
|
+
await onSubmit(
|
|
3368
|
+
payload,
|
|
3369
|
+
creating ? pendingMembers.map(({ userEntityRef, maxBudgetInTeam }) => ({
|
|
3370
|
+
userEntityRef,
|
|
3371
|
+
...maxBudgetInTeam !== void 0 && { maxBudgetInTeam }
|
|
3372
|
+
})) : void 0
|
|
3373
|
+
);
|
|
3357
3374
|
onClose();
|
|
3358
3375
|
} catch (err) {
|
|
3359
3376
|
setError(err instanceof Error ? err.message : "An error occurred");
|
|
@@ -3362,13 +3379,26 @@ var init_ManageTeamDialog = __esm({
|
|
|
3362
3379
|
}
|
|
3363
3380
|
};
|
|
3364
3381
|
const handleAddMember = async () => {
|
|
3365
|
-
if (!
|
|
3382
|
+
if (!memberRef.trim() || !creating && !onAddMember) return;
|
|
3366
3383
|
setMemberError(null);
|
|
3367
3384
|
const budget = !membersOnly && memberBudget ? parseFloat(memberBudget) : void 0;
|
|
3368
3385
|
if (budget !== void 0 && (isNaN(budget) || budget <= 0)) {
|
|
3369
3386
|
setMemberError("Max budget in team must be a positive number");
|
|
3370
3387
|
return;
|
|
3371
3388
|
}
|
|
3389
|
+
if (creating) {
|
|
3390
|
+
const ref = memberRef.trim();
|
|
3391
|
+
if (!pendingMembers.some((p) => p.userEntityRef === ref)) {
|
|
3392
|
+
setPendingMembers((list) => [
|
|
3393
|
+
...list,
|
|
3394
|
+
{ userEntityRef: ref, label: memberQuery.trim() || ref, maxBudgetInTeam: budget }
|
|
3395
|
+
]);
|
|
3396
|
+
}
|
|
3397
|
+
setMemberQuery("");
|
|
3398
|
+
setMemberRef("");
|
|
3399
|
+
setMemberBudget("");
|
|
3400
|
+
return;
|
|
3401
|
+
}
|
|
3372
3402
|
try {
|
|
3373
3403
|
setMemberBusy(true);
|
|
3374
3404
|
await onAddMember(memberRef.trim(), budget);
|
|
@@ -3382,6 +3412,10 @@ var init_ManageTeamDialog = __esm({
|
|
|
3382
3412
|
}
|
|
3383
3413
|
};
|
|
3384
3414
|
const handleRemoveMember = async (userId) => {
|
|
3415
|
+
if (creating) {
|
|
3416
|
+
setPendingMembers((list) => list.filter((p) => p.userEntityRef !== userId));
|
|
3417
|
+
return;
|
|
3418
|
+
}
|
|
3385
3419
|
if (!onRemoveMember) return;
|
|
3386
3420
|
setMemberError(null);
|
|
3387
3421
|
try {
|
|
@@ -3435,13 +3469,14 @@ var init_ManageTeamDialog = __esm({
|
|
|
3435
3469
|
}
|
|
3436
3470
|
return { disabled: false };
|
|
3437
3471
|
};
|
|
3472
|
+
const submitLabel = creating ? "Create team" : "Save";
|
|
3438
3473
|
const getDialogTitle = () => {
|
|
3439
3474
|
if (membersOnly) {
|
|
3440
3475
|
return `Manage members \u2014 ${team?.team_alias || "Untitled team"}`;
|
|
3441
3476
|
}
|
|
3442
3477
|
return mode === "create" ? "Create Team" : "Edit Team";
|
|
3443
3478
|
};
|
|
3444
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Dialog3.default, { open, onClose, maxWidth: "sm", fullWidth: true, children: [
|
|
3479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Dialog3.default, { open, onClose, maxWidth: wide ? "md" : "sm", fullWidth: true, children: [
|
|
3445
3480
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_DialogTitle3.default, { children: getDialogTitle() }),
|
|
3446
3481
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_DialogContent3.default, { sx: { pt: 2, display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3447
3482
|
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Alert2.default, { severity: "error", children: error }),
|
|
@@ -3450,186 +3485,215 @@ var init_ManageTeamDialog = __esm({
|
|
|
3450
3485
|
team?.members_with_roles?.find((m) => m.user_id === currentUserId)?.role || "member",
|
|
3451
3486
|
" you can add or remove members. Budgets and models are managed by your platform admins."
|
|
3452
3487
|
] }),
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
{
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
options: modelNames,
|
|
3469
|
-
value: models,
|
|
3470
|
-
onChange: (_, newValue) => setModels(newValue),
|
|
3471
|
-
disabled: submitting,
|
|
3472
|
-
renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3473
|
-
import_TextField3.default,
|
|
3474
|
-
{
|
|
3475
|
-
...params,
|
|
3476
|
-
label: "Models",
|
|
3477
|
-
required: true,
|
|
3478
|
-
helperText: "At least one model is required here \u2014 this delegated flow only grants access to models your admin has allow-listed for team creation, not every proxy model. (Teams with no model restriction shown elsewhere were configured directly in LiteLLM.)"
|
|
3479
|
-
}
|
|
3480
|
-
)
|
|
3481
|
-
}
|
|
3482
|
-
),
|
|
3483
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3484
|
-
import_TextField3.default,
|
|
3485
|
-
{
|
|
3486
|
-
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3487
|
-
type: "number",
|
|
3488
|
-
value: maxBudget,
|
|
3489
|
-
onChange: (e) => setMaxBudget(e.target.value),
|
|
3490
|
-
disabled: submitting || unlimited,
|
|
3491
|
-
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3492
|
-
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3493
|
-
fullWidth: true
|
|
3494
|
-
}
|
|
3495
|
-
),
|
|
3496
|
-
allowUnlimitedBudget && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3497
|
-
import_FormControlLabel2.default,
|
|
3498
|
-
{
|
|
3499
|
-
control: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3500
|
-
label: "Unlimited Budget"
|
|
3501
|
-
}
|
|
3502
|
-
),
|
|
3503
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3504
|
-
import_TextField3.default,
|
|
3505
|
-
{
|
|
3506
|
-
select: true,
|
|
3507
|
-
label: "Budget Duration",
|
|
3508
|
-
value: budgetDuration,
|
|
3509
|
-
onChange: (e) => setBudgetDuration(e.target.value),
|
|
3510
|
-
disabled: submitting || unlimited,
|
|
3511
|
-
helperText: "Spend-reset period for the team budget",
|
|
3512
|
-
fullWidth: true,
|
|
3513
|
-
children: durationOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_MenuItem2.default, { value: o.value, children: o.label }, o.value))
|
|
3514
|
-
}
|
|
3515
|
-
)
|
|
3516
|
-
] }),
|
|
3517
|
-
showMembers && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3518
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Divider2.default, { sx: { my: 1 } }),
|
|
3519
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: 1 }, children: "Members" }),
|
|
3520
|
-
memberError && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
|
|
3521
|
-
members.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "No members yet." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableContainer2.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Table2.default, { size: "small", sx: dataTableSx, children: [
|
|
3522
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableHead2.default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_TableRow2.default, { children: [
|
|
3523
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { children: "User" }),
|
|
3524
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: "Role" }),
|
|
3525
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", sx: { width: 40 } })
|
|
3526
|
-
] }) }),
|
|
3527
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableBody2.default, { children: members.map((m) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_TableRow2.default, { children: [
|
|
3528
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { children: m.user_email ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
3529
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", children: m.user_email }),
|
|
3488
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
3489
|
+
import_Box10.default,
|
|
3490
|
+
{
|
|
3491
|
+
sx: {
|
|
3492
|
+
display: "grid",
|
|
3493
|
+
gridTemplateColumns: {
|
|
3494
|
+
xs: "1fr",
|
|
3495
|
+
md: wide ? "minmax(0, 1fr) minmax(0, 1fr)" : "1fr"
|
|
3496
|
+
},
|
|
3497
|
+
gap: 3,
|
|
3498
|
+
alignItems: "start"
|
|
3499
|
+
},
|
|
3500
|
+
children: [
|
|
3501
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { sx: { display: "flex", flexDirection: "column", gap: 2 }, children: [
|
|
3502
|
+
wide && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", children: "Settings" }),
|
|
3530
3503
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3531
|
-
|
|
3504
|
+
import_TextField3.default,
|
|
3532
3505
|
{
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3506
|
+
label: "Team Alias",
|
|
3507
|
+
value: alias,
|
|
3508
|
+
onChange: (e) => setAlias(e.target.value),
|
|
3509
|
+
disabled: submitting,
|
|
3510
|
+
fullWidth: true
|
|
3537
3511
|
}
|
|
3538
|
-
)
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
{
|
|
3542
|
-
variant: "body2",
|
|
3543
|
-
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
|
|
3544
|
-
children: m.user_id
|
|
3545
|
-
}
|
|
3546
|
-
) }),
|
|
3547
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3548
|
-
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: (() => {
|
|
3549
|
-
const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
|
|
3550
|
-
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Tooltip4.default, { title: reason ?? "", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3551
|
-
import_IconButton4.default,
|
|
3512
|
+
),
|
|
3513
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3514
|
+
import_Autocomplete2.default,
|
|
3552
3515
|
{
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
{
|
|
3568
|
-
freeSolo: true,
|
|
3569
|
-
autoHighlight: true,
|
|
3570
|
-
options: userOptions,
|
|
3571
|
-
loading: usersLoading,
|
|
3572
|
-
inputValue: memberQuery,
|
|
3573
|
-
onInputChange: (_, newInputValue, reason) => {
|
|
3574
|
-
setMemberQuery(newInputValue);
|
|
3575
|
-
if (reason === "input" || reason === "clear") {
|
|
3576
|
-
setMemberRef(newInputValue);
|
|
3577
|
-
handleUserSearch(newInputValue);
|
|
3516
|
+
multiple: true,
|
|
3517
|
+
options: modelNames,
|
|
3518
|
+
value: models,
|
|
3519
|
+
onChange: (_, newValue) => setModels(newValue),
|
|
3520
|
+
disabled: submitting,
|
|
3521
|
+
renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3522
|
+
import_TextField3.default,
|
|
3523
|
+
{
|
|
3524
|
+
...params,
|
|
3525
|
+
label: "Models",
|
|
3526
|
+
required: true,
|
|
3527
|
+
helperText: "Only models your admin allow-listed for teams. At least one is required."
|
|
3528
|
+
}
|
|
3529
|
+
)
|
|
3578
3530
|
}
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3531
|
+
),
|
|
3532
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(
|
|
3533
|
+
import_Box10.default,
|
|
3534
|
+
{
|
|
3535
|
+
sx: {
|
|
3536
|
+
display: "grid",
|
|
3537
|
+
gridTemplateColumns: { xs: "1fr", sm: "1fr 1fr" },
|
|
3538
|
+
gap: 2
|
|
3539
|
+
},
|
|
3540
|
+
children: [
|
|
3541
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3542
|
+
import_TextField3.default,
|
|
3543
|
+
{
|
|
3544
|
+
label: budgetWriteOnly ? "New Max Budget ($)" : "Max Budget ($)",
|
|
3545
|
+
type: "number",
|
|
3546
|
+
value: maxBudget,
|
|
3547
|
+
onChange: (e) => setMaxBudget(e.target.value),
|
|
3548
|
+
disabled: submitting || unlimited,
|
|
3549
|
+
inputProps: { min: 0, max: maxBudgetCeiling },
|
|
3550
|
+
helperText: budgetWriteOnly ? "Current budget is hidden \u2014 leave blank to keep it, or enter a new value" : `Maximum: $${maxBudgetCeiling}`,
|
|
3551
|
+
fullWidth: true
|
|
3552
|
+
}
|
|
3553
|
+
),
|
|
3554
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3555
|
+
import_TextField3.default,
|
|
3556
|
+
{
|
|
3557
|
+
select: true,
|
|
3558
|
+
label: "Budget Duration",
|
|
3559
|
+
value: budgetDuration,
|
|
3560
|
+
onChange: (e) => setBudgetDuration(e.target.value),
|
|
3561
|
+
disabled: submitting || unlimited,
|
|
3562
|
+
helperText: "Spend-reset period for the team budget",
|
|
3563
|
+
fullWidth: true,
|
|
3564
|
+
children: durationOptions.map((o) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_MenuItem2.default, { value: o.value, children: o.label }, o.value))
|
|
3565
|
+
}
|
|
3566
|
+
)
|
|
3567
|
+
]
|
|
3584
3568
|
}
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
disabled: memberBusy,
|
|
3589
|
-
sx: { flex: 1, minWidth: 260 },
|
|
3590
|
-
renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3591
|
-
import_TextField3.default,
|
|
3569
|
+
),
|
|
3570
|
+
allowUnlimitedBudget && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3571
|
+
import_FormControlLabel2.default,
|
|
3592
3572
|
{
|
|
3593
|
-
|
|
3594
|
-
label: "
|
|
3595
|
-
placeholder: "Search by name or email\u2026",
|
|
3596
|
-
size: "small",
|
|
3597
|
-
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
3598
|
-
InputProps: {
|
|
3599
|
-
...params.InputProps,
|
|
3600
|
-
endAdornment: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
3601
|
-
usersLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null,
|
|
3602
|
-
params.InputProps.endAdornment
|
|
3603
|
-
] })
|
|
3604
|
-
}
|
|
3573
|
+
control: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Checkbox2.default, { checked: unlimited, onChange: (e) => setUnlimited(e.target.checked), disabled: submitting }),
|
|
3574
|
+
label: "Unlimited Budget"
|
|
3605
3575
|
}
|
|
3606
3576
|
)
|
|
3607
|
-
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3577
|
+
] }),
|
|
3578
|
+
showMembers && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3579
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: creating ? 0.25 : 1 }, children: creating ? "Initial members" : "Members" }),
|
|
3580
|
+
creating && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: "Added as team users right after the team is created." }),
|
|
3581
|
+
memberError && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Alert2.default, { severity: "error", sx: { mb: 1 }, onClose: () => setMemberError(null), children: memberError }),
|
|
3582
|
+
members.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", color: "text.secondary", sx: { mb: 1 }, children: creating ? "No members picked yet \u2014 you can also add them later." : "No members yet." }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableContainer2.default, { component: import_Paper4.default, variant: "outlined", sx: { borderRadius: 1.5, mb: 1.5 }, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Table2.default, { size: "small", sx: dataTableSx, children: [
|
|
3583
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableHead2.default, { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_TableRow2.default, { children: [
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { children: "User" }),
|
|
3585
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: "Role" }),
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", sx: { width: 40 } })
|
|
3587
|
+
] }) }),
|
|
3588
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableBody2.default, { children: members.map((m) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_TableRow2.default, { children: [
|
|
3589
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { children: m.user_email ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "body2", children: m.user_email }),
|
|
3591
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3592
|
+
import_Typography8.default,
|
|
3593
|
+
{
|
|
3594
|
+
variant: "caption",
|
|
3595
|
+
color: "text.secondary",
|
|
3596
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace", fontSize: 11 },
|
|
3597
|
+
children: m.user_id
|
|
3598
|
+
}
|
|
3599
|
+
)
|
|
3600
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3601
|
+
import_Typography8.default,
|
|
3602
|
+
{
|
|
3603
|
+
variant: "body2",
|
|
3604
|
+
sx: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" },
|
|
3605
|
+
children: m.user_id
|
|
3606
|
+
}
|
|
3607
|
+
) }),
|
|
3608
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(StatusPill, { label: m.role, tone: m.role === "admin" ? "accent" : "neutral", dot: false }) }),
|
|
3609
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_TableCell2.default, { align: "right", children: (() => {
|
|
3610
|
+
const { disabled, reason } = isRemovalDisabled(m.user_id, m.role);
|
|
3611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Tooltip4.default, { title: reason ?? "", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3612
|
+
import_IconButton4.default,
|
|
3613
|
+
{
|
|
3614
|
+
edge: "end",
|
|
3615
|
+
size: "small",
|
|
3616
|
+
"aria-label": `remove ${m.user_id}`,
|
|
3617
|
+
disabled: memberBusy || disabled,
|
|
3618
|
+
onClick: () => handleRemoveMember(m.user_id),
|
|
3619
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_icons_material5.Delete, { fontSize: "small" })
|
|
3620
|
+
}
|
|
3621
|
+
) }) });
|
|
3622
|
+
})() })
|
|
3623
|
+
] }, m.user_id)) })
|
|
3624
|
+
] }) }),
|
|
3625
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { sx: { display: "flex", gap: 1, alignItems: "flex-start", flexWrap: "wrap" }, children: [
|
|
3626
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3627
|
+
import_Autocomplete2.default,
|
|
3628
|
+
{
|
|
3629
|
+
freeSolo: true,
|
|
3630
|
+
autoHighlight: true,
|
|
3631
|
+
options: userOptions,
|
|
3632
|
+
loading: usersLoading,
|
|
3633
|
+
inputValue: memberQuery,
|
|
3634
|
+
onInputChange: (_, newInputValue, reason) => {
|
|
3635
|
+
setMemberQuery(newInputValue);
|
|
3636
|
+
if (reason === "input" || reason === "clear") {
|
|
3637
|
+
setMemberRef(newInputValue);
|
|
3638
|
+
handleUserSearch(newInputValue);
|
|
3639
|
+
}
|
|
3640
|
+
},
|
|
3641
|
+
onChange: (_, newValue) => {
|
|
3642
|
+
if (newValue && typeof newValue !== "string") {
|
|
3643
|
+
setMemberQuery(newValue.label);
|
|
3644
|
+
setMemberRef(newValue.ref);
|
|
3645
|
+
}
|
|
3646
|
+
},
|
|
3647
|
+
getOptionLabel: (o) => typeof o === "string" ? o : o.label,
|
|
3648
|
+
isOptionEqualToValue: (o, v) => o.ref === (typeof v === "string" ? v : v.ref),
|
|
3649
|
+
disabled: memberBusy,
|
|
3650
|
+
sx: { flex: "1 1 240px" },
|
|
3651
|
+
renderInput: (params) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3652
|
+
import_TextField3.default,
|
|
3653
|
+
{
|
|
3654
|
+
...params,
|
|
3655
|
+
label: "Add member",
|
|
3656
|
+
placeholder: "Search by name or email\u2026",
|
|
3657
|
+
size: "small",
|
|
3658
|
+
helperText: "Pick a catalog user, or paste a user entity ref",
|
|
3659
|
+
InputProps: {
|
|
3660
|
+
...params.InputProps,
|
|
3661
|
+
endAdornment: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_jsx_runtime11.Fragment, { children: [
|
|
3662
|
+
usersLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_CircularProgress3.default, { color: "inherit", size: 14 }) : null,
|
|
3663
|
+
params.InputProps.endAdornment
|
|
3664
|
+
] })
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
3667
|
+
)
|
|
3668
|
+
}
|
|
3669
|
+
),
|
|
3670
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3671
|
+
import_TextField3.default,
|
|
3672
|
+
{
|
|
3673
|
+
label: "Max budget in team ($)",
|
|
3674
|
+
type: "number",
|
|
3675
|
+
value: memberBudget,
|
|
3676
|
+
onChange: (e) => setMemberBudget(e.target.value),
|
|
3677
|
+
disabled: memberBusy,
|
|
3678
|
+
size: "small",
|
|
3679
|
+
sx: { width: 150 }
|
|
3680
|
+
}
|
|
3681
|
+
),
|
|
3682
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
3683
|
+
import_Button4.default,
|
|
3684
|
+
{
|
|
3685
|
+
onClick: handleAddMember,
|
|
3686
|
+
disabled: memberBusy || !memberRef.trim(),
|
|
3687
|
+
variant: "outlined",
|
|
3688
|
+
sx: { mt: 0.5 },
|
|
3689
|
+
children: "Add"
|
|
3690
|
+
}
|
|
3691
|
+
)
|
|
3692
|
+
] })
|
|
3693
|
+
] })
|
|
3694
|
+
]
|
|
3695
|
+
}
|
|
3696
|
+
),
|
|
3633
3697
|
!membersOnly && showKnowledgeBases && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_Box10.default, { children: [
|
|
3634
3698
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Divider2.default, { sx: { my: 1 } }),
|
|
3635
3699
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Typography8.default, { variant: "subtitle2", sx: { mb: 1 }, children: "Knowledge Bases" }),
|
|
@@ -3687,7 +3751,7 @@ var init_ManageTeamDialog = __esm({
|
|
|
3687
3751
|
] }),
|
|
3688
3752
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_DialogActions3.default, { children: [
|
|
3689
3753
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: onClose, disabled: submitting, children: membersOnly ? "Done" : "Cancel" }),
|
|
3690
|
-
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." :
|
|
3754
|
+
!membersOnly && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_Button4.default, { onClick: handleSubmit, disabled: submitting, variant: "contained", children: submitting ? "Saving..." : submitLabel })
|
|
3691
3755
|
] })
|
|
3692
3756
|
] });
|
|
3693
3757
|
};
|
|
@@ -6505,7 +6569,7 @@ var init_LiteLLMPage = __esm({
|
|
|
6505
6569
|
membersOnly: manageTeam?.mode === "members",
|
|
6506
6570
|
currentUserId: userInfo?.user_id,
|
|
6507
6571
|
memberManagerRoles: liteLlmConfig?.teamManagement?.memberManagerRoles,
|
|
6508
|
-
onSubmit: async (payload) => {
|
|
6572
|
+
onSubmit: async (payload, initialMembers) => {
|
|
6509
6573
|
try {
|
|
6510
6574
|
if (manageTeam?.mode === "edit" && manageTeam.team) {
|
|
6511
6575
|
const loadedAt = manageTeam.team.metadata?.updated_at_iso;
|
|
@@ -6515,7 +6579,23 @@ var init_LiteLLMPage = __esm({
|
|
|
6515
6579
|
...typeof loadedAt === "string" ? { expectedUpdatedAtIso: loadedAt } : {}
|
|
6516
6580
|
});
|
|
6517
6581
|
} else {
|
|
6518
|
-
await api.createTeam(payload);
|
|
6582
|
+
const created = await api.createTeam(payload);
|
|
6583
|
+
const failed = [];
|
|
6584
|
+
for (const m of initialMembers ?? []) {
|
|
6585
|
+
try {
|
|
6586
|
+
await api.addTeamMember(created.team_id, m);
|
|
6587
|
+
} catch (e) {
|
|
6588
|
+
failed.push(`${m.userEntityRef} (${e.message})`);
|
|
6589
|
+
}
|
|
6590
|
+
}
|
|
6591
|
+
if (failed.length) {
|
|
6592
|
+
alertApi.post({
|
|
6593
|
+
message: `Team created, but some members could not be added: ${failed.join(", ")}`,
|
|
6594
|
+
severity: "warning"
|
|
6595
|
+
});
|
|
6596
|
+
refreshTeams();
|
|
6597
|
+
return;
|
|
6598
|
+
}
|
|
6519
6599
|
}
|
|
6520
6600
|
const alert = toastFor("teamSaveSuccess");
|
|
6521
6601
|
if (alert) alertApi.post(alert);
|