Skip to content
This repository has been archived by the owner on Dec 3, 2024. It is now read-only.

Commit

Permalink
feat(ui): add created at (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
cyclimse authored Dec 3, 2023
1 parent e46cfff commit 35fe78d
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 11 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/sahilm/fuzzy v0.1.0 // indirect
github.com/xeonx/timeago v1.0.0-rc5 // indirect
go.etcd.io/bbolt v1.3.8 // indirect
golang.org/x/sys v0.14.0 // indirect
golang.org/x/term v0.13.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/RoaringBitmap/roaring v1.6.0 h1:dc7kRiroETgJcHhWX6BerXkZz2b3JgLGg9nTURJL/og=
github.com/RoaringBitmap/roaring v1.6.0/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
github.com/SerhiiCho/timeago v0.0.0-20231128175802-7b007dc561f3 h1:uMjbcIh1uecu/6PWJFwSF/1GVubBUeOtAsaoI2yEq8M=
github.com/SerhiiCho/timeago v0.0.0-20231128175802-7b007dc561f3/go.mod h1:EPnGJQJwLfmanxiCk75E/Xy5Cl/ZO7BlVqrank2a/nM=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
github.com/alecthomas/chroma/v2 v2.11.1 h1:m9uUtgcdAwgfFNxuqj7AIG75jD2YmL61BBIJWtdzJPs=
Expand Down Expand Up @@ -143,6 +145,8 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xeonx/timeago v1.0.0-rc5 h1:pwcQGpaH3eLfPtXeyPA4DmHWjoQt0Ea7/++FwpxqLxg=
github.com/xeonx/timeago v1.0.0-rc5/go.mod h1:qDLrYEFynLO7y5Ho7w3GwgtYgpy5UfhcXIIQvMKVDkA=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
Expand Down
5 changes: 5 additions & 0 deletions internal/resource/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package resource

import (
"context"
"time"

"github.com/scaleway/scaleway-sdk-go/scw"
)
Expand All @@ -24,6 +25,10 @@ type Metadata struct {
// May be empty.
Description *string `json:"description"`

// CreatedAt is the date the resource was created.
// Maybe nil if not available.
CreatedAt *time.Time `json:"created_at,omitempty"`

// Tags is the list of tags associated with the resource.
Tags []string `json:"tags"`

Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/cockpit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (c Cockpit) Metadata() resource.Metadata {
ProjectID: c.ProjectID,
Status: statusPtr(c.Status),
Description: nil,
CreatedAt: c.CreatedAt,
Tags: nil,
Type: resource.TypeCockpit,
Locality: resource.Global,
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func (c Container) Metadata() resource.Metadata {
ProjectID: c.Namespace.ProjectID,
Status: statusPtr(c.Container.Status),
Description: c.Container.Description,
CreatedAt: nil,
Tags: nil,
Type: resource.TypeContainer,
Locality: resource.Region(c.Container.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/container_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (ns ContainerNamespace) Metadata() resource.Metadata {
ProjectID: ns.ProjectID,
Status: statusPtr(ns.Status),
Description: ns.Description,
CreatedAt: nil,
Tags: nil,
Type: resource.TypeContainerNamespace,
Locality: resource.Region(ns.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/function.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func (f Function) Metadata() resource.Metadata {
ProjectID: f.Namespace.ProjectID,
Status: statusPtr(f.Function.Status),
Description: f.Function.Description,
CreatedAt: nil,
Tags: nil,
Type: resource.TypeFunction,
Locality: resource.Region(f.Function.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/function_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (ns FunctionNamespace) Metadata() resource.Metadata {
ProjectID: ns.ProjectID,
Status: statusPtr(ns.Status),
Description: ns.Description,
CreatedAt: nil,
Tags: nil,
Type: resource.TypeFunctionNamespace,
Locality: resource.Region(ns.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/iam_application.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (app IAMApplication) Metadata() resource.Metadata {
ProjectID: "",
Status: nil,
Description: &app.Description,
CreatedAt: app.CreatedAt,
Tags: nil,
Type: resource.TypeIAMApplication,
Locality: resource.Global,
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (i Instance) Metadata() resource.Metadata {
ProjectID: i.Project,
Status: statusPtr(i.State),
Description: nil,
CreatedAt: i.CreationDate,
Tags: i.Tags,
Type: resource.TypeInstance,
Locality: resource.Zone(i.Zone),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/job_definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func (def JobDefinition) Metadata() resource.Metadata {
Name: def.Name,
ProjectID: def.ProjectID,
Description: &def.Description,
CreatedAt: def.CreatedAt,
Type: resource.TypeJobDefinition,
Locality: resource.Region(def.Region),
}
Expand Down
14 changes: 8 additions & 6 deletions internal/resource/scaleway/job_run.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ type JobRun struct {

func (run JobRun) Metadata() resource.Metadata {
return resource.Metadata{
ID: run.ID,
Name: run.JobDefinition.Name,
ProjectID: run.JobDefinition.ProjectID,
Status: statusPtr(run.State),
Type: resource.TypeJobRun,
Locality: resource.Region(run.Region),
ID: run.ID,
Name: run.JobDefinition.Name,
ProjectID: run.JobDefinition.ProjectID,
Status: statusPtr(run.State),
Description: nil,
CreatedAt: run.CreatedAt,
Type: resource.TypeJobRun,
Locality: resource.Region(run.Region),
}
}

Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/kapsule_cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (c KapsuleCluster) Metadata() resource.Metadata {
ProjectID: c.ProjectID,
Status: statusPtr(c.Status),
Description: &c.Description,
CreatedAt: c.CreatedAt,
Tags: c.Tags,
Type: resource.TypeKapsuleCluster,
Locality: resource.Region(c.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func (p Project) Metadata() resource.Metadata {
ProjectID: p.ID,
Status: nil,
Description: &p.Description,
CreatedAt: p.CreatedAt,
Tags: nil,
Type: resource.TypeProject,
Locality: resource.Global,
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/rdb_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (i RdbInstance) Metadata() resource.Metadata {
ProjectID: i.ProjectID,
Status: statusPtr(i.Status),
Description: nil,
CreatedAt: i.CreatedAt,
Tags: i.Tags,
Type: resource.TypeRdbInstance,
Locality: resource.Region(i.Region),
Expand Down
1 change: 1 addition & 0 deletions internal/resource/scaleway/registry_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func (ns RegistryNamespace) Metadata() resource.Metadata {
ProjectID: ns.ProjectID,
Status: statusPtr(ns.Status),
Description: &ns.Description,
CreatedAt: ns.CreatedAt,
Tags: nil,
Type: resource.TypeRegistryNamespace,
Locality: resource.Region(ns.Region),
Expand Down
69 changes: 65 additions & 4 deletions internal/ui/table/builder.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package table

import (
"sync"
"time"

"github.com/charmbracelet/lipgloss"
"github.com/cyclimse/scwtui/internal/resource"
table "github.com/cyclimse/scwtui/internal/ui/table/custom"
"github.com/mattn/go-runewidth"
"github.com/xeonx/timeago"
)

const widthOfAnUUID = 36
Expand All @@ -16,6 +20,7 @@ var (
"Name",
"Type",
"Project",
"Created",
"Locality",
}

Expand All @@ -24,6 +29,7 @@ var (
"ID",
"Type",
"Project ID",
"Created At",
"Locality",
}

Expand All @@ -32,13 +38,32 @@ var (
"Locality": runewidth.StringWidth("Locality"),
"ID": widthOfAnUUID,
"Project ID": widthOfAnUUID,
"Created At": runewidth.StringWidth(time.RFC3339),
"Type": runewidth.StringWidth(resource.TypeContainerNamespace.String()),
"Created": runewidth.StringWidth("about an hour ago"),
}
)

func NewBuilder(styles table.Styles) *Build {
return &Build{
styles: styles,
b := &Build{
styles: styles,
timeagoCache: make(map[time.Time]string),
mutex: &sync.Mutex{},
}

timer := time.NewTicker(time.Minute)

go func() {
// purge the cache every minute to allow timeago to update.
// eg. "a few seconds ago" -> "a minute ago"
for range timer.C {
b.mutex.Lock()
b.timeagoCache = make(map[time.Time]string)
b.mutex.Unlock()
}
}()

return b
}

type BuildParams struct {
Expand Down Expand Up @@ -76,6 +101,7 @@ func (b *Build) buildRows(params BuildParams) []table.Row {
metadata.Name,
metadata.Type.String(),
params.ProjectIDsToNames[metadata.ProjectID],
b.formattedTimeAgo(metadata.CreatedAt),
metadata.Locality.String(),
})
}
Expand All @@ -89,11 +115,18 @@ func (b *Build) buildRowsAlt(params BuildParams) []table.Row {

for _, r := range resources {
metadata := r.Metadata()

createdAt := ""
if metadata.CreatedAt != nil {
createdAt = metadata.CreatedAt.Format(time.RFC3339)
}

rows = append(rows, table.Row{
lipgloss.PlaceHorizontal(6, lipgloss.Center, string(metadata.Status.Emoji(metadata.Type))),
metadata.ID,
metadata.Type.String(),
metadata.ProjectID,
createdAt,
metadata.Locality.String(),
})
}
Expand All @@ -119,7 +152,11 @@ func (b *Build) buildCols(params BuildParams) []table.Column {
}
}

widthPerColumn := (widthWithPadding - fixedColumnsWidth) / (len(titles) - fixedColumnsCount)
widthPerColumn := 0
if len(titles) > fixedColumnsCount {
widthPerColumn = (widthWithPadding - fixedColumnsWidth) / (len(titles) - fixedColumnsCount)
}

if widthPerColumn < 0 {
widthPerColumn = 0
}
Expand All @@ -139,9 +176,33 @@ func (b *Build) buildCols(params BuildParams) []table.Column {
})
}

if widthPerColumn == 0 {
cols[len(cols)-1].Width += widthWithPadding - fixedColumnsWidth
}

return cols
}

func (b Build) formattedTimeAgo(t *time.Time) string {
if t == nil {
return ""
}

b.mutex.Lock()
defer b.mutex.Unlock()

if formatted, ok := b.timeagoCache[*t]; ok {
return formatted
}

formatted := timeago.English.Format(*t)
b.timeagoCache[*t] = formatted

return formatted
}

type Build struct {
styles table.Styles
styles table.Styles
timeagoCache map[time.Time]string
mutex *sync.Mutex
}
22 changes: 22 additions & 0 deletions internal/ui/table/builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package table

import (
"testing"

"github.com/cyclimse/scwtui/internal/resource"
"github.com/mattn/go-runewidth"
"github.com/stretchr/testify/assert"
)

func TestColumnsWithFixedWidth(t *testing.T) {
longestResourceType := 0

for i := 0; i < int(resource.NumberOfResourceTypes); i++ {
typeName := resource.Type(i).String()
if runewidth.StringWidth(typeName) > longestResourceType {
longestResourceType = runewidth.StringWidth(typeName)
}
}

assert.Equal(t, longestResourceType, columnsWithFixedWidth["Type"])
}
2 changes: 1 addition & 1 deletion internal/ui/table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func Table(state ui.ApplicationState) Model {
func (m Model) Init() tea.Cmd { return nil }

const (
additionalHorizontalPadding = 8
additionalHorizontalPadding = 10
tableHeaderHeight = 4
)

Expand Down

0 comments on commit 35fe78d

Please sign in to comment.