diff --git a/cmd/web/handlers_admin.go b/cmd/web/handlers_admin.go index 4adc7ec..ba47bf6 100644 --- a/cmd/web/handlers_admin.go +++ b/cmd/web/handlers_admin.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/http" + "strconv" "time" "git.32bit.cafe/32bitcafe/guestbook/internal/forms" @@ -13,8 +14,28 @@ import ( ) func (app *application) getAdminPanelLanding(w http.ResponseWriter, r *http.Request) { + websites, err := app.websites.GetCount() + if err != nil { + app.serverError(w, r, err) + return + } + users, err := app.users.GetCount() + if err != nil { + app.serverError(w, r, err) + return + } + comments, err := app.guestbookComments.GetCount() + if err != nil { + app.serverError(w, r, err) + return + } + stats := views.AdminStat{ + WebsiteCount: websites, + UserCount: users, + CommentCount: comments, + } data := app.newCommonData(r) - views.AdminPanelLandingView("Admin Panel", data).Render(r.Context(), w) + views.AdminPanelLandingView("Admin Panel", data, stats).Render(r.Context(), w) } func (app *application) getAdminPanelAllUsers(w http.ResponseWriter, r *http.Request) { @@ -158,4 +179,70 @@ func (app *application) putAdminPanelUnbanUser(w http.ResponseWriter, r *http.Re } func (app *application) getAdminPanelWebsites(w http.ResponseWriter, r *http.Request) { + page := r.URL.Query().Get("page") + count := r.URL.Query().Get("count") + var pageNum int64 = 1 + var pageSize int64 = 5 + var err error + if page != "" { + pageNum, err = strconv.ParseInt(page, 10, 0) + if err != nil { + app.clientError(w, http.StatusBadRequest) + return + } + } + if count != "" { + pageSize, err = strconv.ParseInt(count, 10, 0) + if err != nil { + app.clientError(w, http.StatusBadRequest) + return + } + } + websites, err := app.websites.GetAllPage(pageNum, pageSize) + if err != nil { + app.serverError(w, r, err) + return + } + total, err := app.websites.GetCount() + if err != nil { + app.serverError(w, r, err) + return + } + commonData := app.newCommonData(r) + views.AdminPanelAllWebsitesView("All websites", commonData, websites, pageNum, total).Render(r.Context(), w) +} + +func (app *application) getAdminPanelWebsiteDetails(w http.ResponseWriter, r *http.Request) { + slug := r.PathValue("id") + // page := r.URL.Query().Get("page") + // count := r.URL.Query().Get("count") + // var pageNum int64 = 1 + // var pageSize int64 = 5 + // var err error + // if page != "" { + // pageNum, err = strconv.ParseInt(page, 10, 0) + // if err != nil { + // app.clientError(w, http.StatusBadRequest) + // return + // } + // } + // if count != "" { + // pageSize, err = strconv.ParseInt(count, 10, 0) + // if err != nil { + // app.clientError(w, http.StatusBadRequest) + // return + // } + // } + website, err := app.websites.Get(slugToShortId(slug)) + if err != nil { + app.serverError(w, r, err) + return + } + comments, err := app.guestbookComments.GetAll(website.Guestbook.ID) + if err != nil { + app.serverError(w, r, err) + return + } + commonData := app.newCommonData(r) + views.AdminPanelWebsiteDetailView(fmt.Sprintf("Admin - %s", website.Name), commonData, website, comments).Render(r.Context(), w) } diff --git a/cmd/web/routes.go b/cmd/web/routes.go index 1f843cf..932e778 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -67,6 +67,8 @@ func (app *application) routes() http.Handler { mux.Handle("PUT /admin/users/{id}/edit", adminOnly.ThenFunc(app.putAdminPanelUserMgmtForm)) mux.Handle("PUT /admin/users/{id}/ban", adminOnly.ThenFunc(app.putAdminPanelBanUser)) mux.Handle("PUT /admin/users/{id}/unban", adminOnly.ThenFunc(app.putAdminPanelUnbanUser)) + mux.Handle("GET /admin/websites", adminOnly.ThenFunc(app.getAdminPanelWebsites)) + mux.Handle("GET /admin/websites/{id}", adminOnly.ThenFunc(app.getAdminPanelWebsiteDetails)) return standard.Then(mux) } diff --git a/internal/models/guestbookcomment.go b/internal/models/guestbookcomment.go index eb94614..00ba1a4 100644 --- a/internal/models/guestbookcomment.go +++ b/internal/models/guestbookcomment.go @@ -33,6 +33,7 @@ type GuestbookCommentModel struct { type GuestbookCommentModelInterface interface { Insert(shortId uint64, guestbookId, parentId int64, authorName, authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error) Get(shortId uint64) (GuestbookComment, error) + GetCount() (int64, error) GetVisible(guestbookId int64) ([]GuestbookComment, error) GetVisibleSerialized(guestbookId int64) ([]GuestbookCommentSerialized, error) GetDeleted(guestbookId int64) ([]GuestbookComment, error) @@ -74,6 +75,20 @@ func (m *GuestbookCommentModel) Get(shortId uint64) (GuestbookComment, error) { return c, nil } +func (m *GuestbookCommentModel) GetCount() (int64, error) { + stmt := `SELECT COUNT(*) FROM guestbook_comments WHERE Deleted IS NULL` + row := m.DB.QueryRow(stmt) + var result int64 + err := row.Scan(&result) + if err != nil { + return -1, err + } + if err = row.Err(); err != nil { + return -1, err + } + return result, nil +} + func (m *GuestbookCommentModel) GetVisible(guestbookId int64) ([]GuestbookComment, error) { stmt := `SELECT Id, ShortId, GuestbookId, ParentId, AuthorName, AuthorEmail, AuthorSite, CommentText, PageUrl, Created, IsPublished diff --git a/internal/models/mocks/website.go b/internal/models/mocks/website.go index bc50020..0fed223 100644 --- a/internal/models/mocks/website.go +++ b/internal/models/mocks/website.go @@ -56,6 +56,14 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]models.Website, error) { return []models.Website{mockWebsite}, nil } +func GetCountUser(userId int64) (int64, error) { + return 1, nil +} + +func (m *WebsiteModel) GetAllUserPage(userId int64, pageNum int64) ([]models.Website, error) { + return []models.Website{mockWebsite}, nil +} + func (m *WebsiteModel) GetById(id int64) (models.Website, error) { switch id { case 1: @@ -69,6 +77,14 @@ func (m *WebsiteModel) GetAll() ([]models.Website, error) { return []models.Website{mockWebsite}, nil } +func GetCount() (int64, error) { + return 1, nil +} + +func (m *WebsiteModel) GetAllPage(pageNum int64) ([]models.Website, error) { + return []models.Website{mockWebsite}, nil +} + func (m *WebsiteModel) Update(w models.Website) error { return nil } diff --git a/internal/models/user.go b/internal/models/user.go index 1e70241..a7b4e78 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -56,6 +56,7 @@ type UserModelInterface interface { Insert(shortId uint64, username string, email string, password string, settings UserSettings) error InsertWithoutPassword(shortId uint64, username string, email string, subject string, settings UserSettings) (int64, error) Get(shortId uint64) (User, error) + GetCount() (int64, error) GetById(id int64) (User, error) GetByEmail(email string) (int64, error) GetBySubject(subject string) (int64, error) @@ -247,6 +248,20 @@ func (m *UserModel) Get(shortId uint64) (User, error) { return u, nil } +func (m *UserModel) GetCount() (int64, error) { + stmt := `SELECT COUNT(*) FROM users WHERE Deleted IS NULL` + row := m.DB.QueryRow(stmt) + var result int64 + err := row.Scan(&result) + if err != nil { + return -1, err + } + if err = row.Err(); err != nil { + return -1, err + } + return result, nil +} + func (m *UserModel) GetById(id int64) (User, error) { stmt := `SELECT Id, ShortId, Username, Email, Created, Banned FROM users WHERE Id = ? AND Deleted IS NULL` tx, err := m.DB.Begin() diff --git a/internal/models/website.go b/internal/models/website.go index 9d29399..1d0048d 100644 --- a/internal/models/website.go +++ b/internal/models/website.go @@ -95,7 +95,11 @@ type WebsiteModelInterface interface { Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) Get(shortId uint64) (Website, error) GetAllUser(userId int64) ([]Website, error) + GetCountUser(userId int64) (int64, error) + GetAllUserPage(userId int64, pageNum int64, pageSize int64) ([]Website, error) GetAll() ([]Website, error) + GetCount() (int64, error) + GetAllPage(pageNum int64, pageSize int64) ([]Website, error) Update(w Website) error InitializeSettingsMap() error UpdateGuestbookSettings(guestbookId int64, settings GuestbookSettings) error @@ -270,6 +274,51 @@ func (m *WebsiteModel) GetAllUser(userId int64) ([]Website, error) { return websites, nil } +func (m *WebsiteModel) GetCountUser(userId int64) (int64, error) { + stmt := `SELECT COUNT(*) FROM websites WHERE UserId = ? AND Deleted IS NULL` + row := m.DB.QueryRow(stmt, userId) + var result int64 + err := row.Scan(&result) + if err != nil { + return -1, err + } + if err = row.Err(); err != nil { + return -1, err + } + return result, nil +} + +func (m *WebsiteModel) GetAllUserPage(userId int64, pageNum int64, pageSize int64) ([]Website, error) { + stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created, + g.Id, g.ShortId, g.Created, g.IsActive + FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId + WHERE w.UserId = ? AND w.Deleted IS NULL + LIMIT ? OFFSET ?` + rows, err := m.DB.Query(stmt, userId, pageSize, (pageNum-1)*pageSize) + if err != nil { + return nil, err + } + var websites []Website + for rows.Next() { + var w Website + var u string + err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created, + &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive) + if err != nil { + return nil, err + } + w.Url, err = url.Parse(u) + if err != nil { + return nil, err + } + websites = append(websites, w) + } + if err = rows.Err(); err != nil { + return nil, err + } + return websites, nil +} + func (m *WebsiteModel) GetAll() ([]Website, error) { stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created, g.Id, g.ShortId, g.Created, g.IsActive @@ -299,6 +348,50 @@ func (m *WebsiteModel) GetAll() ([]Website, error) { return websites, nil } +func (m *WebsiteModel) GetCount() (int64, error) { + stmt := `SELECT COUNT(*) FROM websites WHERE Deleted IS NULL` + row := m.DB.QueryRow(stmt) + var result int64 + err := row.Scan(&result) + if err != nil { + return -1, err + } + if err = row.Err(); err != nil { + return -1, err + } + return result, nil +} + +func (m *WebsiteModel) GetAllPage(pageNum int64, pageSize int64) ([]Website, error) { + stmt := `SELECT w.Id, w.ShortId, w.Name, w.SiteUrl, w.AuthorName, w.UserId, w.Created, + g.Id, g.ShortId, g.Created, g.IsActive + FROM websites AS w INNER JOIN guestbooks AS g ON w.Id = g.WebsiteId WHERE w.Deleted IS NULL + LIMIT ? OFFSET ?` + rows, err := m.DB.Query(stmt, pageSize, (pageNum-1)*pageSize) + if err != nil { + return nil, err + } + var websites []Website + for rows.Next() { + var w Website + var u string + err := rows.Scan(&w.ID, &w.ShortId, &w.Name, &u, &w.AuthorName, &w.UserId, &w.Created, + &w.Guestbook.ID, &w.Guestbook.ShortId, &w.Guestbook.Created, &w.Guestbook.IsActive) + if err != nil { + return nil, err + } + w.Url, err = url.Parse(u) + if err != nil { + return nil, err + } + websites = append(websites, w) + } + if err = rows.Err(); err != nil { + return nil, err + } + return websites, nil +} + func (m *WebsiteModel) Update(w Website) error { stmt := `UPDATE websites SET Name = ?, SiteUrl = ?, AuthorName = ? WHERE ID = ?` r, err := m.DB.Exec(stmt, w.Name, w.Url.String(), w.AuthorName, w.ID) diff --git a/ui/static/css/style.css b/ui/static/css/style.css index af544a5..7872d7a 100644 --- a/ui/static/css/style.css +++ b/ui/static/css/style.css @@ -709,6 +709,54 @@ ul#websites li { margin-top: var(--space-2xl); } +/* Admin Panel */ +.admin-section { + background-color: var(--color-surface); + padding: var(--space-xl); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + margin-bottom: var(--space-xl); +} + +.admin-section table { + width: 100%; +} + +.admin-section th { + border-bottom: 1px solid var(--color-border); +} + +.admin-section td { + padding: var(--space-sm); + text-align: center; +} + +.admin-flex { + display: flex; + justify-content: space-between; + gap: var(--space-sm); +} + +.admin-info { + flex: 1; + text-align: center; + font-size: x-large; +} + +/* Pagination */ +.pagination { + width: 100%; + display: flex; + justify-content: center; +} +.pagination-btn { + background-color: var(--color-surface); + padding: var(--space-sm); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + margin: var(--space-xs); +} + /* Utilities */ hr { border: none; diff --git a/ui/views/admin.templ b/ui/views/admin.templ index f8ab1c3..e94dad8 100644 --- a/ui/views/admin.templ +++ b/ui/views/admin.templ @@ -8,6 +8,12 @@ import ( "time" ) +type AdminStat struct { + WebsiteCount int64 + UserCount int64 + CommentCount int64 +} + templ adminBase(title string, data CommonData) { @@ -41,20 +47,36 @@ templ adminSidebar() {
} -templ AdminPanelLandingView(title string, data CommonData) { +templ AdminPanelLandingView(title string, data CommonData, stats AdminStat) { @adminBase(title, data) {Welcome to the admin panel
+Welcome to the admin panel
+{ fmt.Sprintf("%d", stats.UserCount) }
+{ fmt.Sprintf("%d", stats.WebsiteCount) }
+{ fmt.Sprintf("%d", stats.CommentCount) }
+| Username | Joined | @@ -180,3 +202,62 @@ templ AdminPanelUserMgmtEditForm(csrfToken string, form forms.AdminUserMgmtForm, } + +templ AdminPanelAllWebsitesView(title string, data CommonData, websites []models.Website, pageNum int64, total int64) { + @adminBase(title, data) { +
|---|
| Site Name | +Owner | +URL | +Created | +Guestbook | + for _, w := range websites { +
|---|---|---|---|---|
| { w.Name } | +{ w.AuthorName } | +{ w.Url.String() } | +{ w.Created.Format(time.RFC1123) } | + {{ gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) }} +View | +
| Author | +Created | +Homepage | +Comment | + for _, c := range comments { +|
|---|---|---|---|---|
| { c.AuthorName } | +{ c.Created.Format(time.RFC1123) } | +{ c.AuthorEmail } | +{ c.AuthorSite } | +{ c.CommentText } | +
Welcome to the admin panel
Welcome to the admin panel
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", stats.UserCount)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 69, Col: 45} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", stats.WebsiteCount)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 73, Col: 48} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var10 string + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(fmt.Sprintf("%d", stats.CommentCount)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 77, Col: 48} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
| Username | Joined |
|---|
| Username | Joined | |||
|---|---|---|---|---|
| ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var11 string - templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(u.Username) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(u.Username) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 76, Col: 51} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 98, Col: 51} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, " | ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, " | ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(u.Created.Format(time.RFC3339)) + var templ_7745c5c3_Var15 string + templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(u.Created.Format(time.RFC3339)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 77, Col: 44} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 99, Col: 44} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " | ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, " | ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var13 string - templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(u.Email) + var templ_7745c5c3_Var16 string + templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(u.Email) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 78, Col: 21} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 100, Col: 21} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, " |
| Site Name | Owner | URL | Created | Guestbook | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, w := range websites { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 55, "
|---|---|---|---|---|
| ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var39 string + templ_7745c5c3_Var39, templ_7745c5c3_Err = templ.JoinStringErrs(w.Name) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 221, Col: 57} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var39)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 58, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var40 string + templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(w.AuthorName) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 222, Col: 26} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 59, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var42 string + templ_7745c5c3_Var42, templ_7745c5c3_Err = templ.JoinStringErrs(w.Url.String()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 223, Col: 70} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var42)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var43 string + templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(w.Created.Format(time.RFC1123)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 224, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + gbUrl := fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(w.ShortId)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 63, "View |
| Author | Created | Homepage | Comment | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, c := range comments { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 69, "|
|---|---|---|---|---|
| ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var47 string + templ_7745c5c3_Var47, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorName) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 251, Col: 26} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var47)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 70, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var48 string + templ_7745c5c3_Var48, templ_7745c5c3_Err = templ.JoinStringErrs(c.Created.Format(time.RFC1123)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 252, Col: 44} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var48)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 71, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var49 string + templ_7745c5c3_Var49, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorEmail) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 253, Col: 27} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var49)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 72, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var50 string + templ_7745c5c3_Var50, templ_7745c5c3_Err = templ.JoinStringErrs(c.AuthorSite) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 254, Col: 26} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var50)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 73, " | ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var51 string + templ_7745c5c3_Var51, templ_7745c5c3_Err = templ.JoinStringErrs(c.CommentText) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `ui/views/admin.templ`, Line: 255, Col: 27} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var51)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 74, " |