xtablo-source/backend/templates/discussion_forms.go
Arthur Belleville 7388418aed
feat(19): restyle tablo cards to production design with X/Y task count
- TabloCardView gains DoneTasks/TotalTasks int fields; handler stores raw counts alongside Progress %
- TabloProjectCard: rounded-2xl card, border-[#EAECF0], purple-50 status badge, colored avatar initial (12x12 rounded-xl), calendar date row, Progression label + X/Y task count + purple-500 progress bar
- List row: matching pill badge + X/Y count
- All 7 TestTablosDashboard_* tests pass

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-05-17 17:00:51 +02:00

137 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package templates
import (
"strconv"
"time"
"backend/internal/db/sqlc"
"github.com/google/uuid"
)
const DiscussionMaxBodyLength = 10000
type DiscussionForm struct {
Body string
}
type DiscussionErrors struct {
Body string
General string
}
type DiscussionMessageView struct {
ID uuid.UUID
AuthorEmail string
Body string
CreatedAt time.Time
IsOwn bool
}
type DiscussionTabData struct {
Messages []DiscussionMessageView
CurrentUserID uuid.UUID
}
type TabloCardView struct {
Tablo sqlc.Tablo
DiscussionUnreadCount int64
Progress int // 0100; 0 when no tasks (D-05)
DoneTasks int // raw count of done tasks
TotalTasks int // raw count of all tasks
}
func DiscussionPostURL(tabloID uuid.UUID) string {
return "/tablos/" + tabloID.String() + "/discussion/messages"
}
func DiscussionMaxBodyLengthString() string {
return strconv.Itoa(DiscussionMaxBodyLength)
}
func DiscussionURL(tabloID uuid.UUID) string {
return "/tablos/" + tabloID.String() + "/discussion"
}
func DiscussionStreamURL(tabloID uuid.UUID) string {
return "/tablos/" + tabloID.String() + "/discussion/stream"
}
func DiscussionMessagesFromRows(rows []sqlc.ListDiscussionMessagesByTabloRow, currentUserID uuid.UUID) []DiscussionMessageView {
messages := make([]DiscussionMessageView, 0, len(rows))
for _, row := range rows {
messages = append(messages, DiscussionMessageView{
ID: row.ID,
AuthorEmail: row.AuthorEmail,
Body: row.Body,
CreatedAt: row.CreatedAt.Time,
IsOwn: row.AuthorUserID == currentUserID,
})
}
return messages
}
func DiscussionMessageFromRow(row sqlc.GetDiscussionMessageWithAuthorRow, currentUserID uuid.UUID) DiscussionMessageView {
return DiscussionMessageView{
ID: row.ID,
AuthorEmail: row.AuthorEmail,
Body: row.Body,
CreatedAt: row.CreatedAt.Time,
IsOwn: row.AuthorUserID == currentUserID,
}
}
func TabloCardsFromUnreadRows(rows []sqlc.ListTablosByUserWithDiscussionUnreadRow) []TabloCardView {
cards := make([]TabloCardView, 0, len(rows))
for _, row := range rows {
cards = append(cards, TabloCardView{
Tablo: sqlc.Tablo{
ID: row.ID,
UserID: row.UserID,
Title: row.Title,
Description: row.Description,
Color: row.Color,
CreatedAt: row.CreatedAt,
UpdatedAt: row.UpdatedAt,
Status: row.Status,
},
DiscussionUnreadCount: row.DiscussionUnreadCount,
})
}
return cards
}
func TabloCardFromTablo(tablo sqlc.Tablo) TabloCardView {
return TabloCardView{Tablo: tablo}
}
func DiscussionUnreadDisplay(count int64) string {
if count > 99 {
return "99+"
}
return strconv.FormatInt(count, 10)
}
func DiscussionUnreadAriaLabel(count int64) string {
if count == 1 {
return "1 unread discussion message"
}
return strconv.FormatInt(count, 10) + " unread discussion messages"
}
func DiscussionDateLabel(t time.Time) string {
return t.Local().Format("January 2, 2006")
}
func DiscussionTimestampLabel(t time.Time) string {
return t.Local().Format("January 2, 2006 15:04")
}
func DiscussionShowDaySeparator(messages []DiscussionMessageView, index int) bool {
if index == 0 {
return true
}
current := messages[index].CreatedAt.Local()
previous := messages[index-1].CreatedAt.Local()
return current.Year() != previous.Year() || current.YearDay() != previous.YearDay()
}