mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-09 15:07:55 -05:00
fix(ui/ux): templates and knowledge pages (#2296)
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { VerifiedBadge } from '@/components/ui/verified-badge'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
@@ -41,6 +42,95 @@ import { useStarTemplate, useTemplate } from '@/hooks/queries/templates'
|
||||
|
||||
const logger = createLogger('TemplateDetails')
|
||||
|
||||
interface TemplateDetailsLoadingProps {
|
||||
isWorkspaceContext?: boolean
|
||||
workspaceId?: string | null
|
||||
}
|
||||
|
||||
function TemplateDetailsLoading({ isWorkspaceContext, workspaceId }: TemplateDetailsLoadingProps) {
|
||||
const breadcrumbItems = [
|
||||
{
|
||||
label: 'Templates',
|
||||
href:
|
||||
isWorkspaceContext && workspaceId ? `/workspace/${workspaceId}/templates` : '/templates',
|
||||
},
|
||||
{ label: 'Template' },
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isWorkspaceContext ? 'h-full flex-1 overflow-hidden' : 'min-h-screen'
|
||||
)}
|
||||
>
|
||||
<div className={cn('flex flex-1', isWorkspaceContext && 'overflow-hidden')}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col px-[24px] pt-[24px] pb-[24px]',
|
||||
isWorkspaceContext ? 'overflow-auto' : 'overflow-visible'
|
||||
)}
|
||||
>
|
||||
{/* Breadcrumb navigation */}
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
|
||||
{/* Template name and action buttons */}
|
||||
<div className='mt-[14px] flex items-center justify-between'>
|
||||
<Skeleton className='h-[27px] w-[250px] rounded-[4px]' />
|
||||
<div className='flex items-center gap-[8px]'>
|
||||
<Skeleton className='h-[32px] w-[80px] rounded-[6px]' />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Template tagline */}
|
||||
<div className='mt-[4px]'>
|
||||
<Skeleton className='h-[21px] w-[400px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Creator and stats row */}
|
||||
<div className='mt-[16px] flex items-center gap-[8px]'>
|
||||
{/* Star icon and count */}
|
||||
<Skeleton className='h-[14px] w-[14px] rounded-[2px]' />
|
||||
<Skeleton className='h-[21px] w-[24px] rounded-[4px]' />
|
||||
|
||||
{/* Views icon and count */}
|
||||
<Skeleton className='h-[16px] w-[16px] rounded-[2px]' />
|
||||
<Skeleton className='h-[21px] w-[32px] rounded-[4px]' />
|
||||
|
||||
{/* Vertical divider */}
|
||||
<div className='mx-[4px] mb-[-1.5px] h-[18px] w-[1.25px] rounded-full bg-[var(--border)]' />
|
||||
|
||||
{/* Creator profile pic */}
|
||||
<Skeleton className='h-[16px] w-[16px] rounded-full' />
|
||||
{/* Creator name */}
|
||||
<Skeleton className='h-[21px] w-[100px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Credentials needed */}
|
||||
<div className='mt-[12px]'>
|
||||
<Skeleton className='h-[18px] w-[280px] rounded-[4px]' />
|
||||
</div>
|
||||
|
||||
{/* Canvas preview */}
|
||||
<div className='relative mt-[24px] h-[450px] w-full flex-shrink-0 overflow-hidden rounded-[8px] border border-[var(--border)]'>
|
||||
<Skeleton className='h-full w-full rounded-none' />
|
||||
</div>
|
||||
|
||||
{/* About this Workflow */}
|
||||
<div className='mt-8'>
|
||||
<Skeleton className='mb-4 h-[24px] w-[180px] rounded-[4px]' />
|
||||
<div className='space-y-2'>
|
||||
<Skeleton className='h-[18px] w-full rounded-[4px]' />
|
||||
<Skeleton className='h-[18px] w-[90%] rounded-[4px]' />
|
||||
<Skeleton className='h-[18px] w-[75%] rounded-[4px]' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface TemplateDetailsProps {
|
||||
isWorkspaceContext?: boolean
|
||||
}
|
||||
@@ -207,11 +297,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className='flex h-screen items-center justify-center'>
|
||||
<div className='text-center'>
|
||||
<p className='font-sans text-muted-foreground text-sm'>Loading template...</p>
|
||||
</div>
|
||||
</div>
|
||||
<TemplateDetailsLoading isWorkspaceContext={isWorkspaceContext} workspaceId={workspaceId} />
|
||||
)
|
||||
}
|
||||
|
||||
@@ -542,9 +628,19 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={cn('flex flex-col', isWorkspaceContext ? 'h-full flex-1' : 'min-h-screen')}>
|
||||
<div className='flex flex-1 overflow-hidden'>
|
||||
<div className='flex flex-1 flex-col overflow-auto px-[24px] pt-[24px] pb-[24px]'>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col',
|
||||
isWorkspaceContext ? 'h-full flex-1 overflow-hidden' : 'min-h-screen'
|
||||
)}
|
||||
>
|
||||
<div className={cn('flex flex-1', isWorkspaceContext && 'overflow-hidden')}>
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-1 flex-col px-[24px] pt-[24px] pb-[24px]',
|
||||
isWorkspaceContext ? 'overflow-auto' : 'overflow-visible'
|
||||
)}
|
||||
>
|
||||
{/* Breadcrumb navigation */}
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
|
||||
@@ -697,7 +793,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
{/* Template tagline */}
|
||||
{template.details?.tagline && (
|
||||
<p className='mt-[4px] font-medium text-[14px] text-[var(--text-tertiary)]'>
|
||||
<p className='mt-[4px] line-clamp-2 max-w-[40vw] font-medium text-[14px] text-[var(--text-tertiary)]'>
|
||||
{template.details.tagline}
|
||||
</p>
|
||||
)}
|
||||
@@ -770,7 +866,7 @@ export default function TemplateDetails({ isWorkspaceContext = false }: Template
|
||||
|
||||
{/* Canvas preview */}
|
||||
<div
|
||||
className='relative mt-[24px] h-[450px] w-full overflow-hidden rounded-[8px] border border-[var(--border)]'
|
||||
className='relative mt-[24px] h-[450px] w-full flex-shrink-0 overflow-hidden rounded-[8px] border border-[var(--border)]'
|
||||
onWheelCapture={handleCanvasWheelCapture}
|
||||
>
|
||||
{renderWorkflowPreview()}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { season } from '@/app/_styles/fonts/season/season'
|
||||
export default function TemplatesLayoutClient({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<Tooltip.Provider delayDuration={600} skipDelayDuration={0}>
|
||||
<div className={`${season.variable} font-season`}>{children}</div>
|
||||
<div className={`${season.variable} flex min-h-screen flex-col font-season`}>{children}</div>
|
||||
</Tooltip.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
* Knowledge Base layout - applies sidebar padding for all knowledge routes.
|
||||
*/
|
||||
export default function KnowledgeLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className='flex h-full flex-1 flex-col pl-60'>{children}</div>
|
||||
return <div className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</div>
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@
|
||||
* Logs layout - applies sidebar padding for all logs routes.
|
||||
*/
|
||||
export default function LogsLayout({ children }: { children: React.ReactNode }) {
|
||||
return <div className='flex h-full flex-1 flex-col pl-60'>{children}</div>
|
||||
return <div className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</div>
|
||||
}
|
||||
|
||||
@@ -206,7 +206,10 @@ function TemplateCardInner({
|
||||
className
|
||||
)}
|
||||
>
|
||||
<div ref={previewRef} className='relative h-[180px] w-full overflow-hidden rounded-[6px]'>
|
||||
<div
|
||||
ref={previewRef}
|
||||
className='pointer-events-none h-[180px] w-full overflow-hidden rounded-[6px]'
|
||||
>
|
||||
{normalizedState && isInView ? (
|
||||
<WorkflowPreview
|
||||
workflowState={normalizedState}
|
||||
@@ -222,8 +225,6 @@ function TemplateCardInner({
|
||||
) : (
|
||||
<div className='h-full w-full bg-[#2A2A2A]' />
|
||||
)}
|
||||
{/* Transparent overlay to block all pointer events from the preview */}
|
||||
<div className='pointer-events-none absolute inset-0' />
|
||||
</div>
|
||||
|
||||
<div className='mt-[10px] flex items-center justify-between'>
|
||||
|
||||
@@ -2,9 +2,5 @@
|
||||
* Templates layout - applies sidebar padding for all template routes.
|
||||
*/
|
||||
export default function TemplatesLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<main className='flex h-full flex-1 flex-col overflow-hidden pl-60'>
|
||||
<div>{children}</div>
|
||||
</main>
|
||||
)
|
||||
return <main className='flex h-full flex-1 flex-col overflow-hidden pl-60'>{children}</main>
|
||||
}
|
||||
|
||||
@@ -175,8 +175,8 @@ export default function Templates({
|
||||
<div className='flex flex-1 flex-col overflow-auto px-[24px] pt-[28px] pb-[24px]'>
|
||||
<div>
|
||||
<div className='flex items-start gap-[12px]'>
|
||||
<div className='flex h-[26px] w-[26px] items-center justify-center rounded-[6px] border border-[#1E3A5A] bg-[#0F2A3D]'>
|
||||
<Layout className='h-[14px] w-[14px] text-[#60A5FA]' />
|
||||
<div className='flex h-[26px] w-[26px] items-center justify-center rounded-[6px] border border-[#1A5070] bg-[#153347]'>
|
||||
<Layout className='h-[14px] w-[14px] text-[#33b4ff]' />
|
||||
</div>
|
||||
<h1 className='font-medium text-[18px]'>Templates</h1>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
} from '@/components/emcn'
|
||||
import { Skeleton, TagInput } from '@/components/ui'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import { cn } from '@/lib/core/utils/cn'
|
||||
import { createLogger } from '@/lib/logs/console/logger'
|
||||
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
|
||||
import {
|
||||
@@ -87,7 +88,8 @@ export function TemplateDeploy({
|
||||
const deleteMutation = useDeleteTemplate()
|
||||
|
||||
const isSubmitting = createMutation.isPending || updateMutation.isPending
|
||||
const isFormValid = formData.name.trim().length > 0 && formData.name.length <= 100
|
||||
const isFormValid =
|
||||
formData.name.trim().length > 0 && formData.name.length <= 100 && formData.tagline.length <= 200
|
||||
|
||||
const updateField = <K extends keyof TemplateFormData>(field: K, value: TemplateFormData[K]) => {
|
||||
setFormData((prev) => ({ ...prev, [field]: value }))
|
||||
@@ -302,6 +304,7 @@ export function TemplateDeploy({
|
||||
value={formData.tagline}
|
||||
onChange={(e) => updateField('tagline', e.target.value)}
|
||||
disabled={isSubmitting}
|
||||
className={cn(formData.tagline.length > 200 && 'border-[var(--text-error)]')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user