Add time to timer

This commit is contained in:
2024-04-14 23:51:04 +02:00
parent 3928c520e8
commit 1e9b8d0fa1
6 changed files with 302 additions and 76 deletions

View File

@ -1,34 +1,42 @@
package model package model
import ( import (
"time" sqldriver "database/sql/driver"
"errors" "errors"
sqldriver "database/sql/driver" "time"
) )
type Time time.Time type Time time.Time
func MakeTimeNow() Time { func MakeTimeNow() Time {
return Time(time.Now().UTC()) return Time(time.Now().UTC())
} }
func (self Time) AsUTCString() string { func (self Time) AsUTCString() string {
return time.Time(self).Format(time.RFC3339) return time.Time(self).Format(time.RFC3339)
} }
func (self Time) Value() (sqldriver.Value, error) { func (self Time) Value() (sqldriver.Value, error) {
return self.AsUTCString(), nil return self.AsUTCString(), nil
} }
func (self *Time) Scan(value any) error { func (self *Time) Scan(value any) error {
if valueStr, ok := value.(string); ok { if valueStr, ok := value.(string); ok {
parsedTime, err := time.Parse(time.RFC3339, valueStr) parsedTime, err := time.Parse(time.RFC3339, valueStr)
if err == nil { if err == nil {
*self = Time(parsedTime.UTC()) *self = Time(parsedTime.UTC())
} }
return err return err
} else { } else {
return errors.New("Expected a string") return errors.New("Expected a string")
} }
}
func (from Time) Compare(to Time) int {
return time.Time(from).Compare(time.Time(to))
}
func (self *Time) Add(duration time.Duration) {
*self = Time(time.Time(*self).Add(duration))
} }

View File

@ -1,9 +1,12 @@
package model package model
type Timer struct { type Timer struct {
Id UUID Id UUID
Name string Name string
StartTime Time StartTime Time
EndTime Time EndTime Time
} }
func (self Timer) IsFinished() bool {
return MakeTimeNow().Compare(self.EndTime) >= 0
}

View File

@ -1,28 +1,27 @@
package model package model
import ( import (
sqldriver "database/sql/driver" sqldriver "database/sql/driver"
"github.com/google/uuid" "github.com/google/uuid"
) )
type UUID struct { type UUID struct {
payload uuid.UUID payload uuid.UUID
} }
func MakeUUID() UUID { func MakeUUID() UUID {
id, _ := uuid.NewV7() id, _ := uuid.NewV7()
return UUID { payload: id } return UUID{payload: id}
} }
func (self UUID) Value() (sqldriver.Value, error) { func (self UUID) Value() (sqldriver.Value, error) {
return self.payload[:], nil return self.payload[:], nil
} }
func (self *UUID) Scan(value any) error { func (self *UUID) Scan(value any) error {
return self.payload.Scan(value) return self.payload.Scan(value)
} }
func (self UUID) String() string { func (self UUID) String() string {
return self.payload.String() return self.payload.String()
} }

View File

@ -3,6 +3,7 @@ package main
import ( import (
"context" "context"
"database/sql" "database/sql"
"errors"
"log" "log"
"net/http" "net/http"
"strconv" "strconv"
@ -50,12 +51,12 @@ func initializeDatabase(db *sql.DB) error {
return err return err
} }
err = insertTimer(tx, "My timer", 600) err = insertTimer(tx, "My timer", 6)
if err != nil { if err != nil {
return err return err
} }
err = insertTimer(tx, "My timer2", 600) err = insertTimer(tx, "My timer2", 6)
if err != nil { if err != nil {
return err return err
} }
@ -86,7 +87,7 @@ func initializeDatabase(db *sql.DB) error {
return err return err
} }
_, err = tx.Exec(`INSERT INTO Timer VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password) _, err = tx.Exec(`INSERT INTO User VALUES ($1, $2, $3, $4)`, userId, "steven", userUuidStr, password)
if err != nil { if err != nil {
return err return err
} }
@ -139,7 +140,17 @@ func deleteTimer(db *sql.DB, idStr string) bool {
} }
affected, err := res.RowsAffected() affected, err := res.RowsAffected()
return err == nil && affected > 0 return err == nil && affected == 1
}
func updateTimerEndTime(db *sql.DB, id model.UUID, endTime model.Time) bool {
res, err := db.Exec("UPDATE Timer SET EndTime=$1 WHERE Id=$2", endTime, id)
if err != nil {
return false
}
affected, err := res.RowsAffected()
return err == nil && affected == 1
} }
type Session struct { type Session struct {
@ -174,6 +185,65 @@ func (server *MyServer) handleTimer(w http.ResponseWriter, r *http.Request) {
} }
} }
func parseDuration(value string) (time.Duration, error) {
const nullDuration = time.Duration(0)
if len(value) == 0 {
return nullDuration, errors.New("Empty duration string")
}
var unit time.Duration
switch value[len(value)-1] {
case 's':
unit = time.Second
case 'm':
unit = time.Minute
case 'h':
unit = time.Hour
case 'd':
unit = time.Duration(24) * time.Hour
case 'w':
unit = time.Duration(24*7) * time.Hour
default:
return nullDuration, errors.New("Invalid duration format")
}
amount, err := strconv.ParseInt(value[0:len(value)-1], 10, 64)
if err != nil || amount < 0 {
return nullDuration, errors.New("Invalid duration value")
}
return time.Duration(amount) * unit, nil
}
func (server *MyServer) handleTimerAddTime(w http.ResponseWriter, r *http.Request) {
timer := queryTimer(server.db, r.PathValue("timerId"))
if timer == nil {
server.handleNotFound(w, r)
return
}
if timer.IsFinished() {
w.WriteHeader(http.StatusBadRequest)
return
}
duration, err := parseDuration(r.PathValue("timeToAdd"))
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(err.Error()))
return
}
timer.EndTime.Add(duration)
res := updateTimerEndTime(server.db, timer.Id, timer.EndTime)
if !res {
w.WriteHeader(http.StatusBadRequest)
return
}
view.TimerView(*timer).Render(context.Background(), w)
}
func (server *MyServer) handleDeleteTimer(w http.ResponseWriter, r *http.Request) { func (server *MyServer) handleDeleteTimer(w http.ResponseWriter, r *http.Request) {
success := deleteTimer(server.db, r.PathValue("timerId")) success := deleteTimer(server.db, r.PathValue("timerId"))
if !success { if !success {
@ -244,6 +314,7 @@ func main() {
http.Handle("GET /static/", http.StripPrefix("/static/", fs)) http.Handle("GET /static/", http.StripPrefix("/static/", fs))
http.HandleFunc("GET /timer/{timerId}", myServer.handleTimer) http.HandleFunc("GET /timer/{timerId}", myServer.handleTimer)
http.HandleFunc("POST /timer/{timerId}/addTime/{timeToAdd}", myServer.handleTimerAddTime)
http.HandleFunc("DELETE /timer/{timerId}", myServer.handleDeleteTimer) http.HandleFunc("DELETE /timer/{timerId}", myServer.handleDeleteTimer)
http.HandleFunc("PUT /timer", myServer.handlePutTimer) http.HandleFunc("PUT /timer", myServer.handlePutTimer)
http.HandleFunc("GET /", myServer.handleMain) http.HandleFunc("GET /", myServer.handleMain)

View File

@ -1,26 +1,43 @@
package view package view
import ( import (
"stevenlr.com/timer/model" "fmt"
"stevenlr.com/timer/model"
) )
templ TimerView(timer model.Timer) { templ TimerView(timer model.Timer) {
<h1>This is timer { timer.Name } </h1> <div class="timer">
<p><a href="/">Back to list</a></p> <h1>This is timer { timer.Name } </h1>
<p>Start time: <local-date>{ timer.StartTime.AsUTCString() }</local-date></p> <p><a href="/">Back to list</a></p>
<p>End time: <local-date>{ timer.EndTime.AsUTCString() }</local-date></p> <p>Start time: <local-date>{ timer.StartTime.AsUTCString() }</local-date></p>
<p> <p>End time: <local-date>{ timer.EndTime.AsUTCString() }</local-date></p>
Total time: <p>
<timer-countdown Total time:
start={ timer.StartTime.AsUTCString() } <timer-countdown
end={ timer.EndTime.AsUTCString() } start={ timer.StartTime.AsUTCString() }
></timer-countdown> end={ timer.EndTime.AsUTCString() }
</p> ></timer-countdown>
<p> </p>
Remaining time: <p>
<timer-countdown Remaining time:
end={ timer.EndTime.AsUTCString() } <timer-countdown
></timer-countdown> end={ timer.EndTime.AsUTCString() }
</p> ></timer-countdown>
</p>
if !timer.IsFinished() {
<h3>Add time</h3>
<p>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/15m") }>15 minutes</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/30m") }>30 minutes</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/1h") }>1 hour</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/2h") }>2 hours</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/6h") }>6 hours</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/12h") }>12 hours</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/1d") }>1 day</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/1w") }>1 week</button>
<button hx-target="closest .timer" hx-post={ fmt.Sprint("/timer/", timer.Id, "/addTime/4w") }>4 weeks</button>
</p>
}
</div>
} }

View File

@ -11,6 +11,7 @@ import "io"
import "bytes" import "bytes"
import ( import (
"fmt"
"stevenlr.com/timer/model" "stevenlr.com/timer/model"
) )
@ -27,14 +28,14 @@ func TimerView(timer model.Timer) templ.Component {
templ_7745c5c3_Var1 = templ.NopComponent templ_7745c5c3_Var1 = templ.NopComponent
} }
ctx = templ.ClearChildren(ctx) ctx = templ.ClearChildren(ctx)
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h1>This is timer ") _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"timer\"><h1>This is timer ")
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
var templ_7745c5c3_Var2 string var templ_7745c5c3_Var2 string
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(timer.Name) templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(timer.Name)
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 8, Col: 34} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 10, Col: 32}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -47,7 +48,7 @@ func TimerView(timer model.Timer) templ.Component {
var templ_7745c5c3_Var3 string var templ_7745c5c3_Var3 string
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.AsUTCString()) templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.AsUTCString())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 10, Col: 62} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 12, Col: 60}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -60,7 +61,7 @@ func TimerView(timer model.Timer) templ.Component {
var templ_7745c5c3_Var4 string var templ_7745c5c3_Var4 string
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString()) templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 11, Col: 58} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 13, Col: 56}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -73,7 +74,7 @@ func TimerView(timer model.Timer) templ.Component {
var templ_7745c5c3_Var5 string var templ_7745c5c3_Var5 string
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.AsUTCString()) templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(timer.StartTime.AsUTCString())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 15, Col: 49} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 17, Col: 41}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -86,7 +87,7 @@ func TimerView(timer model.Timer) templ.Component {
var templ_7745c5c3_Var6 string var templ_7745c5c3_Var6 string
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString()) templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 16, Col: 45} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 18, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -99,7 +100,7 @@ func TimerView(timer model.Timer) templ.Component {
var templ_7745c5c3_Var7 string var templ_7745c5c3_Var7 string
templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString()) templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(timer.EndTime.AsUTCString())
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 22, Col: 45} return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 24, Col: 37}
} }
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7))
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
@ -109,6 +110,133 @@ func TimerView(timer model.Timer) templ.Component {
if templ_7745c5c3_Err != nil { if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err return templ_7745c5c3_Err
} }
if !timer.IsFinished() {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<h3>Add time</h3><p><button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var8 string
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/15m"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 30, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">15 minutes</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var9 string
templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/30m"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 31, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">30 minutes</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var10 string
templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/1h"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 32, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">1 hour</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var11 string
templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/2h"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 33, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">2 hours</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var12 string
templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/6h"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 34, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">6 hours</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var13 string
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/12h"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 35, Col: 96}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">12 hours</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var14 string
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/1d"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 36, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">1 day</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var15 string
templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/1w"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 37, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">1 week</button> <button hx-target=\"closest .timer\" hx-post=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprint("/timer/", timer.Id, "/addTime/4w"))
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `view\timer.templ`, Line: 38, Col: 95}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">4 weeks</button></p>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if !templ_7745c5c3_IsBuffer { if !templ_7745c5c3_IsBuffer {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W) _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
} }