- 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>
137 lines
3.4 KiB
Go
137 lines
3.4 KiB
Go
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 // 0–100; 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()
|
||
}
|