diff --git a/apis/kaneo/main.go b/apis/kaneo/main.go new file mode 100644 index 0000000..0cd4813 --- /dev/null +++ b/apis/kaneo/main.go @@ -0,0 +1,102 @@ +package kaneo + +import ( + "ArinDash/config" + "encoding/json" + "io" + "net/http" + "time" +) + +const apiBaseURL = "https://board.arindy.de/api" + +type configFile struct { + Board boardConfig +} + +type boardConfig struct { + ApiKey string + ProjectId string +} + +type BoardData struct { + Data Board `json:"data"` +} + +type Column struct { + Position int `json:"position"` + Name string `json:"name"` + Tasks []Task `json:"tasks"` +} + +type Task struct { + Position int `json:"position"` + Title string `json:"title"` + Description string `json:"description"` + Priority string `json:"priority"` + DueDate time.Time `json:"dueDate"` +} + +type Board struct { + Name string `json:"name"` + Columns []Column `json:"columns"` + Status string `json:"status"` +} + +func FetchBoard() Board { + cfg := &configFile{} + config.LoadConfig(cfg) + client := &http.Client{} + if cfg.Board.ApiKey == "" { + return Board{ + Status: "No API key provided", + } + } + if cfg.Board.ProjectId == "" { + return Board{ + Status: "No ProjectId provided", + } + } + + req, err := http.NewRequest("GET", apiBaseURL+"/task/tasks/"+cfg.Board.ProjectId, nil) + if err != nil { + return Board{ + Status: err.Error(), + } + } + req.Header.Add("Authorization", "Bearer "+cfg.Board.ApiKey) + resp, err := client.Do(req) + if err != nil { + return Board{ + Status: err.Error(), + } + } + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + return + } + }(resp.Body) + + respBody, err := io.ReadAll(resp.Body) + if err != nil { + return Board{ + Status: err.Error(), + } + } + + boardData := BoardData{} + board := Board{} + + err = json.Unmarshal(respBody, &boardData) + if err != nil { + board = Board{ + Status: err.Error(), + } + } else { + board = boardData.Data + board.Status = "ok" + } + + return board +} diff --git a/config_template.toml b/config_template.toml index bff544f..e3b625b 100644 --- a/config_template.toml +++ b/config_template.toml @@ -23,6 +23,10 @@ gebietsCode = "000000000000" ApiKey = "ApiKey" Sources = "comma,sepparated,sources,from,newsapi" +[board] +ApiKey = "ApiKey" +ProjectId = "ProjectId" + [[calendar.ics]] Icon = "" Url = "https://url.to.ics" diff --git a/main.go b/main.go index 6b57747..98777f0 100644 --- a/main.go +++ b/main.go @@ -40,7 +40,7 @@ func initWidgets(ctx context.Context, term *tcell.Terminal) { widgets.New("Weather", widgets.Weather()), widgets.New("ChangeTitle", widgets.TextInput(titleUpdate, textinput.Label("Update Title: "), textinput.PlaceHolder("New Title"))), widgets.New("Wifi", widgets.WifiQRCode()), - widgets.New("NetworkDevices", widgets.NetworkDevices()), + /*widgets.New("NetworkDevices", widgets.NetworkDevices()),*/ widgets.New("Docker", widgets.DockerList()), widgets.New("HTTPProber", widgets.HTTPProber()), widgets.New("PiHole", widgets.PiholeStats()), @@ -48,6 +48,7 @@ func initWidgets(ctx context.Context, term *tcell.Terminal) { widgets.New("NinaWarnings", widgets.NinaWarnings()), widgets.New("News", widgets.News()), widgets.New("Zukitchi", widgets.Zukitchi()), + widgets.New("Board", widgets.Board()), ) } @@ -214,7 +215,7 @@ func layout() []container.Option { ), ), grid.RowHeightFixed(85, - grid.Widget(widgets.Get["empty"], + grid.Widget(widgets.Get["Board"], container.Border(linestyle.Light), container.BorderColor(cell.ColorWhite), ), diff --git a/widgets/board.go b/widgets/board.go new file mode 100644 index 0000000..6b3069d --- /dev/null +++ b/widgets/board.go @@ -0,0 +1,150 @@ +package widgets + +import ( + "ArinDash/apis/kaneo" + "ArinDash/util" + "context" + "sort" + "strings" + "time" + + "github.com/mum4k/termdash/cell" + "github.com/mum4k/termdash/terminal/terminalapi" + "github.com/mum4k/termdash/widgetapi" + "github.com/mum4k/termdash/widgets/text" +) + +type BoardOptions struct { +} + +func Board() BoardOptions { + widgetOptions["BoardOptions"] = createBoard + return BoardOptions{} +} + +func createBoard(ctx context.Context, _ terminalapi.Terminal, _ interface{}) widgetapi.Widget { + widget := util.PanicOnErrorWithResult(NewBoardText()) + go util.Periodic(ctx, 1*time.Minute, func() error { + widget.Reset() + widget.board = kaneo.FetchBoard() + if widget.board.Status != "ok" { + if err := widget.Write("Error:"+widget.board.Status, text.WriteCellOpts(cell.FgColor(cell.ColorRed))); err != nil { + return nil + } + } + + sort.Slice(widget.board.Columns, func(i, j int) bool { + return widget.board.Columns[i].Position < widget.board.Columns[j].Position + }) + + colWidth := 70 / len(widget.board.Columns) + + if err := widget.Write("┌"+strings.Repeat(strings.Repeat("─", colWidth)+"┬", len(widget.board.Columns)-1)+strings.Repeat("─", colWidth)+"┐\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return err + } + + if err := widget.Write("│", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + for i, column := range widget.board.Columns { + if i != 0 { + if err := widget.Write("│", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + } + name := column.Name + if len(name) > colWidth { + name = name[0:colWidth-2] + ".." + } + if len(name) < colWidth { + name = name + strings.Repeat(" ", colWidth-len(name)) + } + + if err := widget.Write(name, text.WriteCellOpts(cell.FgColor(cell.ColorGreen))); err != nil { + return nil + } + } + if err := widget.Write("│\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + + if err := writeMiddle(widget, colWidth, widget.board.Columns); err != nil { + return err + } + + for i := range 4 { + if i != 0 { + if err := writeMiddle(widget, colWidth, widget.board.Columns); err != nil { + return err + } + } + if err := widget.Write("│", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + for j, column := range widget.board.Columns { + sort.Slice(column.Tasks, func(i, j int) bool { + return column.Tasks[i].Position < column.Tasks[j].Position + }) + + if j != 0 { + if err := widget.Write("│", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + } + if len(column.Tasks) > i { + title := column.Tasks[i].Title + if len(title) > colWidth { + title = title[0:colWidth-2] + ".." + } + if len(title) < colWidth { + title = title + strings.Repeat(" ", colWidth-len(title)) + } + if err := widget.Write(title, text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + } else { + if err := widget.Write(strings.Repeat(" ", colWidth), text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + } + } + if err := widget.Write("│\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return nil + } + } + + if err := writeBottom(widget, colWidth, widget.board.Columns); err != nil { + return err + } + return nil + }) + return widget +} + +func writeMiddle(widget *BoardText, colWidth int, columns []kaneo.Column) error { + if err := widget.Write("├"+strings.Repeat(strings.Repeat("─", colWidth)+"┼", len(columns)-1)+strings.Repeat("─", colWidth)+"┤\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return err + } + return nil +} + +func writeBottom(widget *BoardText, colWidth int, columns []kaneo.Column) error { + if err := widget.Write("└"+strings.Repeat(strings.Repeat("─", colWidth)+"┴", len(columns)-1)+strings.Repeat("─", colWidth)+"┘\n", text.WriteCellOpts(cell.FgColor(cell.ColorWhite))); err != nil { + return err + } + return nil +} + +type BoardText struct { + *text.Text + Selected int + board kaneo.Board +} + +func NewBoardText(opts ...text.Option) (*BoardText, error) { + t, err := text.New(opts...) + if err != nil { + return nil, err + } + return &BoardText{Text: t}, nil +}