package views import ( "fmt" "net/url" "strings" "xtablo-backend/internal/web/ui" ) type TabloCardView struct { ID string Name string Status string StatusLabel string StatusClass string StatusTone string Progress int CreatedAtLabel string CardDateLabel string ProgressLabel string DeleteURL string DeleteRequestURL string IconKind string IconBgClass string IconFgClass string Accent string Initial string } type TablosPageViewModel struct { DisplayName string View string Query string Status string ModalOpen bool FormName string ErrorMessage string Tablos []TabloCardView } func NewTablosPageViewModel(displayName string, view string, query string, status string, modalOpen bool, formName string, errorMessage string, tablos []TabloCardView) TablosPageViewModel { return TablosPageViewModel{ DisplayName: displayName, View: normalizedView(view), Query: strings.TrimSpace(query), Status: normalizedStatus(status), ModalOpen: modalOpen, FormName: strings.TrimSpace(formName), ErrorMessage: strings.TrimSpace(errorMessage), Tablos: tablos, } } func (vm TablosPageViewModel) IsGridView() bool { return vm.View != "list" } func (vm TablosPageViewModel) HasTablos() bool { return len(vm.Tablos) > 0 } func (vm TablosPageViewModel) StatusHref(status string) string { values := vm.baseValues() values.Set("status", normalizedStatus(status)) return "/tablos?" + values.Encode() } func (vm TablosPageViewModel) ViewHref(view string) string { values := vm.baseValues() values.Set("view", normalizedView(view)) return "/tablos?" + values.Encode() } func (vm TablosPageViewModel) SearchHref() string { return "/tablos" } func (vm TablosPageViewModel) HiddenStateFields() map[string]string { return map[string]string{ "view": vm.View, "status": vm.Status, "q": vm.Query, } } func (vm TablosPageViewModel) SearchValues() string { return fmt.Sprintf("view=%s&status=%s", vm.View, vm.Status) } func (vm TablosPageViewModel) CreateModalHref() string { values := vm.baseValues() values.Set("modal", "create") return "/tablos?" + values.Encode() } func (vm TablosPageViewModel) CloseModalHref() string { values := vm.baseValues() return "/tablos?" + values.Encode() } func (vm TablosPageViewModel) HasSearch() bool { return vm.Query != "" } func normalizedView(view string) string { if view == "list" { return "list" } return "grid" } func normalizedStatus(status string) string { switch status { case "todo", "in_progress", "done": return status default: return "all" } } func (vm TablosPageViewModel) baseValues() url.Values { values := url.Values{} values.Set("view", vm.View) values.Set("status", vm.Status) if vm.Query != "" { values.Set("q", vm.Query) } return values } func gridToggleClass(active bool) string { if active { return "flex items-center gap-2 pb-3 border-b-2 transition-colors border-purple-600 text-purple-600 dark:border-purple-400 dark:text-purple-400 font-semibold" } return "flex items-center gap-2 pb-3 border-b-2 transition-colors border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200" } func listToggleClass(gridActive bool) string { return gridToggleClass(!gridActive) } func statusPillClass(active bool) string { if active { return "flex items-center gap-1.5 px-4 py-2.5 border rounded-[8px] font-medium text-sm transition-colors border-purple-600 bg-purple-50 dark:bg-purple-950/30 text-purple-600 dark:text-purple-400" } return "flex items-center gap-1.5 px-4 py-2.5 border rounded-[8px] font-medium text-sm transition-colors border-[#EAECF0] dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800 text-gray-700 dark:text-gray-300" } func badgeVariantForTone(tone string) ui.BadgeVariant { switch tone { case "warning": return ui.BadgeVariantWarning case "success": return ui.BadgeVariantSuccess case "danger": return ui.BadgeVariantDanger default: return ui.BadgeVariantInfo } }