feat: Sprint 3 — settings page with tabs/toggles/devices/danger-zone, brief page

This commit is contained in:
swanadiva
2026-07-07 13:31:13 +07:00
parent c4b0d7d8c0
commit c5ac07e27f
10 changed files with 438 additions and 11 deletions
+11
View File
@@ -0,0 +1,11 @@
package handler
import "github.com/gofiber/fiber/v2"
func Brief(c *fiber.Ctx) error {
return c.Render("brief", fiber.Map{
"View": "brief",
"Title": "Quick Brief",
"NavItems": navItems,
})
}
+44
View File
@@ -0,0 +1,44 @@
package handler
import (
"github.com/gofiber/fiber/v2"
"git.tukangketik.id/swanadiva/hostkeeper/v2/internal/model"
)
var deviceStore = model.NewDeviceStore()
var configStore = model.NewConfigStore()
func Settings(c *fiber.Ctx) error {
return c.Render("settings", fiber.Map{
"View": "settings",
"Title": "Settings",
"Config": configStore.Get(),
"Devices": deviceStore.All(),
"NavItems": navItems,
})
}
func UpdateConfig(c *fiber.Ctx) error {
cfg := configStore.Get()
if v := c.FormValue("theme"); v != "" { cfg.Theme = v }
if v := c.FormValue("fontSize"); v != "" { cfg.FontSize = parseInt(v, 14) }
if v := c.FormValue("scrollback"); v != "" { cfg.Scrollback = parseInt(v, 5000) }
if v := c.FormValue("autoLock"); v != "" { cfg.AutoLock = parseInt(v, 5) }
cfg.Keepalive = c.FormValue("keepalive") == "on"
cfg.CopyOnSelect = c.FormValue("copyOnSelect") == "on"
cfg.BellEnabled = c.FormValue("bellEnabled") == "on"
cfg.AutoReconnect = c.FormValue("autoReconnect") == "on"
cfg.BlinkCursor = c.FormValue("blinkCursor") == "on"
configStore.Update(cfg)
return c.Redirect("/settings", 303)
}
func parseInt(s string, def int) int {
i := 0
for _, c := range s {
if c < '0' || c > '9' { return def }
i = i*10 + int(c-'0')
}
if i == 0 { return def }
return i
}
+39
View File
@@ -0,0 +1,39 @@
package model
type AppConfig struct {
Theme string `json:"theme"` // light | dark
FontSize int `json:"fontSize"` // 12-18
Scrollback int `json:"scrollback"` // 1000-10000
AutoLock int `json:"autoLock"` // minutes
Keepalive bool `json:"keepalive"`
CopyOnSelect bool `json:"copyOnSelect"`
BellEnabled bool `json:"bellEnabled"`
AutoReconnect bool `json:"autoReconnect"`
BlinkCursor bool `json:"blinkCursor"`
}
type ConfigStore struct {
Config AppConfig
}
func NewConfigStore() *ConfigStore {
return &ConfigStore{Config: defaultConfig()}
}
func (s *ConfigStore) Get() AppConfig { return s.Config }
func (s *ConfigStore) Update(cfg AppConfig) { s.Config = cfg }
func defaultConfig() AppConfig {
return AppConfig{
Theme: "light",
FontSize: 14,
Scrollback: 5000,
AutoLock: 5,
Keepalive: true,
CopyOnSelect: true,
BellEnabled: false,
AutoReconnect: true,
BlinkCursor: false,
}
}
+28
View File
@@ -0,0 +1,28 @@
package model
type Device struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"` // desktop | mobile | tablet
OS string `json:"os"`
LastSeen string `json:"lastSeen"`
Current bool `json:"current"`
}
type DeviceStore struct {
Devices []Device
}
func NewDeviceStore() *DeviceStore {
return &DeviceStore{Devices: defaultDevices()}
}
func (s *DeviceStore) All() []Device { return s.Devices }
func defaultDevices() []Device {
return []Device{
{ID: "d1", Name: "MacBook Pro M4", Type: "desktop", OS: "macOS 15 Sequoia", LastSeen: "Just now", Current: true},
{ID: "d2", Name: "iPhone 17 Pro", Type: "mobile", OS: "iOS 20", LastSeen: "2 hours ago", Current: false},
{ID: "d3", Name: "iPad Air M3", Type: "tablet", OS: "iPadOS 20", LastSeen: "Yesterday", Current: false},
}
}