mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-12 05:36:49 +00:00
reports page
This commit is contained in:
parent
9f4af72c85
commit
172c5037b4
9 changed files with 283 additions and 0 deletions
16
constants/months.go
Normal file
16
constants/months.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package constants
|
||||||
|
|
||||||
|
var Months = []string{
|
||||||
|
"January",
|
||||||
|
"February",
|
||||||
|
"March",
|
||||||
|
"April",
|
||||||
|
"May",
|
||||||
|
"June",
|
||||||
|
"July",
|
||||||
|
"August",
|
||||||
|
"September",
|
||||||
|
"October",
|
||||||
|
"November",
|
||||||
|
"December",
|
||||||
|
}
|
||||||
|
|
@ -3,4 +3,5 @@ package constants
|
||||||
const (
|
const (
|
||||||
RouteBooking = "/bookings"
|
RouteBooking = "/bookings"
|
||||||
RouteNewBooking = "/bookings/new"
|
RouteNewBooking = "/bookings/new"
|
||||||
|
RouteReports = "/reports"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,60 @@ func (s Server) handleCreateItem() echo.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s Server) handleReportsPage() echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
period := c.QueryParam("period")
|
||||||
|
if !u.Contains([]string{"month", "year"}, period) {
|
||||||
|
period = "month"
|
||||||
|
}
|
||||||
|
|
||||||
|
monthStr := c.QueryParam("month")
|
||||||
|
month, err := strconv.Atoi(monthStr)
|
||||||
|
if err != nil || month < 1 || month > 12 {
|
||||||
|
month = int(time.Now().Month())
|
||||||
|
}
|
||||||
|
|
||||||
|
yearStr := c.QueryParam("year")
|
||||||
|
_, err = strconv.Atoi(yearStr)
|
||||||
|
if err != nil {
|
||||||
|
yearStr = time.Now().Format("2006")
|
||||||
|
}
|
||||||
|
return s.renderTempl(c, http.StatusOK, views.Reports(constants.Months, yearStr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Server) handleComputeReport() echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
period := c.QueryParam("period")
|
||||||
|
if !u.Contains([]string{"month", "year"}, period) {
|
||||||
|
return &echo.HTTPError{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: fmt.Sprintf("%q is not a valid period", period),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monthStr := c.QueryParam("month")
|
||||||
|
month, err := strconv.Atoi(monthStr)
|
||||||
|
if err != nil || month < 1 || month > 12 {
|
||||||
|
return &echo.HTTPError{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: fmt.Sprintf("%q is not a valid month", month),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yearStr := c.QueryParam("year")
|
||||||
|
year, err := strconv.Atoi(yearStr)
|
||||||
|
if err != nil {
|
||||||
|
return &echo.HTTPError{
|
||||||
|
Code: http.StatusBadRequest,
|
||||||
|
Message: fmt.Sprintf("%q is not a valid year", year),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: compute report view model
|
||||||
|
return s.renderTempl(c, http.StatusOK, views.ReportSection())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) handleCreateInvoicePdf() echo.HandlerFunc {
|
func (s Server) handleCreateInvoicePdf() echo.HandlerFunc {
|
||||||
return func(c echo.Context) error {
|
return func(c echo.Context) error {
|
||||||
data := struct {
|
data := struct {
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,7 @@ func (s Server) MountHandlers() {
|
||||||
s.Router.POST(constants.RouteNewBooking, s.handleCreateBooking())
|
s.Router.POST(constants.RouteNewBooking, s.handleCreateBooking())
|
||||||
s.Router.GET(fmt.Sprintf("%s/:id", constants.RouteBooking), s.handleBookingPage())
|
s.Router.GET(fmt.Sprintf("%s/:id", constants.RouteBooking), s.handleBookingPage())
|
||||||
s.Router.POST(fmt.Sprintf("%s/:id/items", constants.RouteBooking), s.handleCreateItem())
|
s.Router.POST(fmt.Sprintf("%s/:id/items", constants.RouteBooking), s.handleCreateItem())
|
||||||
|
s.Router.GET(constants.RouteReports, s.handleReportsPage())
|
||||||
|
s.Router.GET(fmt.Sprintf("%s/do", constants.RouteReports), s.handleComputeReport())
|
||||||
s.Router.GET("/pdf", s.handleCreateInvoicePdf())
|
s.Router.GET("/pdf", s.handleCreateInvoicePdf())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ templ BaseLayout() {
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/bookings">Bookings</a></li>
|
<li><a href="/bookings">Bookings</a></li>
|
||||||
|
<li><a href="/reports">Reports</a></li>
|
||||||
<li><a href="/bookings/new" role="button">New Booking</a></li>
|
<li><a href="/bookings/new" role="button">New Booking</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
|
||||||
38
internal/views/report_section.templ
Normal file
38
internal/views/report_section.templ
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
templ ReportSection() {
|
||||||
|
<div class="grid">
|
||||||
|
<h2>Your report </h2>
|
||||||
|
<div>
|
||||||
|
<button class="outline">Create PDF</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<figure>
|
||||||
|
<table role="grid">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">ID</th>
|
||||||
|
<th scope="col">Name</th>
|
||||||
|
<th scope="col">Total (€)</th>
|
||||||
|
<th scope="col">From</th>
|
||||||
|
<th scope="col">To</th>
|
||||||
|
<th scope="col">Platform</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th scope="row">
|
||||||
|
<a href="fjkslafj">
|
||||||
|
12
|
||||||
|
</a>
|
||||||
|
</th>
|
||||||
|
<td>joe</td>
|
||||||
|
<td></td>
|
||||||
|
<td>fgsdkjf</td>
|
||||||
|
<td>jjxlkvj</td>
|
||||||
|
<td>jjxlkvj</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</figure>
|
||||||
|
}
|
||||||
35
internal/views/report_section_templ.go
Normal file
35
internal/views/report_section_templ.go
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.543
|
||||||
|
package views
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
func ReportSection() templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"grid\"><h2>Your report </h2><div><button class=\"outline\">Create PDF</button></div></div><figure><table role=\"grid\"><thead><tr><th scope=\"col\">ID</th><th scope=\"col\">Name</th><th scope=\"col\">Total (€)</th><th scope=\"col\">From</th><th scope=\"col\">To</th><th scope=\"col\">Platform</th></tr></thead> <tbody><tr><th scope=\"row\"><a href=\"fjkslafj\">12</a></th><td>joe</td><td></td><td>fgsdkjf</td><td>jjxlkvj</td><td>jjxlkvj</td></tr></tbody></table></figure>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
43
internal/views/reports.templ
Normal file
43
internal/views/reports.templ
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package views
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
templ Reports(months []string, year string) {
|
||||||
|
@BaseLayout() {
|
||||||
|
<hgroup>
|
||||||
|
<h1>Reports</h1>
|
||||||
|
<h2>Generate monthly and yearly statements</h2>
|
||||||
|
</hgroup>
|
||||||
|
<form hx-get="/reports/do" hx-target="#report">
|
||||||
|
<fieldset>
|
||||||
|
<legend>Period</legend>
|
||||||
|
<label for="month">
|
||||||
|
<input type="radio" id="month" name="period" value="month" checked/>
|
||||||
|
Monthly
|
||||||
|
</label>
|
||||||
|
<label for="year">
|
||||||
|
<input type="radio" id="year" name="period" value="year"/>
|
||||||
|
Yearly
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<label for="year">
|
||||||
|
Year
|
||||||
|
<input type="number" id="year" name="year" value={ year }/>
|
||||||
|
</label>
|
||||||
|
<label for="month">
|
||||||
|
Month
|
||||||
|
<select name="month" id="month" autofocus>
|
||||||
|
for i, month := range months {
|
||||||
|
<option value={ strconv.Itoa(i + 1) }>{ month } </option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<button>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<section id="report"></section>
|
||||||
|
}
|
||||||
|
}
|
||||||
93
internal/views/reports_templ.go
Normal file
93
internal/views/reports_templ.go
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Code generated by templ - DO NOT EDIT.
|
||||||
|
|
||||||
|
// templ: version: v0.2.543
|
||||||
|
package views
|
||||||
|
|
||||||
|
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||||
|
|
||||||
|
import "github.com/a-h/templ"
|
||||||
|
import "context"
|
||||||
|
import "io"
|
||||||
|
import "bytes"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Reports(months []string, year string) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var1 == nil {
|
||||||
|
templ_7745c5c3_Var1 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<hgroup><h1>Reports</h1><h2>Generate monthly and yearly statements</h2></hgroup><form hx-get=\"/reports/do\" hx-target=\"#report\"><fieldset><legend>Period</legend> <label for=\"month\"><input type=\"radio\" id=\"month\" name=\"period\" value=\"month\" checked> Monthly</label> <label for=\"year\"><input type=\"radio\" id=\"year\" name=\"period\" value=\"year\"> Yearly</label></fieldset><label for=\"year\">Year <input type=\"number\" id=\"year\" name=\"year\" value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(year))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"></label> <label for=\"month\">Month <select name=\"month\" id=\"month\" autofocus>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for i, month := range months {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(strconv.Itoa(i + 1)))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var3 string
|
||||||
|
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(month)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/views/reports.templ`, Line: 32, Col: 51}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></label> <button>Submit</button></form><section id=\"report\"></section>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
templ_7745c5c3_Err = BaseLayout().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue