Skip to content

Commit aec63d5

Browse files
committed
feat: org/account switching, preference cascading clears, table column reordering
- Add Switch Organization and Switch Account palette commands - Add cascading clear methods to preferences (org clears account+workspace, etc.) - Extract preference store keys to constants - Reorder policy table columns: Category, Pending, Risk, Benefit, Waste, Impact - Reorder catalog table columns: Service before Status - Sort disabled/inactive services after ready in catalog table
1 parent f14835b commit aec63d5

11 files changed

Lines changed: 206 additions & 81 deletions

File tree

‎internal/api/gen/generated.go‎

Lines changed: 7 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/api/gen/schema.graphql‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,12 @@ input CreateDatadogAccountInput {
631631
($0.10/GB). Set to override with actual contract rate.
632632
"""
633633
costPerGBIngested: Float
634+
635+
"""
636+
Fraction of the API rate limit to consume (0.0-1.0). NULL = default (0.8 =
637+
80%). Leaves headroom for the customer's own API usage.
638+
"""
639+
rateLimitUtilization: Float
634640
accountID: ID!
635641

636642
"""
@@ -831,6 +837,12 @@ type DatadogAccount implements Node {
831837
"""
832838
costPerGBIngested: Float
833839

840+
"""
841+
Fraction of the API rate limit to consume (0.0-1.0). NULL = default (0.8 =
842+
80%). Leaves headroom for the customer's own API usage.
843+
"""
844+
rateLimitUtilization: Float
845+
834846
"""Account this Datadog configuration belongs to"""
835847
account: Account!
836848

@@ -1150,6 +1162,18 @@ input DatadogAccountWhereInput {
11501162
costPerGBIngestedIsNil: Boolean
11511163
costPerGBIngestedNotNil: Boolean
11521164

1165+
"""rate_limit_utilization field predicates"""
1166+
rateLimitUtilization: Float
1167+
rateLimitUtilizationNEQ: Float
1168+
rateLimitUtilizationIn: [Float!]
1169+
rateLimitUtilizationNotIn: [Float!]
1170+
rateLimitUtilizationGT: Float
1171+
rateLimitUtilizationGTE: Float
1172+
rateLimitUtilizationLT: Float
1173+
rateLimitUtilizationLTE: Float
1174+
rateLimitUtilizationIsNil: Boolean
1175+
rateLimitUtilizationNotNil: Boolean
1176+
11531177
"""account edge predicates"""
11541178
hasAccount: Boolean
11551179
hasAccountWith: [AccountWhereInput!]
@@ -3723,6 +3747,13 @@ input UpdateDatadogAccountInput {
37233747
"""
37243748
costPerGBIngested: Float
37253749
clearCostPerGBIngested: Boolean
3750+
3751+
"""
3752+
Fraction of the API rate limit to consume (0.0-1.0). NULL = default (0.8 =
3753+
80%). Leaves headroom for the customer's own API usage.
3754+
"""
3755+
rateLimitUtilization: Float
3756+
clearRateLimitUtilization: Boolean
37263757
accountID: ID
37273758
}
37283759

‎internal/app/app.go‎

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ type Model struct {
7070
authService auth.Auth
7171
syncer powersync.Syncer
7272
services api.APIServices
73+
prefs preferences.Preferences
7374

7475
// Runtime (created after account selection / onboarding)
7576
db sqlite.DB
@@ -142,6 +143,7 @@ func New(
142143
authService: authService,
143144
syncer: syncer,
144145
services: services,
146+
prefs: prefs,
145147
statusBar: statusbar.New(theme, syncer, cfg.APIEndpoint),
146148
toast: toast.New(theme),
147149
keyBar: keybar.New(theme, scope),
@@ -688,3 +690,41 @@ func (m *Model) shutdown() {
688690
m.db.Close()
689691
}
690692
}
693+
694+
// switchOrganization clears org preference (cascades to account and workspace)
695+
// and re-enters onboarding. Onboarding will prompt for all choices.
696+
func (m *Model) switchOrganization() tea.Cmd {
697+
m.scope.Info("switching organization")
698+
_ = m.prefs.ClearDefaultOrgID()
699+
return m.restartOnboarding()
700+
}
701+
702+
// switchAccount clears account preference (cascades to workspace) and
703+
// re-enters onboarding. The saved org auto-selects, then prompts for account.
704+
func (m *Model) switchAccount() tea.Cmd {
705+
m.scope.Info("switching account")
706+
_ = m.prefs.ClearDefaultAccountID()
707+
return m.restartOnboarding()
708+
}
709+
710+
// restartOnboarding tears down the current session and re-enters onboarding
711+
// at the org selection step. Onboarding auto-selects any saved preferences
712+
// and prompts for anything that was cleared.
713+
func (m *Model) restartOnboarding() tea.Cmd {
714+
m.shutdown()
715+
716+
m.db = nil
717+
m.uploader = nil
718+
m.chatClient = nil
719+
m.toolRegistry = nil
720+
m.chat = nil
721+
722+
m.statusBar = statusbar.New(m.theme, m.syncer, m.cfg.APIEndpoint)
723+
m.windowTitle = ""
724+
725+
m.onboarding = onboarding.New(m.ctx, m.theme, m.services, m.prefs, m.authService, m.syncer, m.scope)
726+
m.state = stateOnboarding
727+
m.updateLayout()
728+
729+
return m.onboarding.StartFromOrgSelect()
730+
}

‎internal/app/commands.go‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ func (m *Model) paletteCommands() []palette.Command {
1919
return m.chat.Init()
2020
},
2121
},
22+
{
23+
Name: "Switch Organization",
24+
Handler: func() tea.Cmd {
25+
return m.switchOrganization()
26+
},
27+
},
28+
{
29+
Name: "Switch Account",
30+
Handler: func() tea.Cmd {
31+
return m.switchAccount()
32+
},
33+
},
2234
{
2335
Name: "Quit",
2436
Handler: func() tea.Cmd {

‎internal/app/onboarding/onboarding.go‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ func (m *Model) Init() tea.Cmd {
9393
return m.setStep(auth.NewCheck(m.ctx, m.theme, m.auth, m.scope))
9494
}
9595

96+
// StartFromOrgSelect starts the onboarding flow at the organization selection
97+
// step, skipping auth check and role selection. Used when switching orgs or
98+
// accounts — the caller clears the relevant preferences so onboarding prompts
99+
// for selection instead of auto-selecting.
100+
func (m *Model) StartFromOrgSelect() tea.Cmd {
101+
m.scope.Info("onboarding started from org select")
102+
return m.setStep(organizations.NewSelect(m.ctx, m.theme, m.services, m.prefs, m.auth, m.scope))
103+
}
104+
96105
// Update handles messages and orchestrates step transitions.
97106
func (m *Model) Update(msg tea.Msg) tea.Cmd {
98107
switch msg := msg.(type) {

‎internal/app/statusbar/catalogstatus/catalogstatus.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,13 +176,13 @@ func (m *Model) renderServiceTable(width int) string {
176176
}
177177

178178
tbl := table.New(m.theme, table.WithMaxValueWidth(30))
179-
tbl.Headers("Status", "Service", "Events", "Volume", "Bytes", "Cost")
179+
tbl.Headers("Service", "Status", "Events", "Volume", "Bytes", "Cost")
180180
tbl.SetWidth(width)
181181

182182
for _, svc := range m.services {
183183
row := []string{
184-
status.Service(m.theme, svc.Status, true),
185184
m.serviceName(svc),
185+
status.Service(m.theme, svc.Status, true),
186186
fmt.Sprintf("%d", svc.EventCount),
187187
formatVolume(svc.VolumePerHour) + "/hr",
188188
formatBytes(svc.BytesPerHour) + "/hr",

‎internal/app/statusbar/policystatus/policystatus.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,17 @@ func (m *Model) renderCategoryTable(width int) string {
218218
}
219219

220220
tbl := table.New(m.theme, table.WithMaxValueWidth(30))
221-
tbl.Headers("Category", "Pending", "Waste", "Impact", "Risk", "Benefit")
221+
tbl.Headers("Category", "Pending", "Risk", "Benefit", "Waste", "Impact")
222222
tbl.SetWidth(width)
223223

224224
for _, c := range m.categories {
225225
tbl.Row(
226226
c.Category,
227227
fmt.Sprintf("%d", c.PendingCount),
228-
m.formatWaste(c),
229-
formatCategoryImpact(c),
230228
m.renderRisk(c.RiskLevel),
231229
formatBenefitLabel(c.Benefit),
230+
m.formatWaste(c),
231+
formatCategoryImpact(c),
232232
)
233233
}
234234

0 commit comments

Comments
 (0)