From 9dee558333fb2d040978af65ceb2de1cc1478d8e Mon Sep 17 00:00:00 2001 From: akhisud3195 Date: Mon, 15 Sep 2025 15:28:45 +0400 Subject: [PATCH] Fix bug with sorting by likes count and update design of cards --- .../components/build-assistant-section.tsx | 3 +- .../components/common/AssistantCard.tsx | 110 ++++++++++-------- .../common/UnifiedTemplatesSection.tsx | 64 ++++++++-- 3 files changed, 115 insertions(+), 62 deletions(-) diff --git a/apps/rowboat/app/projects/components/build-assistant-section.tsx b/apps/rowboat/app/projects/components/build-assistant-section.tsx index 5bcbb5ba..554e4aea 100644 --- a/apps/rowboat/app/projects/components/build-assistant-section.tsx +++ b/apps/rowboat/app/projects/components/build-assistant-section.tsx @@ -628,7 +628,6 @@ export function BuildAssistantSection() { onLike={handleTemplateLike} onShare={handleTemplateShare} onDelete={async (item) => { - if (!confirm('Delete this template? This action cannot be undone.')) return; try { const resp = await fetch(`/api/assistant-templates/${item.id}`, { method: 'DELETE' }); if (!resp.ok) { @@ -637,7 +636,7 @@ export function BuildAssistantSection() { setCommunityTemplates(prev => prev.filter(t => t.id !== item.id)); } catch (e) { console.error(e); - alert('Failed to delete template'); + // Optional: surface non-blocking feedback; keeping console error for now } }} getUniqueTools={getUniqueTools} diff --git a/apps/rowboat/components/common/AssistantCard.tsx b/apps/rowboat/components/common/AssistantCard.tsx index 7873c011..c32957c7 100644 --- a/apps/rowboat/components/common/AssistantCard.tsx +++ b/apps/rowboat/components/common/AssistantCard.tsx @@ -118,19 +118,19 @@ export function AssistantCard({ const getCategoryColor = (category: string) => { const lowerCategory = category.toLowerCase(); if (lowerCategory.includes('work productivity')) { - return 'bg-amber-50 text-amber-700 ring-1 ring-amber-200 dark:bg-amber-400/10 dark:text-amber-300 dark:ring-amber-400/30'; + return 'bg-amber-50 text-amber-700 dark:bg-amber-400/10 dark:text-amber-300'; } else if (lowerCategory.includes('developer productivity')) { - return 'bg-indigo-50 text-indigo-700 ring-1 ring-indigo-200 dark:bg-indigo-400/10 dark:text-indigo-300 dark:ring-indigo-400/30'; + return 'bg-indigo-50 text-indigo-700 dark:bg-indigo-400/10 dark:text-indigo-300'; } else if (lowerCategory.includes('news') || lowerCategory.includes('social')) { - return 'bg-green-50 text-green-700 ring-1 ring-green-200 dark:bg-green-400/10 dark:text-green-300 dark:ring-green-400/30'; + return 'bg-green-50 text-green-700 dark:bg-green-400/10 dark:text-green-300'; } else if (lowerCategory.includes('customer support')) { - return 'bg-red-50 text-red-700 ring-1 ring-red-200 dark:bg-red-400/10 dark:text-red-300 dark:ring-red-400/30'; + return 'bg-red-50 text-red-700 dark:bg-red-400/10 dark:text-red-300'; } else if (lowerCategory.includes('education')) { - return 'bg-blue-50 text-blue-700 ring-1 ring-blue-200 dark:bg-blue-400/10 dark:text-blue-300 dark:ring-blue-400/30'; + return 'bg-blue-50 text-blue-700 dark:bg-blue-400/10 dark:text-blue-300'; } else if (lowerCategory.includes('entertainment')) { - return 'bg-purple-50 text-purple-700 ring-1 ring-purple-200 dark:bg-purple-400/10 dark:text-purple-300 dark:ring-purple-400/30'; + return 'bg-purple-50 text-purple-700 dark:bg-purple-400/10 dark:text-purple-300'; } else { - return 'bg-gray-50 text-gray-700 ring-1 ring-gray-200 dark:bg-gray-400/10 dark:text-gray-300 dark:ring-gray-400/30'; + return 'bg-gray-50 text-gray-700 dark:bg-gray-400/10 dark:text-gray-300'; } }; @@ -157,51 +157,51 @@ export function AssistantCard({ "inline-flex items-center px-1.5 py-0.5 rounded text-xs font-medium flex-shrink-0", templateType === 'prebuilt' ? "bg-blue-50 text-blue-700 dark:bg-blue-400/10 dark:text-blue-300" - : "bg-purple-50 text-purple-700 dark:bg-purple-400/10 dark:text-purple-300" + : "bg-rose-50 text-rose-700 dark:bg-rose-400/10 dark:text-rose-300" )}> {templateType === 'prebuilt' ? 'Library' : 'Community'} )} -
+
{description}
{showDescriptionToggle && ( - + !isDescriptionExpanded ? ( +
+
+ +
+
+ ) : ( + + ) )}
- {/* Category Badge */} -
- - {category} - - {loading && ( -
-
-
- )} -
- {/* Tools (reserve row height even when absent to align cards) */} -
+
{displayTools.length > 0 && ( <>
@@ -229,12 +229,40 @@ export function AssistantCard({ )}
+ {/* Category Badge */} +
+ + {category} + + {loading && ( +
+
+
+ )} +
+ {/* Author and interaction info */}
- {authorName ? (isAnonymous ? 'Anonymous' : authorName) : 'Rowboat'} + {isAnonymous ? 'Anonymous' : (authorName ? (authorName.split(' ')[0] || 'Rowboat') : 'Rowboat')} + {onDelete && ( + + )} {createdAt && (
@@ -270,20 +298,6 @@ export function AssistantCard({ {copied && Copied} - {onDelete && ( - - )}
diff --git a/apps/rowboat/components/common/UnifiedTemplatesSection.tsx b/apps/rowboat/components/common/UnifiedTemplatesSection.tsx index edb24290..eeb44e00 100644 --- a/apps/rowboat/components/common/UnifiedTemplatesSection.tsx +++ b/apps/rowboat/components/common/UnifiedTemplatesSection.tsx @@ -62,6 +62,10 @@ export function UnifiedTemplatesSection({ const [confirmOpen, setConfirmOpen] = useState(false); const [pendingDeleteItem, setPendingDeleteItem] = useState(null); + // Row-based pagination state + const [columns, setColumns] = useState(1); + const [rowsShown, setRowsShown] = useState(4); + useEffect(() => { let isMounted = true; (async () => { @@ -126,24 +130,50 @@ export function UnifiedTemplatesSection({ return a.name.localeCompare(b.name); case 'popular': default: - // For prebuilt templates, use a default order - // For community templates, use like count - if (a.type === 'community' && b.type === 'community') { - return (b.likeCount || 0) - (a.likeCount || 0); - } - if (a.type === 'prebuilt' && b.type === 'prebuilt') { + // Sort across both types by like count desc; tie-break by createdAt desc, then name + { + const aLikes = a.likeCount || 0; + const bLikes = b.likeCount || 0; + if (bLikes !== aLikes) return bLikes - aLikes; + const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0; + const bTime = b.createdAt ? new Date(b.createdAt).getTime() : 0; + if (bTime !== aTime) return bTime - aTime; return a.name.localeCompare(b.name); } - // Prebuilt templates first, then community - if (a.type === 'prebuilt' && b.type === 'community') return -1; - if (a.type === 'community' && b.type === 'prebuilt') return 1; - return 0; } }); return filtered; }, [allTemplates, searchQuery, selectedType, selectedCategories, sortBy]); + // Determine columns based on Tailwind breakpoints used by the grid + useEffect(() => { + const computeColumns = () => { + if (typeof window === 'undefined') return 1; + // Tailwind: grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 + const isLg = window.matchMedia('(min-width: 1024px)').matches; + const isSm = window.matchMedia('(min-width: 640px)').matches; + return isLg ? 3 : (isSm ? 2 : 1); + }; + const update = () => setColumns(computeColumns()); + update(); + window.addEventListener('resize', update); + return () => window.removeEventListener('resize', update); + }, []); + + // Reset rowsShown when filters/sort change + useEffect(() => { + setRowsShown(4); + }, [searchQuery, selectedType, selectedCategories, sortBy]); + + const itemsPerRow = Math.max(columns, 1); + const visibleCount = rowsShown * itemsPerRow; + const hasMore = filteredTemplates.length > visibleCount; + const remainingItems = Math.max(filteredTemplates.length - visibleCount, 0); + const remainingRows = Math.ceil(remainingItems / itemsPerRow); + + const visibleTemplates = filteredTemplates.slice(0, visibleCount); + // Handle category toggle const toggleCategory = (category: string) => { setSelectedCategories(prev => { @@ -345,10 +375,10 @@ export function UnifiedTemplatesSection({ ) : ( <>
- Showing {filteredTemplates.length} template{filteredTemplates.length !== 1 ? 's' : ''} + Showing {Math.min(visibleCount, filteredTemplates.length)} of {filteredTemplates.length} template{filteredTemplates.length !== 1 ? 's' : ''} ({rowsShown} row{rowsShown !== 1 ? 's' : ''})
- {filteredTemplates.map((item) => ( + {visibleTemplates.map((item) => ( ))}
+ {hasMore && ( +
+ +
+ )} )}