Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/sim/app/(home)/components/footer/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const PRODUCT_LINKS: FooterItem[] = [

const RESOURCES_LINKS: FooterItem[] = [
{ label: 'Blog', href: '/blog' },
{ label: 'Templates', href: '/templates' },
// { label: 'Templates', href: '/templates' },
{ label: 'Docs', href: 'https://docs.sim.ai', external: true },
{ label: 'Careers', href: 'https://jobs.ashbyhq.com/sim', external: true },
{ label: 'Changelog', href: '/changelog' },
Expand Down
1 change: 0 additions & 1 deletion apps/sim/app/llms.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Sim lets teams create agents, workflows, knowledge bases, tables, and docs. Over
## Core Pages

- [Homepage](${baseUrl}): Product overview, features, and pricing
- [Templates](${baseUrl}/templates): Pre-built workflow templates to get started quickly
- [Changelog](${baseUrl}/changelog): Product updates and release notes
- [Sim Blog](${baseUrl}/blog): Announcements, insights, and guides

Expand Down
8 changes: 4 additions & 4 deletions apps/sim/app/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
url: `${baseUrl}/blog/tags`,
lastModified: now,
},
{
url: `${baseUrl}/templates`,
lastModified: now,
},
// {
// url: `${baseUrl}/templates`,
// lastModified: now,
// },
{
url: `${baseUrl}/changelog`,
lastModified: now,
Expand Down
41 changes: 10 additions & 31 deletions apps/sim/app/templates/[id]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { db } from '@sim/db'
import { permissions, workspace } from '@sim/db/schema'
import { and, desc, eq } from 'drizzle-orm'
import { redirect } from 'next/navigation'
import { getSession } from '@/lib/auth'
// import { db } from '@sim/db'
// import { permissions, workspace } from '@sim/db/schema'
// import { and, desc, eq } from 'drizzle-orm'
// import { redirect } from 'next/navigation'
// import { getSession } from '@/lib/auth'

export const dynamic = 'force-dynamic'
export const revalidate = 0
// export const dynamic = 'force-dynamic'
// export const revalidate = 0

interface TemplateLayoutProps {
children: React.ReactNode
Expand All @@ -15,30 +15,9 @@ interface TemplateLayoutProps {
}

/**
* Template detail layout (public scope).
* - If user is authenticated, redirect to workspace-scoped template detail.
* - Otherwise render the public template detail children.
* Template detail layout (public scope) — currently disabled.
* Previously redirected authenticated users to the workspace-scoped template detail.
*/
export default async function TemplateDetailLayout({ children, params }: TemplateLayoutProps) {
const { id } = await params
const session = await getSession()

if (session?.user?.id) {
const userWorkspaces = await db
.select({
workspace: workspace,
})
.from(permissions)
.innerJoin(workspace, eq(permissions.entityId, workspace.id))
.where(and(eq(permissions.userId, session.user.id), eq(permissions.entityType, 'workspace')))
.orderBy(desc(workspace.createdAt))
.limit(1)

if (userWorkspaces.length > 0) {
const firstWorkspace = userWorkspaces[0].workspace
redirect(`/workspace/${firstWorkspace.id}/templates/${id}`)
}
}

export default function TemplateDetailLayout({ children }: TemplateLayoutProps) {
return children
}
169 changes: 85 additions & 84 deletions apps/sim/app/templates/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,93 @@
import { db } from '@sim/db'
import { templateCreators, templates } from '@sim/db/schema'
import { createLogger } from '@sim/logger'
import { eq } from 'drizzle-orm'
import type { Metadata } from 'next'
import { getBaseUrl } from '@/lib/core/utils/urls'
import TemplateDetails from '@/app/templates/[id]/template'
import { notFound } from 'next/navigation'

const logger = createLogger('TemplateMetadata')
// import { db } from '@sim/db'
// import { templateCreators, templates } from '@sim/db/schema'
// import { createLogger } from '@sim/logger'
// import { eq } from 'drizzle-orm'
// import type { Metadata } from 'next'
// import { getBaseUrl } from '@/lib/core/utils/urls'
// import TemplateDetails from '@/app/templates/[id]/template'

/**
* Generate dynamic metadata for template pages.
* This provides OpenGraph images for social media sharing.
*/
export async function generateMetadata({
params,
}: {
params: Promise<{ id: string }>
}): Promise<Metadata> {
const { id } = await params

try {
const result = await db
.select({
template: templates,
creator: templateCreators,
})
.from(templates)
.leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
.where(eq(templates.id, id))
.limit(1)

if (result.length === 0) {
return {
title: 'Template Not Found',
description: 'The requested template could not be found.',
}
}
// const logger = createLogger('TemplateMetadata')

const { template, creator } = result[0]
const baseUrl = getBaseUrl()

const details = template.details as { tagline?: string; about?: string } | null
const description = details?.tagline || 'AI workflow template on Sim'

const hasOgImage = !!template.ogImageUrl
const ogImageUrl = template.ogImageUrl || `${baseUrl}/logo/primary/rounded.png`

return {
title: template.name,
description,
openGraph: {
title: template.name,
description,
type: 'website',
url: `${baseUrl}/templates/${id}`,
siteName: 'Sim',
images: [
{
url: ogImageUrl,
width: hasOgImage ? 1200 : 512,
height: hasOgImage ? 630 : 512,
alt: `${template.name} - Workflow Preview`,
},
],
},
twitter: {
card: hasOgImage ? 'summary_large_image' : 'summary',
title: template.name,
description,
images: [ogImageUrl],
creator: creator?.details
? ((creator.details as Record<string, unknown>).xHandle as string) || undefined
: undefined,
},
}
} catch (error) {
logger.error('Failed to generate template metadata:', error)
return {
title: 'Template',
description: 'AI workflow template on Sim',
}
}
}
// /**
// * Generate dynamic metadata for template pages.
// * This provides OpenGraph images for social media sharing.
// */
// export async function generateMetadata({
// params,
// }: {
// params: Promise<{ id: string }>
// }): Promise<Metadata> {
// const { id } = await params
//
// try {
// const result = await db
// .select({
// template: templates,
// creator: templateCreators,
// })
// .from(templates)
// .leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
// .where(eq(templates.id, id))
// .limit(1)
//
// if (result.length === 0) {
// return {
// title: 'Template Not Found',
// description: 'The requested template could not be found.',
// }
// }
//
// const { template, creator } = result[0]
// const baseUrl = getBaseUrl()
//
// const details = template.details as { tagline?: string; about?: string } | null
// const description = details?.tagline || 'AI workflow template on Sim'
//
// const hasOgImage = !!template.ogImageUrl
// const ogImageUrl = template.ogImageUrl || `${baseUrl}/logo/primary/rounded.png`
//
// return {
// title: template.name,
// description,
// openGraph: {
// title: template.name,
// description,
// type: 'website',
// url: `${baseUrl}/templates/${id}`,
// siteName: 'Sim',
// images: [
// {
// url: ogImageUrl,
// width: hasOgImage ? 1200 : 512,
// height: hasOgImage ? 630 : 512,
// alt: `${template.name} - Workflow Preview`,
// },
// ],
// },
// twitter: {
// card: hasOgImage ? 'summary_large_image' : 'summary',
// title: template.name,
// description,
// images: [ogImageUrl],
// creator: creator?.details
// ? ((creator.details as Record<string, unknown>).xHandle as string) || undefined
// : undefined,
// },
// }
// } catch (error) {
// logger.error('Failed to generate template metadata:', error)
// return {
// title: 'Template',
// description: 'AI workflow template on Sim',
// }
// }
// }

/**
* Public template detail page for unauthenticated users.
* Authenticated-user redirect is handled in templates/[id]/layout.tsx.
* Public template detail page — currently disabled, returns 404.
*/
export default function TemplatePage() {
return <TemplateDetails />
notFound()
}
Loading
Loading