mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-01 03:16:29 +02:00
Prebuilt cards sections (#237)
* add spinner to pre-built cards * added subsections for prebuilt * added section name styling * added news section and reddit to slack workflow
This commit is contained in:
parent
13051dbf3a
commit
63e95cf853
7 changed files with 424 additions and 59 deletions
|
|
@ -63,6 +63,7 @@ export function BuildAssistantSection() {
|
|||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [autoCreateLoading, setAutoCreateLoading] = useState(false);
|
||||
const [loadingTemplateId, setLoadingTemplateId] = useState<string | null>(null);
|
||||
|
||||
const totalPages = Math.ceil(projects.length / ITEMS_PER_PAGE);
|
||||
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||
|
|
@ -107,12 +108,22 @@ export function BuildAssistantSection() {
|
|||
|
||||
// Handle template selection
|
||||
const handleTemplateSelect = async (templateId: string) => {
|
||||
// When selecting a pre-built template, kick off Copilot with an explain prompt
|
||||
await createProjectWithOptions({
|
||||
template: templateId,
|
||||
prompt: 'Explain this workflow',
|
||||
router,
|
||||
});
|
||||
// Show a small non-blocking spinner on the clicked card
|
||||
setLoadingTemplateId(templateId);
|
||||
try {
|
||||
await createProjectWithOptions({
|
||||
template: templateId,
|
||||
prompt: 'Explain this workflow',
|
||||
router,
|
||||
onError: () => {
|
||||
// Clear loading state if creation fails
|
||||
setLoadingTemplateId(null);
|
||||
},
|
||||
});
|
||||
} catch (_err) {
|
||||
// In case of unexpected error, clear loading state
|
||||
setLoadingTemplateId(null);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle prompt card selection
|
||||
|
|
@ -427,7 +438,7 @@ export function BuildAssistantSection() {
|
|||
<div className="bg-white dark:bg-gray-800 rounded-2xl p-6 shadow-lg border border-gray-200 dark:border-gray-700">
|
||||
<div className="text-left mb-6">
|
||||
<h2 className="text-xl font-semibold text-gray-900 dark:text-gray-100 mb-4">
|
||||
Pre-built Assistants
|
||||
Prebuilt Assistants
|
||||
</h2>
|
||||
</div>
|
||||
{templatesLoading ? (
|
||||
|
|
@ -443,60 +454,112 @@ export function BuildAssistantSection() {
|
|||
No pre-built assistants available
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{templates.map((template) => (
|
||||
<button
|
||||
key={template.id}
|
||||
onClick={() => handleTemplateSelect(template.id)}
|
||||
className="block p-4 border border-gray-200 dark:border-gray-700 rounded-xl hover:border-blue-300 dark:hover:border-blue-600 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-all group hover:shadow-md text-left"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<div className="font-medium text-gray-900 dark:text-gray-100 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors line-clamp-1">
|
||||
{template.name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{template.description}
|
||||
</div>
|
||||
(() => {
|
||||
const workTemplates = templates.filter((t) => (t.category || '').toLowerCase() === 'work productivity');
|
||||
const devTemplates = templates.filter((t) => (t.category || '').toLowerCase() === 'developer productivity');
|
||||
const newsTemplates = templates.filter((t) => (t.category || '').toLowerCase() === 'news');
|
||||
|
||||
{/* Tool logos */}
|
||||
{(() => {
|
||||
const tools = getUniqueTools(template);
|
||||
return tools.length > 0 && (
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500">
|
||||
Tools:
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{tools.slice(0, 4).map((tool) => (
|
||||
tool.logo && (
|
||||
<PictureImg
|
||||
key={tool.name}
|
||||
src={tool.logo}
|
||||
alt={`${tool.name} logo`}
|
||||
className="w-4 h-4 rounded-sm object-cover flex-shrink-0"
|
||||
title={tool.name}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
{tools.length > 4 && (
|
||||
<span className="text-xs text-gray-400 dark:text-gray-500">
|
||||
+{tools.length - 4}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
const renderGrid = (items: any[]) => (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{items.map((template) => (
|
||||
<button
|
||||
key={template.id}
|
||||
onClick={() => handleTemplateSelect(template.id)}
|
||||
disabled={loadingTemplateId === template.id}
|
||||
className={clsx(
|
||||
"relative block p-4 border border-gray-200 dark:border-gray-700 rounded-xl transition-all group text-left",
|
||||
"hover:border-blue-300 dark:hover:border-blue-600 hover:bg-gray-50 dark:hover:bg-gray-700/50 hover:shadow-md",
|
||||
loadingTemplateId === template.id && "opacity-90 cursor-not-allowed"
|
||||
)}
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<div className="font-medium text-gray-900 dark:text-gray-100 group-hover:text-blue-600 dark:group-hover:text-blue-400 transition-colors line-clamp-1">
|
||||
{template.name}
|
||||
</div>
|
||||
<div className="text-sm text-gray-600 dark:text-gray-400 line-clamp-2">
|
||||
{template.description}
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500">
|
||||
{(() => {
|
||||
const tools = getUniqueTools(template);
|
||||
return tools.length > 0 && (
|
||||
<div className="flex items-center gap-2 mt-2">
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500">
|
||||
Tools:
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{tools.slice(0, 4).map((tool) => (
|
||||
tool.logo && (
|
||||
<PictureImg
|
||||
key={tool.name}
|
||||
src={tool.logo}
|
||||
alt={`${tool.name} logo`}
|
||||
className="w-4 h-4 rounded-sm object-cover flex-shrink-0"
|
||||
title={tool.name}
|
||||
/>
|
||||
)
|
||||
))}
|
||||
{tools.length > 4 && (
|
||||
<span className="text-xs text-gray-400 dark:text-gray-500">
|
||||
+{tools.length - 4}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
|
||||
<div className="flex items-center justify-between mt-2">
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500"></div>
|
||||
{loadingTemplateId === template.id ? (
|
||||
<div className="text-blue-600 dark:text-blue-400">
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-current"></div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500 opacity-75"></div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2 h-2 rounded-full bg-blue-500 opacity-75"></div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{workTemplates.length > 0 && (
|
||||
<div>
|
||||
<div className="mb-3">
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold 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">
|
||||
Work Productivity
|
||||
</span>
|
||||
</div>
|
||||
{renderGrid(workTemplates)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{devTemplates.length > 0 && (
|
||||
<div>
|
||||
<div className="mb-3">
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold 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">
|
||||
Developer Productivity
|
||||
</span>
|
||||
</div>
|
||||
{renderGrid(devTemplates)}
|
||||
</div>
|
||||
)}
|
||||
{newsTemplates.length > 0 && (
|
||||
<div>
|
||||
<div className="mb-3">
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold 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">
|
||||
News
|
||||
</span>
|
||||
</div>
|
||||
{renderGrid(newsTemplates)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue