basic account creation flow created

This commit is contained in:
2026-01-24 16:45:38 -07:00
parent 7d37fae332
commit 1c305cd494
14 changed files with 649 additions and 37 deletions
+11 -8
View File
@@ -2,10 +2,10 @@ package accounts
import ( import (
"errors" "errors"
"fmt"
"strconv" "strconv"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid"
"ruben/inventory2/internal/consts" "ruben/inventory2/internal/consts"
"ruben/inventory2/internal/domains/accounts" "ruben/inventory2/internal/domains/accounts"
@@ -31,6 +31,8 @@ func Routes(
accts: accts, accts: accts,
} }
r.POST("", response.Handler(as.createAccount))
syncGroups := r.Group("/:acctID/inventory/sync-groups") syncGroups := r.Group("/:acctID/inventory/sync-groups")
syncGroups.POST("", response.Handler(as.saveNewSyncGroup)) syncGroups.POST("", response.Handler(as.saveNewSyncGroup))
@@ -41,19 +43,17 @@ func Routes(
draftListings.DELETE("/:orderIndex", response.Handler(as.deleteSyncGroupListingDraft)) draftListings.DELETE("/:orderIndex", response.Handler(as.deleteSyncGroupListingDraft))
} }
// TODO: test this endpoint again, to make it work.
// POST / // POST /
func (s *accountSubrouter) createAccount(c *gin.Context) (response.Response, error) { func (s *accountSubrouter) createAccount(c *gin.Context) (response.Response, error) {
r := c.Request r := c.Request
ctx := r.Context() ctx := r.Context()
email := r.FormValue("email")
if email == "" { // TODO: get email from the user's account profile (how?) - will need to flesh out the account creation process later on
return nil, response.BadRequest().Msg("no email provided") email := uuid.NewString() + "@example.com"
}
userID := auth.GetIdentity(ctx).User.UserID userID := auth.GetIdentity(ctx).User.UserID
acct, err := s.accts.CreateAccount(ctx, userID, email) _, err := s.accts.CreateAccount(ctx, userID, email)
if err != nil { if err != nil {
if errors.Is(err, consts.ErrConflict) { if errors.Is(err, consts.ErrConflict) {
return nil, response.Conflict(). return nil, response.Conflict().
@@ -62,7 +62,10 @@ func (s *accountSubrouter) createAccount(c *gin.Context) (response.Response, err
return nil, response.Errorf("failed to create account: %w", err) return nil, response.Errorf("failed to create account: %w", err)
} }
return response.SeeOther(fmt.Sprintf("/ui/accounts/%d", acct.AccountID)), nil return response.StatusCreated().
// force a reload to ensure an sse connection is made.
// user should automatically be redirected to the appropriate page based on their current location and/or login/account statuses.
HXRefresh("true"), nil
} }
// POST /:acctID/inventory/sync-groups/draft/listings // POST /:acctID/inventory/sync-groups/draft/listings
+55
View File
@@ -59,6 +59,54 @@ func (b bodyRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(b) return Cookie(ck).wrap(b)
} }
func (b bodyRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(b)
}
func (b bodyRes) HXLocation(v string) Response {
return HXLocation(v).wrap(b)
}
func (b bodyRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(b)
}
func (b bodyRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(b)
}
func (b bodyRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(b)
}
func (b bodyRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(b)
}
func (b bodyRes) HXReswap(v string) Response {
return HXReswap(v).wrap(b)
}
func (b bodyRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(b)
}
func (b bodyRes) HXReselect(v string) Response {
return HXReselect(v).wrap(b)
}
func (b bodyRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(b)
}
func (b bodyRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(b)
}
func (b bodyRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(b)
}
func (b bodyRes) GetStatus() (int, bool) { func (b bodyRes) GetStatus() (int, bool) {
if b.res == nil { if b.res == nil {
return 0, false return 0, false
@@ -67,6 +115,13 @@ func (b bodyRes) GetStatus() (int, bool) {
return b.res.GetStatus() return b.res.GetStatus()
} }
func (b bodyRes) getHeaders() [][2]string {
if b.res != nil {
return b.res.getHeaders()
}
return nil
}
func (b bodyRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (b bodyRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if b.res == nil { if b.res == nil {
return 0, "", false return 0, "", false
+55
View File
@@ -58,6 +58,54 @@ func (c cookieRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(c) return Cookie(ck).wrap(c)
} }
func (c cookieRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(c)
}
func (c cookieRes) HXLocation(v string) Response {
return HXLocation(v).wrap(c)
}
func (c cookieRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(c)
}
func (c cookieRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(c)
}
func (c cookieRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(c)
}
func (c cookieRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(c)
}
func (c cookieRes) HXReswap(v string) Response {
return HXReswap(v).wrap(c)
}
func (c cookieRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(c)
}
func (c cookieRes) HXReselect(v string) Response {
return HXReselect(v).wrap(c)
}
func (c cookieRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(c)
}
func (c cookieRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(c)
}
func (c cookieRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(c)
}
func (c cookieRes) GetStatus() (int, bool) { func (c cookieRes) GetStatus() (int, bool) {
if c.res == nil { if c.res == nil {
return 0, false return 0, false
@@ -66,6 +114,13 @@ func (c cookieRes) GetStatus() (int, bool) {
return c.res.GetStatus() return c.res.GetStatus()
} }
func (c cookieRes) getHeaders() [][2]string {
if c.res != nil {
return c.res.getHeaders()
}
return nil
}
func (c cookieRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (c cookieRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if c.res == nil { if c.res == nil {
return 0, "", false return 0, "", false
+212
View File
@@ -0,0 +1,212 @@
package response
import (
"fmt"
"io"
"net/http"
"ruben/inventory2/internal/server/redirect"
)
type (
headerRes struct {
name string
value string
res Response
}
)
var _ Response = headerRes{}
func header(name, value string) Response {
return headerRes{
name: name,
value: value,
}
}
func HXPushURL(v string) Response {
return header("HX-Push-Url", v)
}
func HXLocation(v string) Response {
return header("HX-Location", v)
}
func HXPushUrl(v string) Response {
return header("HX-Push-Url", v)
}
func HXRedirect(v string) Response {
return header("HX-Redirect", v)
}
func HXRefresh(v string) Response {
return header("HX-Refresh", v)
}
func HXReplaceUrl(v string) Response {
return header("HX-Replace-Url", v)
}
func HXReswap(v string) Response {
return header("HX-Reswap", v)
}
func HXRetarget(v string) Response {
return header("HX-Retarget", v)
}
func HXReselect(v string) Response {
return header("HX-Reselect", v)
}
func HXTrigger(v string) Response {
return header("HX-Trigger", v)
}
func HXTriggerAfterSettle(v string) Response {
return header("HX-Trigger-After-Settle", v)
}
func HXTriggerAfterSwap(v string) Response {
return header("HX-Trigger-After-Swap", v)
}
func (h headerRes) String() string {
if h.res != nil {
return fmt.Sprintf(`{"header": {"name": %q, "value": %q}, "nested": %s}`, h.name, h.value, h.res)
}
return fmt.Sprintf(`{"header": {"name": %q, "value": %q}}`, h.name, h.value)
}
func (h headerRes) wrap(res Response) Response {
h.res = res
return h
}
func (h headerRes) Status(code int) Response {
return Status(code).wrap(h)
}
func (h headerRes) Header(name, value string) Response {
h.name = name
h.value = value
return h
}
func (h headerRes) Redirect(code redirect.Code, to string) Response {
return Redirect(code, to).wrap(h)
}
func (h headerRes) HTML(body []byte) Response {
return HTML(body).wrap(h)
}
func (h headerRes) JSON(body any) Response {
return JSON(body).wrap(h)
}
func (h headerRes) Body(body io.ReadCloser) Response {
return Body(body).wrap(h)
}
func (h headerRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(h)
}
func (h headerRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(h)
}
func (h headerRes) HXLocation(v string) Response {
return HXLocation(v).wrap(h)
}
func (h headerRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(h)
}
func (h headerRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(h)
}
func (h headerRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(h)
}
func (h headerRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(h)
}
func (h headerRes) HXReswap(v string) Response {
return HXReswap(v).wrap(h)
}
func (h headerRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(h)
}
func (h headerRes) HXReselect(v string) Response {
return HXReselect(v).wrap(h)
}
func (h headerRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(h)
}
func (h headerRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(h)
}
func (h headerRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(h)
}
func (h headerRes) GetStatus() (int, bool) {
if h.res == nil {
return 0, false
}
return h.res.GetStatus()
}
func (h headerRes) getHeaders() [][2]string {
hdr := [2]string{h.name, h.value}
if h.res == nil {
return [][2]string{hdr}
}
return append(h.res.getHeaders(), hdr)
}
func (h headerRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if h.res == nil {
return 0, "", false
}
return h.res.GetRedirect()
}
func (h headerRes) getBody() (body io.ReadCloser, ok bool, err error) {
if h.res == nil {
return nil, false, nil
}
return h.res.getBody()
}
func (h headerRes) getCookies() []http.Cookie {
if h.res != nil {
return h.res.getCookies()
}
return nil
}
func (h headerRes) getContentType() (contentType string, ok bool) {
if h.res != nil {
return h.res.getContentType()
}
return "", false
}
+55
View File
@@ -60,6 +60,54 @@ func (h htmlRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(h) return Cookie(ck).wrap(h)
} }
func (h htmlRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(h)
}
func (h htmlRes) HXLocation(v string) Response {
return HXLocation(v).wrap(h)
}
func (h htmlRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(h)
}
func (h htmlRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(h)
}
func (h htmlRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(h)
}
func (h htmlRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(h)
}
func (h htmlRes) HXReswap(v string) Response {
return HXReswap(v).wrap(h)
}
func (h htmlRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(h)
}
func (h htmlRes) HXReselect(v string) Response {
return HXReselect(v).wrap(h)
}
func (h htmlRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(h)
}
func (h htmlRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(h)
}
func (h htmlRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(h)
}
func (h htmlRes) GetStatus() (int, bool) { func (h htmlRes) GetStatus() (int, bool) {
if h.res == nil { if h.res == nil {
return 0, false return 0, false
@@ -68,6 +116,13 @@ func (h htmlRes) GetStatus() (int, bool) {
return h.res.GetStatus() return h.res.GetStatus()
} }
func (h htmlRes) getHeaders() [][2]string {
if h.res != nil {
return h.res.getHeaders()
}
return nil
}
func (h htmlRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (h htmlRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if h.res == nil { if h.res == nil {
return 0, "", false return 0, "", false
+55
View File
@@ -61,6 +61,54 @@ func (j jsonRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(j) return Cookie(ck).wrap(j)
} }
func (j jsonRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(j)
}
func (j jsonRes) HXLocation(v string) Response {
return HXLocation(v).wrap(j)
}
func (j jsonRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(j)
}
func (j jsonRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(j)
}
func (j jsonRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(j)
}
func (j jsonRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(j)
}
func (j jsonRes) HXReswap(v string) Response {
return HXReswap(v).wrap(j)
}
func (j jsonRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(j)
}
func (j jsonRes) HXReselect(v string) Response {
return HXReselect(v).wrap(j)
}
func (j jsonRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(j)
}
func (j jsonRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(j)
}
func (j jsonRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(j)
}
func (j jsonRes) GetStatus() (int, bool) { func (j jsonRes) GetStatus() (int, bool) {
if j.res == nil { if j.res == nil {
return 0, false return 0, false
@@ -69,6 +117,13 @@ func (j jsonRes) GetStatus() (int, bool) {
return j.res.GetStatus() return j.res.GetStatus()
} }
func (j jsonRes) getHeaders() [][2]string {
if j.res != nil {
return j.res.getHeaders()
}
return nil
}
func (j jsonRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (j jsonRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if j.res == nil { if j.res == nil {
return 0, "", false return 0, "", false
+55
View File
@@ -99,6 +99,54 @@ func (r redirectRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(r) return Cookie(ck).wrap(r)
} }
func (r redirectRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(r)
}
func (r redirectRes) HXLocation(v string) Response {
return HXLocation(v).wrap(r)
}
func (r redirectRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(r)
}
func (r redirectRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(r)
}
func (r redirectRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(r)
}
func (r redirectRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(r)
}
func (r redirectRes) HXReswap(v string) Response {
return HXReswap(v).wrap(r)
}
func (r redirectRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(r)
}
func (r redirectRes) HXReselect(v string) Response {
return HXReselect(v).wrap(r)
}
func (r redirectRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(r)
}
func (r redirectRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(r)
}
func (r redirectRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(r)
}
func (r redirectRes) GetStatus() (int, bool) { func (r redirectRes) GetStatus() (int, bool) {
if r.res == nil { if r.res == nil {
return 0, false return 0, false
@@ -107,6 +155,13 @@ func (r redirectRes) GetStatus() (int, bool) {
return r.res.GetStatus() return r.res.GetStatus()
} }
func (r redirectRes) getHeaders() [][2]string {
if r.res != nil {
return r.res.getHeaders()
}
return nil
}
func (r redirectRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (r redirectRes) GetRedirect() (code redirect.Code, to string, ok bool) {
return r.code, r.to, true return r.code, r.to, true
} }
+13
View File
@@ -10,6 +10,18 @@ import (
type ( type (
Response interface { Response interface {
Status(int) Response Status(int) Response
HXPushURL(string) Response
HXLocation(string) Response
HXPushUrl(string) Response
HXRedirect(string) Response
HXRefresh(string) Response
HXReplaceUrl(string) Response
HXReswap(string) Response
HXRetarget(string) Response
HXReselect(string) Response
HXTrigger(string) Response
HXTriggerAfterSettle(string) Response
HXTriggerAfterSwap(string) Response
Redirect(code redirect.Code, to string) Response Redirect(code redirect.Code, to string) Response
Body(io.ReadCloser) Response Body(io.ReadCloser) Response
HTML([]byte) Response HTML([]byte) Response
@@ -21,6 +33,7 @@ type (
GetRedirect() (code redirect.Code, to string, ok bool) GetRedirect() (code redirect.Code, to string, ok bool)
getCookies() []http.Cookie getCookies() []http.Cookie
getContentType() (contentType string, ok bool) getContentType() (contentType string, ok bool)
getHeaders() [][2]string
wrap(Response) Response wrap(Response) Response
} }
+55
View File
@@ -76,10 +76,65 @@ func (s statusRes) Cookie(ck http.Cookie) Response {
return Cookie(ck).wrap(s) return Cookie(ck).wrap(s)
} }
func (s statusRes) HXPushURL(v string) Response {
return HXPushURL(v).wrap(s)
}
func (s statusRes) HXLocation(v string) Response {
return HXLocation(v).wrap(s)
}
func (s statusRes) HXPushUrl(v string) Response {
return HXPushUrl(v).wrap(s)
}
func (s statusRes) HXRedirect(v string) Response {
return HXRedirect(v).wrap(s)
}
func (s statusRes) HXRefresh(v string) Response {
return HXRefresh(v).wrap(s)
}
func (s statusRes) HXReplaceUrl(v string) Response {
return HXReplaceUrl(v).wrap(s)
}
func (s statusRes) HXReswap(v string) Response {
return HXReswap(v).wrap(s)
}
func (s statusRes) HXRetarget(v string) Response {
return HXRetarget(v).wrap(s)
}
func (s statusRes) HXReselect(v string) Response {
return HXReselect(v).wrap(s)
}
func (s statusRes) HXTrigger(v string) Response {
return HXTrigger(v).wrap(s)
}
func (s statusRes) HXTriggerAfterSettle(v string) Response {
return HXTriggerAfterSettle(v).wrap(s)
}
func (s statusRes) HXTriggerAfterSwap(v string) Response {
return HXTriggerAfterSwap(v).wrap(s)
}
func (s statusRes) GetStatus() (int, bool) { func (s statusRes) GetStatus() (int, bool) {
return s.code, true return s.code, true
} }
func (s statusRes) getHeaders() [][2]string {
if s.res != nil {
return s.res.getHeaders()
}
return nil
}
func (s statusRes) GetRedirect() (code redirect.Code, to string, ok bool) { func (s statusRes) GetRedirect() (code redirect.Code, to string, ok bool) {
if s.res == nil { if s.res == nil {
return 0, "", false return 0, "", false
+5 -1
View File
@@ -72,12 +72,16 @@ func writeResponse(c *gin.Context, res Response) {
// w.Header() must be set before ResponseWriter.WriteHeader is called // w.Header() must be set before ResponseWriter.WriteHeader is called
// or redirect is attempted // or redirect is attempted
hdrs := w.Header() hdrs := w.Header()
for _, hdr := range res.getHeaders() {
hdrs.Add(hdr[0], hdr[1])
}
for _, ck := range res.getCookies() { for _, ck := range res.getCookies() {
hdrs.Add("Set-Cookie", ck.String()) hdrs.Add("Set-Cookie", ck.String())
} }
if ct, ok := res.getContentType(); ok { if ct, ok := res.getContentType(); ok {
hdrs.Add("Content-Type", ct) hdrs.Set("Content-Type", ct)
} }
if code, to, ok := res.GetRedirect(); ok { if code, to, ok := res.GetRedirect(); ok {
+37 -2
View File
@@ -93,10 +93,19 @@ func Routes(
etsy: etsy, etsy: etsy,
} }
r.GET("", response.Handler(s.serveTemplate)) r.GET("", response.Handler(s.redirectToAccountsIfLoggedInWithAnAccount), response.Handler(s.serveTemplate))
r.GET("/*rest", authenticate, response.Handler(s.serveTemplate)) r.GET("/*rest", authenticate, response.Handler(s.serveTemplate))
} }
func (s *webpageRouter) redirectToAccountsIfLoggedInWithAnAccount(c *gin.Context) (response.Response, error) {
id := auth.GetIdentity(c)
if id.Account == nil || id.Account.AccountID == 0 {
return nil, nil
}
return response.TemporaryRedirect(fmt.Sprintf("%s/accounts/%d", s.uiPath, id.Account.AccountID)), nil
}
// GET / // GET /
// compiles the page template or component template matching the url // compiles the page template or component template matching the url
func (s *webpageRouter) serveTemplate(c *gin.Context) (response.Response, error) { func (s *webpageRouter) serveTemplate(c *gin.Context) (response.Response, error) {
@@ -221,7 +230,33 @@ func authorizeByMatchingAccountID(r *http.Request, acctIDPathPosition int) error
id := auth.GetIdentity(r.Context()) id := auth.GetIdentity(r.Context())
if id.Account == nil || id.Account.AccountID != acctID { if id.Account == nil || id.Account.AccountID != acctID {
return response.Unauthorized(). return response.Unauthorized().
Msgf("user does not have access to account %d", acctID) HTML([]byte(fmt.Sprintf(`
<section class="text-center">
<header class="m-[1em]">
<h2>
Access Not Granted
</h2>
<a href="/ui%s" class="block m-[1em]">
<code>
/ui%s
</code>
</a>
</header>
<div class="m-[2em]">
<p class="italic font-bold">
Sorry, you don't have access to the given page.
</p>
</div>
</section>
<section class="text-center">
<a href="/">
Return to app
</a>
</section>
`, r.URL, r.URL)))
} }
return nil return nil
+9 -18
View File
@@ -1,3 +1,7 @@
{{- $loggedIn := and (and .Identity .Identity.AccessToken) true -}}
{{- $hasAccount := and (and .Identity .Identity.Account) true -}}
<!DOCTYPE html> <!DOCTYPE html>
<html class="min-h-full flex flex-col items-stretch"> <html class="min-h-full flex flex-col items-stretch">
<head> <head>
@@ -36,7 +40,7 @@
hx-boost="true" hx-boost="true"
{{/* subscribe to sse (if logged in) */}} {{/* subscribe to sse (if logged in) */}}
{{- if and .Identity .Identity.AccessToken }} {{- if and $loggedIn $hasAccount }}
sse-connect="/api/events/" sse-connect="/api/events/"
sse-close="close" sse-close="close"
{{- end }} {{- end }}
@@ -69,29 +73,16 @@
style=" style="
" "
> >
<li class="pt-[1em] pb-[1em]"> {{- if not $loggedIn }}
<a href="/ui" class="p-[1em] font-display {{ if eq $path "/" }}selected{{ end }}">
Home
</a>
</li>
{{/* */}}
{{- $userID := .Identity.User.UserID }}
{{- $acct := .Identity.Account }}
{{- $acctID := 0 }}
{{- if $acct }}
{{- $acctID = $acct.AccountID }}
{{- end }}
{{- if not $userID }}
<li class="pt-[1em] pb-[1em]"> <li class="pt-[1em] pb-[1em]">
<a href="/api/auth/login" hx-boost="false" class="p-[1em] font-display {{ if eq $path "/auth/login" }}selected{{ end }}"> <a href="/api/auth/login" hx-boost="false" class="p-[1em] font-display {{ if eq $path "/auth/login" }}selected{{ end }}">
Log In / Sign Up Log In
</a> </a>
</li> </li>
{{- else if $acctID }} {{- else if $hasAccount }}
{{- $acctID := .Identity.Account.AccountID }}
<li class="pt-[1em] pb-[1em]"> <li class="pt-[1em] pb-[1em]">
<a href="/ui/accounts/{{$acctID}}" class="p-[1em] font-display {{ if eq $path (printf "/accounts/%d" $acctID) }}selected{{ end }}"> <a href="/ui/accounts/{{$acctID}}" class="p-[1em] font-display {{ if eq $path (printf "/accounts/%d" $acctID) }}selected{{ end }}">
+10 -6
View File
@@ -1,3 +1,6 @@
{{- $loggedIn := and (and .Identity .Identity.AccessToken) true -}}
<section class="flex justify-center max-w-full mt-[3em] mb-[3em]"> <section class="flex justify-center max-w-full mt-[3em] mb-[3em]">
<h1 class=" <h1 class="
min-[400px]:px-[calc(10vw-0.5em)] min-[400px]:px-[calc(10vw-0.5em)]
@@ -12,20 +15,21 @@
</section> </section>
{{- if and .Identity.User.UserID (not .Identity.Account) }}
{{/* TODO: make account creation automatic as part of the user creation process */}}
<h2><a href="/ui/account-creation">New Account</a></h2>
{{- end }}
<section class="flex flex-col items-center max-w-full mt-[1em] mb-[1em] gap-y-[2em]"> <section class="flex flex-col items-center max-w-full mt-[1em] mb-[1em] gap-y-[2em]">
<p class="text-lg text-center max-w-[50%]"> <p class="text-lg text-center max-w-[50%]">
Synchronize inventory automatically! Synchronize inventory automatically!
</p> </p>
<div class="border-button border-sidebar-border bg-card text-lg text-center max-w-[50%]"> <div class="border-button border-sidebar-border bg-card text-lg text-center max-w-[50%]">
<a href="/api/auth/login" hx-boost="false" class="block p-[1em] font-bold"> {{- if $loggedIn }}
<a hx-post="/api/accounts" hx-target="body" class="block p-[1em] font-bold">
Create an Account Create an Account
</a> </a>
{{- else }}
<a href="/api/auth/login" hx-boost="false" class="block p-[1em] font-bold">
New Login
</a>
{{- end }}
</div> </div>
<p class="text-lg text-center max-w-[50%]"> <p class="text-lg text-center max-w-[50%]">
+20
View File
@@ -0,0 +1,20 @@
<section class="text-center">
<header class="m-[1em]">
<h2>
Access Not Granted
</h2>
{{ $link := printf "/ui%s" .Request.URL }}
<a href="{{ $link }}" class="block m-[1em]">
<code>
{{ $link }}
</code>
</a>
</header>
<div class="m-[2em]">
<p class="italic font-bold">
Sorry, you don't have access to the page you were looking for.
</p>
</div>
</section>