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:
arkml 2025-09-09 23:53:34 +05:30 committed by GitHub
parent 13051dbf3a
commit 63e95cf853
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 424 additions and 59 deletions

View file

@ -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>