mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add inline editing of usage limit overrides (#23380)
## Summary Adds inline editing of existing per-user and per-group chat usage limit overrides from the Limits tab. Admins can now click Edit on any override row to modify the spend limit in-place, using the same form used for adding overrides. ## Changes **Backend** (`coderd/chats_test.go`) - Added `UpdateUserOverride` and `UpdateGroupOverride` test cases covering the upsert-in-place behavior. **Frontend** (3 component files + 2 story files) - `LimitsTab.tsx`: Edit state management, mutual-exclusion between user/group edit modes, and handlers that prefill the form from the existing override. - `GroupLimitsSection.tsx`: Edit button per row, read-only group identity in edit mode, Save/Cancel actions, disable states during pending operations. - `UserOverridesSection.tsx`: Same pattern as groups — Edit button, read-only user identity, Save/Cancel, proper disable states. - New Storybook stories for both sections (Default, EmptyState, AddForm, EditForm). ## UX behavior - Clicking Edit opens the inline form with the current spend limit prefilled and the entity shown as read-only. - Save uses the existing PUT upsert endpoint (no new API surface). - Cancel returns to normal list view with form state cleared. - Edit modes are mutually exclusive — editing a user override closes any open group form and vice versa. - All buttons and inputs disable during pending mutations. - Add and delete continue to work after editing.
This commit is contained in:
@@ -3716,6 +3716,35 @@ func TestChatUsageLimitOverrideRoutes(t *testing.T) {
|
||||
require.Equal(t, "Chat usage limit override not found.", sdkErr.Message)
|
||||
})
|
||||
|
||||
t.Run("UpdateUserOverride", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, _ := newChatClientWithDatabase(t)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client)
|
||||
_, member := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
|
||||
|
||||
_, err := client.UpsertChatUsageLimitOverride(ctx, member.ID, codersdk.UpsertChatUsageLimitOverrideRequest{
|
||||
SpendLimitMicros: 5_000_000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
override, err := client.UpsertChatUsageLimitOverride(ctx, member.ID, codersdk.UpsertChatUsageLimitOverrideRequest{
|
||||
SpendLimitMicros: 10_000_000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, member.ID, override.UserID)
|
||||
require.NotNil(t, override.SpendLimitMicros)
|
||||
require.EqualValues(t, 10_000_000, *override.SpendLimitMicros)
|
||||
|
||||
config, err := client.GetChatUsageLimitConfig(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.Overrides, 1)
|
||||
require.Equal(t, member.ID, config.Overrides[0].UserID)
|
||||
require.NotNil(t, config.Overrides[0].SpendLimitMicros)
|
||||
require.EqualValues(t, 10_000_000, *config.Overrides[0].SpendLimitMicros)
|
||||
})
|
||||
|
||||
t.Run("UpsertGroupOverrideIncludesMemberCount", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -3750,6 +3779,40 @@ func TestChatUsageLimitOverrideRoutes(t *testing.T) {
|
||||
require.EqualValues(t, 1, listed.MemberCount)
|
||||
})
|
||||
|
||||
t.Run("UpdateGroupOverride", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := testutil.Context(t, testutil.WaitLong)
|
||||
client, db := newChatClientWithDatabase(t)
|
||||
firstUser := coderdtest.CreateFirstUser(t, client)
|
||||
_, member := coderdtest.CreateAnotherUser(t, client, firstUser.OrganizationID)
|
||||
group := dbgen.Group(t, db, database.Group{OrganizationID: firstUser.OrganizationID})
|
||||
dbgen.GroupMember(t, db, database.GroupMemberTable{GroupID: group.ID, UserID: firstUser.UserID})
|
||||
dbgen.GroupMember(t, db, database.GroupMemberTable{GroupID: group.ID, UserID: member.ID})
|
||||
|
||||
_, err := client.UpsertChatUsageLimitGroupOverride(ctx, group.ID, codersdk.UpsertChatUsageLimitGroupOverrideRequest{
|
||||
SpendLimitMicros: 5_000_000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
override, err := client.UpsertChatUsageLimitGroupOverride(ctx, group.ID, codersdk.UpsertChatUsageLimitGroupOverrideRequest{
|
||||
SpendLimitMicros: 10_000_000,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, group.ID, override.GroupID)
|
||||
require.EqualValues(t, 2, override.MemberCount)
|
||||
require.NotNil(t, override.SpendLimitMicros)
|
||||
require.EqualValues(t, 10_000_000, *override.SpendLimitMicros)
|
||||
|
||||
config, err := client.GetChatUsageLimitConfig(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, config.GroupOverrides, 1)
|
||||
require.Equal(t, group.ID, config.GroupOverrides[0].GroupID)
|
||||
require.EqualValues(t, 2, config.GroupOverrides[0].MemberCount)
|
||||
require.NotNil(t, config.GroupOverrides[0].SpendLimitMicros)
|
||||
require.EqualValues(t, 10_000_000, *config.GroupOverrides[0].SpendLimitMicros)
|
||||
})
|
||||
|
||||
t.Run("UpsertGroupOverrideMissingGroup", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user