From a2a7cb431d323caac611ca00291b5dfe59d3a612 Mon Sep 17 00:00:00 2001 From: Joe Julian Date: Wed, 1 Apr 2026 16:34:24 -0700 Subject: [PATCH] Refine group, editor, and admin workflows --- main.go | 20 ++++++++++-- ui_api.go | 32 +++++++++++++++--- ui_forms.go | 94 ++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 111 insertions(+), 35 deletions(-) diff --git a/main.go b/main.go index 2239e73..1c28fc9 100644 --- a/main.go +++ b/main.go @@ -3431,6 +3431,10 @@ func (u *ui) groupBar(gtx layout.Context) layout.Dimensions { func detailLine(th *material.Theme, label, value string) layout.Widget { return func(gtx layout.Context) layout.Dimensions { + valueSize := unit.Sp(16) + if gtx.Constraints.Max.X <= gtx.Dp(unit.Dp(460)) { + valueSize = unit.Sp(15) + } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { lbl := material.Label(th, unit.Sp(12), strings.ToUpper(label)) @@ -3438,7 +3442,7 @@ func detailLine(th *material.Theme, label, value string) layout.Widget { return lbl.Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(th, unit.Sp(16), value) + lbl := material.Label(th, valueSize, value) return lbl.Layout(gtx) }), ) @@ -3447,6 +3451,10 @@ func detailLine(th *material.Theme, label, value string) layout.Widget { func (u *ui) passwordLine(label, value string) layout.Widget { return func(gtx layout.Context) layout.Dimensions { + valueSize := unit.Sp(16) + if gtx.Constraints.Max.X <= gtx.Dp(unit.Dp(460)) { + valueSize = unit.Sp(15) + } return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { lbl := material.Label(u.theme, unit.Sp(12), strings.ToUpper(label)) @@ -3456,7 +3464,7 @@ func (u *ui) passwordLine(label, value string) layout.Widget { layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Alignment: layout.Middle}.Layout(gtx, layout.Flexed(1, func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(16), value) + lbl := material.Label(u.theme, valueSize, value) return lbl.Layout(gtx) }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { @@ -3554,6 +3562,10 @@ func tonedButton(gtx layout.Context, th *material.Theme, click *widget.Clickable btn.Background, btn.Color = buttonFocusColors(false) btn.CornerRadius = unit.Dp(10) btn.TextSize = unit.Sp(15) + if gtx.Constraints.Max.X <= gtx.Dp(unit.Dp(460)) { + btn.TextSize = unit.Sp(14) + btn.Inset = layout.Inset{Top: 7, Bottom: 7, Left: 10, Right: 10} + } return btn.Layout(gtx) } @@ -3562,6 +3574,10 @@ func sectionTabButton(gtx layout.Context, th *material.Theme, click *widget.Clic btn.CornerRadius = unit.Dp(10) btn.TextSize = unit.Sp(11) btn.Inset = layout.Inset{Top: 5, Bottom: 5, Left: 9, Right: 9} + if gtx.Constraints.Max.X <= gtx.Dp(unit.Dp(460)) { + btn.TextSize = unit.Sp(10) + btn.Inset = layout.Inset{Top: 4, Bottom: 4, Left: 8, Right: 8} + } if active { btn.Background = accentColor btn.Color = color.NRGBA{R: 255, G: 252, B: 247, A: 255} diff --git a/ui_api.go b/ui_api.go index ef62230..c124362 100644 --- a/ui_api.go +++ b/ui_api.go @@ -494,6 +494,12 @@ func (u *ui) apiTokenDetailPanel(gtx layout.Context) layout.Dimensions { return lbl.Layout(gtx) }, layout.Spacer{Height: unit.Dp(8)}.Layout, + func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(12), "Identity") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }, + layout.Spacer{Height: unit.Dp(4)}.Layout, labeledEditor(u.theme, "Name", &u.apiTokenName, false), layout.Spacer{Height: unit.Dp(6)}.Layout, labeledEditor(u.theme, "Client Name", &u.apiTokenClientName, false), @@ -546,6 +552,12 @@ func (u *ui) apiTokenDetailPanel(gtx layout.Context) layout.Dimensions { } rows = append(rows, layout.Spacer{Height: unit.Dp(10)}.Layout, + func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(12), "Token actions") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }, + layout.Spacer{Height: unit.Dp(6)}.Layout, func(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { @@ -632,6 +644,12 @@ func (u *ui) apiTokenDetailPanel(gtx layout.Context) layout.Dimensions { } rows = append(rows, layout.Spacer{Height: unit.Dp(10)}.Layout, + func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(12), "New rule") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }, + layout.Spacer{Height: unit.Dp(6)}.Layout, func(gtx layout.Context) layout.Dimensions { return material.CheckBox(u.theme, &u.apiPolicyAllow, "Allow rule (unchecked means deny rule)").Layout(gtx) }, @@ -678,16 +696,22 @@ func (u *ui) apiAuditDetailPanel(gtx layout.Context) layout.Dimensions { event := events[u.selectedAuditIndex] rows = append(rows, layout.Spacer{Height: unit.Dp(12)}.Layout, - detailLine(u.theme, "Type", string(event.Type)), - layout.Spacer{Height: unit.Dp(6)}.Layout, + func(gtx layout.Context) layout.Dimensions { + return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(detailLine(u.theme, "Type", string(event.Type))), + layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), + layout.Rigid(detailLine(u.theme, "Operation", string(event.Operation))), + ) + }) + }, + layout.Spacer{Height: unit.Dp(8)}.Layout, detailLine(u.theme, "When", event.At.Local().Format(time.RFC3339)), layout.Spacer{Height: unit.Dp(6)}.Layout, detailLine(u.theme, "Token", event.TokenName), layout.Spacer{Height: unit.Dp(6)}.Layout, detailLine(u.theme, "Client", event.ClientName), layout.Spacer{Height: unit.Dp(6)}.Layout, - detailLine(u.theme, "Operation", string(event.Operation)), - layout.Spacer{Height: unit.Dp(6)}.Layout, detailLine(u.theme, "Resource", formatAuditResource(event.Resource)), layout.Spacer{Height: unit.Dp(6)}.Layout, detailLine(u.theme, "Message", event.Message), diff --git a/ui_forms.go b/ui_forms.go index 39d9e03..7a02820 100644 --- a/ui_forms.go +++ b/ui_forms.go @@ -329,7 +329,25 @@ func (u *ui) attachmentList(gtx layout.Context) layout.Dimensions { for u.attachmentClicks[index].Clicked(gtx) { u.attachmentName.SetText(itemName) } - return tonedButton(gtx, u.theme, &u.attachmentClicks[index], label) + return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { + return u.attachmentClicks[index].Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.UniformInset(unit.Dp(8)).Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(13), itemName) + lbl.Color = accentColor + return lbl.Layout(gtx) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(2)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(11), label) + lbl.Color = mutedColor + return lbl.Layout(gtx) + }), + ) + }) + }) + }) })) if i < len(items)-1 { children = append(children, layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout)) @@ -405,40 +423,50 @@ func (u *ui) groupControls(gtx layout.Context) layout.Dimensions { } deletable, deleteReason := u.currentGroupDeletionState() return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(12), "CREATE") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout), layout.Rigid(labeledEditor(u.theme, "Create Group / Subgroup", &u.groupName, false)), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, + return tonedButton(gtx, u.theme, &u.createGroup, "Create Group") + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(12)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + if len(u.displayPath()) == 0 { + return layout.Dimensions{} + } + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return tonedButton(gtx, u.theme, &u.createGroup, "Create") + lbl := material.Label(u.theme, unit.Sp(12), "MANAGE CURRENT GROUP") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(12), strings.Join(u.displayPath(), " / ")) + lbl.Color = accentColor + return lbl.Layout(gtx) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return tonedButton(gtx, u.theme, &u.renameGroup, "Rename Current Group") + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return tonedButton(gtx, u.theme, &u.moveGroup, "Move Current Group") }), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - if len(u.displayPath()) == 0 { + if !deletable || u.deleteGroupPendingConfirmation() { return layout.Dimensions{} } - return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, - layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout), + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return tonedButton(gtx, u.theme, &u.renameGroup, "Rename Current Group") - }), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, - layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return tonedButton(gtx, u.theme, &u.moveGroup, "Move Current Group") - }), - ) - }), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - if !deletable || u.deleteGroupPendingConfirmation() { - return layout.Dimensions{} - } - return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, - layout.Rigid(layout.Spacer{Width: unit.Dp(6)}.Layout), - layout.Rigid(func(gtx layout.Context) layout.Dimensions { - return tonedButton(gtx, u.theme, &u.deleteGroup, "Delete Empty Group") - }), - ) + return tonedButton(gtx, u.theme, &u.deleteGroup, "Delete Empty Group") }), ) }), @@ -583,6 +611,14 @@ func (u *ui) entryEditorPanel(gtx layout.Context) layout.Dimensions { layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), layout.Rigid(labeledEditorWithFocus(u.theme, "History Index", &u.historyIndex, false, u.isFocused(detailFocusID(detailFieldHistoryIndex)))), layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), + layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return compactCard(gtx, func(gtx layout.Context) layout.Dimensions { + lbl := material.Label(u.theme, unit.Sp(11), "Generate Password only updates the form. Nothing is persisted until you save.") + lbl.Color = mutedColor + return lbl.Layout(gtx) + }) + }), + layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { @@ -601,13 +637,13 @@ func (u *ui) entryEditorPanel(gtx layout.Context) layout.Dimensions { }), ) }), - layout.Rigid(layout.Spacer{Height: unit.Dp(4)}.Layout), + layout.Rigid(layout.Spacer{Height: unit.Dp(10)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { - lbl := material.Label(u.theme, unit.Sp(11), "Generate Password only updates the form. Nothing is persisted until you save.") + lbl := material.Label(u.theme, unit.Sp(12), "COPY") lbl.Color = mutedColor return lbl.Layout(gtx) }), - layout.Rigid(layout.Spacer{Height: unit.Dp(8)}.Layout), + layout.Rigid(layout.Spacer{Height: unit.Dp(6)}.Layout), layout.Rigid(func(gtx layout.Context) layout.Dimensions { return layout.Flex{Spacing: layout.SpaceStart}.Layout(gtx, layout.Rigid(func(gtx layout.Context) layout.Dimensions { return tonedButton(gtx, u.theme, &u.copyUser, "Copy User") }),