Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion apps/backend/internal/backendapp/boot_state_routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ func (b bootStateBuilder) tasksPageBootData(ctx context.Context, req *http.Reque
settingsWorkspaceID := ""
settingsWorkflowID := ""
settingsRepositoryID := ""
settingsTasksListSort := ""
settingsTasksListGroup := ""
if hasSettings {
settingsWorkspaceID = settings.Settings.WorkspaceID
settingsWorkflowID = settings.Settings.WorkflowFilterID
settingsTasksListSort = settings.Settings.TasksListSort
settingsTasksListGroup = settings.Settings.TasksListGroup
if len(settings.Settings.RepositoryIDs) > 0 {
settingsRepositoryID = settings.Settings.RepositoryIDs[0]
}
}
tasksListSort := tasksListSortForRoute(queryValue(req, "sort"), settingsTasksListSort)
tasksListGroup := tasksListGroupForRoute(queryValue(req, "group"), settingsTasksListGroup)
workspaceIDs := workspaceIDSet(workspaces)
activeWorkspaceID := firstValidID(
workspaceIDs,
Expand All @@ -54,7 +60,7 @@ func (b bootStateBuilder) tasksPageBootData(ctx context.Context, req *http.Reque
state["userSettings"] = mapUserSettingsState(settings, activeWorkspaceID)
}
if activeWorkspaceID == "" {
return state, map[string]any{"activeWorkspaceId": nil, "workflows": []any{}, "steps": []any{}, "repositories": []any{}, "tasks": []any{}, "total": 0}
return state, map[string]any{"activeWorkspaceId": nil, "workflows": []any{}, "steps": []any{}, "repositories": []any{}, "tasks": []any{}, "total": 0, "tasksListSort": tasksListSort, "tasksListGroup": tasksListGroup}
}
workflows, err := b.p.taskSvc.ListWorkflows(ctx, activeWorkspaceID, false)
if err != nil {
Expand All @@ -77,6 +83,8 @@ func (b bootStateBuilder) tasksPageBootData(ctx context.Context, req *http.Reque
"repositories": repositories,
"tasks": tasks,
"total": total,
"tasksListSort": tasksListSort,
"tasksListGroup": tasksListGroup,
}
return state, routeData
}
Expand Down Expand Up @@ -438,6 +446,8 @@ func mapUserSettingsState(response userdto.UserSettingsResponse, workspaceID str
"kanbanViewMode": nullString(settings.KanbanViewMode),
"workflowId": nullString(settings.WorkflowFilterID),
"repositoryIds": stringSlice(settings.RepositoryIDs),
"tasksListSort": defaultString(settings.TasksListSort, "updated_desc"),
"tasksListGroup": defaultString(settings.TasksListGroup, "state"),
"preferredShell": nullString(settings.PreferredShell),
"shellOptions": response.ShellOptions,
"defaultEditorId": nullString(settings.DefaultEditorID),
Expand Down Expand Up @@ -635,6 +645,44 @@ func queryValue(req *http.Request, name string) string {
return strings.TrimSpace(parsed.Query().Get(name))
}

func tasksListSortForRoute(queryValue, settingsValue string) string {
if validTasksListSort(queryValue) {
return queryValue
}
if validTasksListSort(settingsValue) {
return settingsValue
}
return "updated_desc"
}

func tasksListGroupForRoute(queryValue, settingsValue string) string {
if validTasksListGroup(queryValue) {
return queryValue
}
if validTasksListGroup(settingsValue) {
return settingsValue
}
return "state"
}

func validTasksListSort(value string) bool {
Comment thread
carlosflorencio marked this conversation as resolved.
Outdated
switch strings.TrimSpace(value) {
case "updated_desc", "updated_asc", "created_desc", "created_asc", "title_asc", "title_desc":
return true
default:
return false
}
}

func validTasksListGroup(value string) bool {
switch strings.TrimSpace(value) {
case "state", "workflow", "repository", "none":
return true
default:
return false
}
}

func readActiveWorkspaceCookie(req *http.Request) string {
if req == nil {
return ""
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/internal/user/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func (c *Controller) UpdateUserSettings(ctx context.Context, req dto.UpdateUserS
KanbanViewMode: req.KanbanViewMode,
WorkflowFilterID: req.WorkflowFilterID,
RepositoryIDs: req.RepositoryIDs,
TasksListSort: req.TasksListSort,
TasksListGroup: req.TasksListGroup,
InitialSetupComplete: req.InitialSetupComplete,
PreferredShell: req.PreferredShell,
DefaultEditorID: req.DefaultEditorID,
Expand Down
6 changes: 6 additions & 0 deletions apps/backend/internal/user/dto/dto.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type UserSettingsDTO struct {
KanbanViewMode string `json:"kanban_view_mode"`
WorkflowFilterID string `json:"workflow_filter_id"`
RepositoryIDs []string `json:"repository_ids"`
TasksListSort string `json:"tasks_list_sort"`
TasksListGroup string `json:"tasks_list_group"`
InitialSetupComplete bool `json:"initial_setup_complete"`
PreferredShell string `json:"preferred_shell"`
DefaultEditorID string `json:"default_editor_id"`
Expand Down Expand Up @@ -74,6 +76,8 @@ type UpdateUserSettingsRequest struct {
KanbanViewMode *string `json:"kanban_view_mode,omitempty"`
WorkflowFilterID *string `json:"workflow_filter_id,omitempty"`
RepositoryIDs *[]string `json:"repository_ids,omitempty"`
TasksListSort *string `json:"tasks_list_sort,omitempty"`
TasksListGroup *string `json:"tasks_list_group,omitempty"`
InitialSetupComplete *bool `json:"initial_setup_complete,omitempty"`
PreferredShell *string `json:"preferred_shell,omitempty"`
DefaultEditorID *string `json:"default_editor_id,omitempty"`
Expand Down Expand Up @@ -184,6 +188,8 @@ func FromUserSettings(settings *models.UserSettings) UserSettingsDTO {
KanbanViewMode: settings.KanbanViewMode,
WorkflowFilterID: settings.WorkflowFilterID,
RepositoryIDs: settings.RepositoryIDs,
TasksListSort: settings.TasksListSort,
TasksListGroup: settings.TasksListGroup,
InitialSetupComplete: settings.InitialSetupComplete,
PreferredShell: settings.PreferredShell,
DefaultEditorID: settings.DefaultEditorID,
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/internal/user/models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ type UserSettings struct {
KanbanViewMode string `json:"kanban_view_mode"`
WorkflowFilterID string `json:"workflow_filter_id"`
RepositoryIDs []string `json:"repository_ids"`
TasksListSort string `json:"tasks_list_sort"`
TasksListGroup string `json:"tasks_list_group"`
InitialSetupComplete bool `json:"initial_setup_complete"`
PreferredShell string `json:"preferred_shell"`
DefaultEditorID string `json:"default_editor_id"`
Expand Down
51 changes: 51 additions & 0 deletions apps/backend/internal/user/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ var (
const (
changesPanelLayoutFlat = "flat"
changesPanelLayoutTree = "tree"
tasksListSortDefault = "updated_desc"
tasksListGroupDefault = "state"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
)

type Service struct {
Expand All @@ -39,6 +41,8 @@ type UpdateUserSettingsRequest struct {
KanbanViewMode *string
WorkflowFilterID *string
RepositoryIDs *[]string
TasksListSort *string
TasksListGroup *string
InitialSetupComplete *bool
PreferredShell *string
DefaultEditorID *string
Expand Down Expand Up @@ -193,6 +197,9 @@ func applyBasicSettings(settings *models.UserSettings, req *UpdateUserSettingsRe
if req.RepositoryIDs != nil {
settings.RepositoryIDs = *req.RepositoryIDs
}
if err := applyTasksListPreferences(settings, req.TasksListSort, req.TasksListGroup); err != nil {
return err
}
if req.InitialSetupComplete != nil {
settings.InitialSetupComplete = *req.InitialSetupComplete
}
Expand Down Expand Up @@ -248,6 +255,48 @@ func applyBasicSettings(settings *models.UserSettings, req *UpdateUserSettingsRe
return nil
}

func applyTasksListPreferences(settings *models.UserSettings, sortValue, groupValue *string) error {
if sortValue != nil {
v := strings.TrimSpace(*sortValue)
if v == "" {
v = tasksListSortDefault
}
if !validTasksListSort(v) {
return errors.New("tasks_list_sort must be one of updated_desc, updated_asc, created_desc, created_asc, title_asc, title_desc")
}
settings.TasksListSort = v
}
if groupValue != nil {
v := strings.TrimSpace(*groupValue)
if v == "" {
v = tasksListGroupDefault
}
if !validTasksListGroup(v) {
return errors.New("tasks_list_group must be one of state, workflow, repository, none")
}
settings.TasksListGroup = v
}
return nil
}

func validTasksListSort(value string) bool {
switch value {
case "updated_desc", "updated_asc", "created_desc", "created_asc", "title_asc", "title_desc":
return true
default:
return false
}
}

func validTasksListGroup(value string) bool {
switch value {
case "state", "workflow", "repository", "none":
return true
default:
return false
}
}

func applyTerminalLinkBehavior(settings *models.UserSettings, value *string) error {
if value == nil {
return nil
Expand Down Expand Up @@ -505,6 +554,8 @@ func (s *Service) publishUserSettingsEvent(ctx context.Context, settings *models
"kanban_view_mode": settings.KanbanViewMode,
"workflow_filter_id": settings.WorkflowFilterID,
"repository_ids": settings.RepositoryIDs,
"tasks_list_sort": settings.TasksListSort,
"tasks_list_group": settings.TasksListGroup,
"initial_setup_complete": settings.InitialSetupComplete,
"preferred_shell": settings.PreferredShell,
"default_editor_id": settings.DefaultEditorID,
Expand Down
35 changes: 35 additions & 0 deletions apps/backend/internal/user/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,41 @@ func TestApplyBasicSettings_ReleaseNotes(t *testing.T) {
})
}

func TestApplyBasicSettings_TasksListPreferences(t *testing.T) {
t.Run("sets valid sort and group", func(t *testing.T) {
settings := &models.UserSettings{}
req := &UpdateUserSettingsRequest{
TasksListSort: ptr("title_asc"),
TasksListGroup: ptr("repository"),
}
if err := applyBasicSettings(settings, req); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if settings.TasksListSort != "title_asc" {
t.Fatalf("TasksListSort = %q, want title_asc", settings.TasksListSort)
}
if settings.TasksListGroup != "repository" {
t.Fatalf("TasksListGroup = %q, want repository", settings.TasksListGroup)
}
})

t.Run("rejects invalid sort", func(t *testing.T) {
settings := &models.UserSettings{}
req := &UpdateUserSettingsRequest{TasksListSort: ptr("priority_desc")}
if err := applyBasicSettings(settings, req); err == nil {
t.Fatal("expected invalid sort error")
}
})

t.Run("rejects invalid group", func(t *testing.T) {
settings := &models.UserSettings{}
req := &UpdateUserSettingsRequest{TasksListGroup: ptr("assignee")}
if err := applyBasicSettings(settings, req); err == nil {
t.Fatal("expected invalid group error")
}
})
}

func TestApplyBasicSettings_TerminalFontFamily(t *testing.T) {
t.Run("nil leaves settings unchanged", func(t *testing.T) {
settings := &models.UserSettings{TerminalFontFamily: "Fira Code"}
Expand Down
34 changes: 34 additions & 0 deletions apps/backend/internal/user/store/sqlite.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,8 @@ func marshalUserSettingsPayload(settings *models.UserSettings) ([]byte, error) {
"kanban_view_mode": settings.KanbanViewMode,
"workflow_filter_id": settings.WorkflowFilterID,
"repository_ids": settings.RepositoryIDs,
"tasks_list_sort": normalizeTasksListSort(settings.TasksListSort),
"tasks_list_group": normalizeTasksListGroup(settings.TasksListGroup),
"initial_setup_complete": settings.InitialSetupComplete,
"preferred_shell": settings.PreferredShell,
"default_editor_id": settings.DefaultEditorID,
Expand Down Expand Up @@ -447,6 +449,8 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
settings.UserID = userID
if settingsRaw == "" || settingsRaw == "{}" {
settings.RepositoryIDs = []string{}
settings.TasksListSort = defaultTasksListSort()
settings.TasksListGroup = defaultTasksListGroup()
settings.ShowReleaseNotification = true
settings.ReviewAutoMarkOnScroll = true
settings.ChatSubmitKey = "cmd_enter"
Expand All @@ -463,6 +467,8 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
KanbanViewMode string `json:"kanban_view_mode"`
WorkflowFilterID string `json:"workflow_filter_id"`
RepositoryIDs []string `json:"repository_ids"`
TasksListSort string `json:"tasks_list_sort"`
TasksListGroup string `json:"tasks_list_group"`
InitialSetupComplete bool `json:"initial_setup_complete"`
PreferredShell string `json:"preferred_shell"`
DefaultEditorID string `json:"default_editor_id"`
Expand Down Expand Up @@ -502,6 +508,8 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
settings.KanbanViewMode = payload.KanbanViewMode
settings.WorkflowFilterID = payload.WorkflowFilterID
settings.RepositoryIDs = payload.RepositoryIDs
settings.TasksListSort = normalizeTasksListSort(payload.TasksListSort)
settings.TasksListGroup = normalizeTasksListGroup(payload.TasksListGroup)
settings.InitialSetupComplete = payload.InitialSetupComplete
settings.PreferredShell = payload.PreferredShell
settings.DefaultEditorID = payload.DefaultEditorID
Expand Down Expand Up @@ -572,6 +580,32 @@ func scanUserSettings(scanner interface{ Scan(dest ...any) error }, userID strin
return settings, nil
}

func defaultTasksListSort() string {
return "updated_desc"
}

func defaultTasksListGroup() string {
return "state"
}

func normalizeTasksListSort(value string) string {
switch value {
case "updated_desc", "updated_asc", "created_desc", "created_asc", "title_asc", "title_desc":
return value
default:
return defaultTasksListSort()
}
}

func normalizeTasksListGroup(value string) string {
switch value {
case "state", "workflow", "repository", "none":
return value
default:
return defaultTasksListGroup()
}
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
func normalizeSidebarTaskPrefs(prefs models.SidebarTaskPrefs) models.SidebarTaskPrefs {
if prefs.PinnedTaskIDs == nil {
prefs.PinnedTaskIDs = []string{}
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/pkg/api/v1/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ type UserSettings struct {
KanbanViewMode string `json:"kanban_view_mode"`
WorkflowFilterID string `json:"workflow_filter_id"`
RepositoryIDs []string `json:"repository_ids"`
TasksListSort string `json:"tasks_list_sort"`
TasksListGroup string `json:"tasks_list_group"`
PreferredShell string `json:"preferred_shell"`
DefaultEditorID string `json:"default_editor_id"`
UpdatedAt time.Time `json:"updated_at"`
Expand Down
Loading
Loading