Add server-side rendered setup UI accessible via `heatwave web`. The dashboard is now re-rendered per request and includes a nav bar linking to the new /setup page. Setup provides full CRUD for profiles, rooms, devices, occupants, AC units (with room assignment), scenario toggles, and forecast fetching — all via POST/redirect/GET forms. - Add ShowNav field to DashboardData for conditional nav bar - Extract fetchForecastForProfile() for reuse by web handler - Create setup.html.tmpl with Tailwind-styled entity sections - Create web_handlers.go with 15 route handlers and flash cookies - Switch web.go from pre-rendered to per-request dashboard rendering - Graceful dashboard fallback when no forecast data exists
98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
occRoom int64
|
|
occCount int
|
|
occActivity string
|
|
occVuln bool
|
|
)
|
|
|
|
func init() {
|
|
occCmd := &cobra.Command{
|
|
Use: "occupant",
|
|
Short: "Manage room occupants",
|
|
}
|
|
|
|
addCmd := &cobra.Command{
|
|
Use: "add",
|
|
Short: "Add occupants to a room",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
o, err := db.CreateOccupant(occRoom, occCount, occActivity, occVuln)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("Occupant added (ID: %d, room: %d, count: %d, activity: %s, vulnerable: %v)\n",
|
|
o.ID, o.RoomID, o.Count, o.ActivityLevel, o.Vulnerable)
|
|
return nil
|
|
},
|
|
}
|
|
addCmd.Flags().Int64Var(&occRoom, "room", 0, "room ID")
|
|
addCmd.Flags().IntVar(&occCount, "count", 1, "number of occupants")
|
|
addCmd.Flags().StringVar(&occActivity, "activity", "sedentary", "activity level (sleeping, sedentary, light, moderate, heavy)")
|
|
addCmd.Flags().BoolVar(&occVuln, "vulnerable", false, "vulnerable occupant (elderly, child, ill)")
|
|
addCmd.MarkFlagRequired("room")
|
|
|
|
listCmd := &cobra.Command{
|
|
Use: "list",
|
|
Short: "List occupants",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
p, err := getActiveProfile()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
occupants, err := db.ListAllOccupants(p.ID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(occupants) == 0 {
|
|
fmt.Println("No occupants found")
|
|
return nil
|
|
}
|
|
for _, o := range occupants {
|
|
vuln := ""
|
|
if o.Vulnerable {
|
|
vuln = " [VULNERABLE]"
|
|
}
|
|
fmt.Printf(" [%d] room %d — %d person(s), %s%s\n", o.ID, o.RoomID, o.Count, o.ActivityLevel, vuln)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
editCmd := &cobra.Command{
|
|
Use: "edit <id> <field> <value>",
|
|
Short: "Edit an occupant field",
|
|
Args: cobra.ExactArgs(3),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
id, err := strconv.ParseInt(args[0], 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid occupant ID: %s", args[0])
|
|
}
|
|
return db.UpdateOccupant(id, args[1], args[2])
|
|
},
|
|
}
|
|
|
|
removeCmd := &cobra.Command{
|
|
Use: "remove <id>",
|
|
Short: "Remove an occupant entry",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
id, err := strconv.ParseInt(args[0], 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid occupant ID: %s", args[0])
|
|
}
|
|
return db.DeleteOccupant(id)
|
|
},
|
|
}
|
|
|
|
occCmd.AddCommand(addCmd, listCmd, editCmd, removeCmd)
|
|
rootCmd.AddCommand(occCmd)
|
|
}
|