|
1 | 1 | package summary
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "fmt" |
4 | 5 | "net/http"
|
5 | 6 |
|
6 | 7 | lucide "github.com/eduardolat/gomponents-lucide"
|
| 8 | + "github.com/eduardolat/pgbackweb/internal/database/dbgen" |
7 | 9 | "github.com/eduardolat/pgbackweb/internal/util/echoutil"
|
8 | 10 | "github.com/eduardolat/pgbackweb/internal/view/web/alpine"
|
9 | 11 | "github.com/eduardolat/pgbackweb/internal/view/web/component"
|
10 | 12 | "github.com/eduardolat/pgbackweb/internal/view/web/layout"
|
| 13 | + "github.com/google/uuid" |
11 | 14 | "github.com/labstack/echo/v4"
|
12 | 15 | "github.com/maragudk/gomponents"
|
13 | 16 | "github.com/maragudk/gomponents/html"
|
@@ -46,35 +49,160 @@ func (h *handlers) indexPageHandler(c echo.Context) error {
|
46 | 49 | }
|
47 | 50 |
|
48 | 51 | func indexPage(
|
49 |
| - databasesQty, destinationsQty, backupsQty, executionsQty, restorationsQty int64, |
| 52 | + databasesQty dbgen.DatabasesServiceGetDatabasesQtyRow, |
| 53 | + destinationsQty dbgen.DestinationsServiceGetDestinationsQtyRow, |
| 54 | + backupsQty dbgen.BackupsServiceGetBackupsQtyRow, |
| 55 | + executionsQty dbgen.ExecutionsServiceGetExecutionsQtyRow, |
| 56 | + restorationsQty dbgen.RestorationsServiceGetRestorationsQtyRow, |
50 | 57 | ) gomponents.Node {
|
51 |
| - countCard := func(title string, count int64) gomponents.Node { |
| 58 | + type ChartData struct { |
| 59 | + Label string |
| 60 | + Labels []string |
| 61 | + Data []int64 |
| 62 | + BgColors []string |
| 63 | + } |
| 64 | + |
| 65 | + countCard := func( |
| 66 | + title string, |
| 67 | + count int64, |
| 68 | + chartData ChartData, |
| 69 | + ) gomponents.Node { |
| 70 | + var chart gomponents.Node |
| 71 | + if len(chartData.Data) > 0 { |
| 72 | + chartID := "chart-" + uuid.NewString() |
| 73 | + |
| 74 | + labels := "" |
| 75 | + for _, label := range chartData.Labels { |
| 76 | + labels += fmt.Sprintf("'%s',", label) |
| 77 | + } |
| 78 | + |
| 79 | + areAllZero := true |
| 80 | + for _, d := range chartData.Data { |
| 81 | + if d != 0 { |
| 82 | + areAllZero = false |
| 83 | + break |
| 84 | + } |
| 85 | + } |
| 86 | + |
| 87 | + data := "" |
| 88 | + for _, d := range chartData.Data { |
| 89 | + if areAllZero { |
| 90 | + data += "-1," |
| 91 | + continue |
| 92 | + } |
| 93 | + data += fmt.Sprintf("%d,", d) |
| 94 | + } |
| 95 | + |
| 96 | + bgColors := "" |
| 97 | + for _, color := range chartData.BgColors { |
| 98 | + if areAllZero { |
| 99 | + bgColors += "'#e5e6e6'," |
| 100 | + continue |
| 101 | + } |
| 102 | + bgColors += fmt.Sprintf("'%s',", color) |
| 103 | + } |
| 104 | + |
| 105 | + chart = html.Div( |
| 106 | + html.Class("mt-2"), |
| 107 | + html.Div(html.Canvas(html.ID(chartID))), |
| 108 | + html.Script(gomponents.Raw(` |
| 109 | + new Chart(document.getElementById('`+chartID+`'), { |
| 110 | + type: 'doughnut', |
| 111 | + data: { |
| 112 | + labels: [`+labels+`], |
| 113 | + datasets: [{ |
| 114 | + label: '`+chartData.Label+`', |
| 115 | + data: [`+data+`], |
| 116 | + backgroundColor: [`+bgColors+`], |
| 117 | + borderColor: 'rgba(0, 0, 0, 0)', |
| 118 | + borderWidth: 0 |
| 119 | + }] |
| 120 | + }, |
| 121 | + options: { |
| 122 | + plugins: { |
| 123 | + legend: { |
| 124 | + position: 'bottom' |
| 125 | + }, |
| 126 | + tooltip: { |
| 127 | + callbacks: { |
| 128 | + label: function(tooltipItem) { |
| 129 | + let value = tooltipItem.raw; |
| 130 | + if (value === -1) { |
| 131 | + value = 0; |
| 132 | + } |
| 133 | + return tooltipItem.label + ': ' + value; |
| 134 | + } |
| 135 | + } |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | + }); |
| 140 | + `)), |
| 141 | + ) |
| 142 | + } |
| 143 | + |
52 | 144 | return component.CardBox(component.CardBoxParams{
|
53 |
| - Class: "text-center", |
| 145 | + Class: "text-center max-w-[250px]", |
54 | 146 | Children: []gomponents.Node{
|
55 |
| - component.H2Text(title), |
56 |
| - html.Span( |
57 |
| - html.Class("text-5xl font-bold"), |
58 |
| - gomponents.Textf("%d", count), |
59 |
| - ), |
| 147 | + component.H2Text(fmt.Sprintf("%d %s", count, title)), |
| 148 | + chart, |
60 | 149 | },
|
61 | 150 | })
|
62 | 151 | }
|
63 | 152 |
|
| 153 | + const ( |
| 154 | + greenColor = "#00a96e" |
| 155 | + redColor = "#ff5861" |
| 156 | + yellowColor = "#ffbe00" |
| 157 | + blueColor = "#00b6ff" |
| 158 | + ) |
| 159 | + |
64 | 160 | content := []gomponents.Node{
|
65 | 161 | component.H1Text("Summary"),
|
66 | 162 | html.Div(
|
67 |
| - html.Class("mt-4 grid grid-cols-5 gap-4"), |
68 |
| - countCard("Databases", databasesQty), |
69 |
| - countCard("Destinations", destinationsQty), |
70 |
| - countCard("Backups", backupsQty), |
71 |
| - countCard("Executions", executionsQty), |
72 |
| - countCard("Restorations", restorationsQty), |
| 163 | + html.Class("mt-4 flex justify-start flex-wrap gap-4"), |
| 164 | + |
| 165 | + countCard("Databases", databasesQty.All, ChartData{ |
| 166 | + Label: "Quantity", |
| 167 | + Labels: []string{"Healthy", "Unhealthy"}, |
| 168 | + Data: []int64{databasesQty.Healthy, databasesQty.Unhealthy}, |
| 169 | + BgColors: []string{greenColor, redColor}, |
| 170 | + }), |
| 171 | + countCard("Destinations", destinationsQty.All, ChartData{ |
| 172 | + Label: "Quantity", |
| 173 | + Labels: []string{"Healthy", "Unhealthy"}, |
| 174 | + Data: []int64{destinationsQty.Healthy, destinationsQty.Unhealthy}, |
| 175 | + BgColors: []string{greenColor, redColor}, |
| 176 | + }), |
| 177 | + countCard("Backups", backupsQty.All, ChartData{ |
| 178 | + Label: "Quantity", |
| 179 | + Labels: []string{"Active", "Inactive"}, |
| 180 | + Data: []int64{backupsQty.Active, backupsQty.Inactive}, |
| 181 | + BgColors: []string{greenColor, redColor}, |
| 182 | + }), |
| 183 | + countCard("Executions", executionsQty.All, ChartData{ |
| 184 | + Label: "Status", |
| 185 | + Labels: []string{"Running", "Success", "Failed", "Deleted"}, |
| 186 | + Data: []int64{ |
| 187 | + executionsQty.Running, executionsQty.Success, executionsQty.Failed, |
| 188 | + executionsQty.Deleted, |
| 189 | + }, |
| 190 | + BgColors: []string{blueColor, greenColor, redColor, yellowColor}, |
| 191 | + }), |
| 192 | + countCard("Restorations", restorationsQty.All, ChartData{ |
| 193 | + Label: "Status", |
| 194 | + Labels: []string{"Running", "Success", "Failed"}, |
| 195 | + Data: []int64{ |
| 196 | + restorationsQty.Running, restorationsQty.Success, |
| 197 | + restorationsQty.Failed, |
| 198 | + }, |
| 199 | + BgColors: []string{blueColor, greenColor, redColor}, |
| 200 | + }), |
73 | 201 | ),
|
74 | 202 | html.Div(
|
75 | 203 | alpine.XData("genericSlider(4)"),
|
76 | 204 | alpine.XCloak(),
|
77 |
| - html.Class("mt-6 flex flex-col justify-center items-center"), |
| 205 | + html.Class("mt-6 flex flex-col justify-center items-start"), |
78 | 206 | component.H2Text("How to use PG Back Web"),
|
79 | 207 |
|
80 | 208 | component.CardBox(component.CardBoxParams{
|
|
0 commit comments