mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
chore(templates): disable templates page and related UI (#3690)
* chore(templates): disable templates page and related UI * chore(templates): remove unused imports from disabled template code * fix(config): restore noNestedComponentDefinitions rule in biome config * chore(templates): comment out remaining dead template code Comment out handleTemplateFormSubmit, handleTemplateDelete, TemplateStatusBadge component, and TemplateProfile dynamic import that were left over after disabling the templates feature. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore(templates): clean up dead code from review feedback - Remove unused usePathname/pathnameRef in use-workspace-management.ts - Comment out stale 'template' from TabView union type - Remove unused params from TemplateLayoutProps interface Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1eb85dd66f
commit
e796dfee0d
@@ -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' },
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,44 +1,20 @@
|
||||
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
|
||||
params: Promise<{
|
||||
id: string
|
||||
}>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
// const logger = createLogger('TemplateMetadata')
|
||||
|
||||
// /**
|
||||
// * 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',
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 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()
|
||||
}
|
||||
|
||||
@@ -1,73 +1,79 @@
|
||||
import { db } from '@sim/db'
|
||||
import { permissions, templateCreators, templates, workspace } from '@sim/db/schema'
|
||||
import { and, desc, eq } from 'drizzle-orm'
|
||||
import type { Metadata } from 'next'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import type { Template } from '@/app/templates/templates'
|
||||
import Templates from '@/app/templates/templates'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Templates',
|
||||
description:
|
||||
'Browse pre-built workflow templates to get started quickly with AI agents, automations, and integrations.',
|
||||
}
|
||||
// import { db } from '@sim/db'
|
||||
// import { permissions, templateCreators, templates, workspace } from '@sim/db/schema'
|
||||
// import { and, desc, eq } from 'drizzle-orm'
|
||||
// import type { Metadata } from 'next'
|
||||
// import { redirect } from 'next/navigation'
|
||||
// import { getSession } from '@/lib/auth'
|
||||
// import type { Template } from '@/app/templates/templates'
|
||||
// import Templates from '@/app/templates/templates'
|
||||
|
||||
// export const metadata: Metadata = {
|
||||
// title: 'Templates',
|
||||
// description:
|
||||
// 'Browse pre-built workflow templates to get started quickly with AI agents, automations, and integrations.',
|
||||
// }
|
||||
|
||||
/**
|
||||
* Public templates list page.
|
||||
* Redirects authenticated users to their workspace-scoped templates page.
|
||||
* Allows unauthenticated users to view templates for SEO and discovery.
|
||||
* Currently disabled — returns 404.
|
||||
*/
|
||||
export default async function TemplatesPage() {
|
||||
const session = await getSession()
|
||||
export default function TemplatesPage() {
|
||||
notFound()
|
||||
|
||||
// Authenticated users: redirect to workspace-scoped templates
|
||||
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`)
|
||||
}
|
||||
}
|
||||
|
||||
// Unauthenticated users: show public templates
|
||||
const templatesData = await db
|
||||
.select({
|
||||
id: templates.id,
|
||||
workflowId: templates.workflowId,
|
||||
name: templates.name,
|
||||
details: templates.details,
|
||||
creatorId: templates.creatorId,
|
||||
creator: templateCreators,
|
||||
views: templates.views,
|
||||
stars: templates.stars,
|
||||
status: templates.status,
|
||||
tags: templates.tags,
|
||||
requiredCredentials: templates.requiredCredentials,
|
||||
state: templates.state,
|
||||
createdAt: templates.createdAt,
|
||||
updatedAt: templates.updatedAt,
|
||||
})
|
||||
.from(templates)
|
||||
.leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
|
||||
.where(eq(templates.status, 'approved'))
|
||||
.orderBy(desc(templates.views), desc(templates.createdAt))
|
||||
.then((rows) => rows.map((row) => ({ ...row, isStarred: false })))
|
||||
|
||||
return (
|
||||
<Templates
|
||||
initialTemplates={templatesData as unknown as Template[]}
|
||||
currentUserId={null}
|
||||
isSuperUser={false}
|
||||
/>
|
||||
)
|
||||
// Redirects authenticated users to their workspace-scoped templates page.
|
||||
// Allows unauthenticated users to view templates for SEO and discovery.
|
||||
//
|
||||
// const session = await getSession()
|
||||
//
|
||||
// // Authenticated users: redirect to workspace-scoped templates
|
||||
// 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`)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Unauthenticated users: show public templates
|
||||
// const templatesData = await db
|
||||
// .select({
|
||||
// id: templates.id,
|
||||
// workflowId: templates.workflowId,
|
||||
// name: templates.name,
|
||||
// details: templates.details,
|
||||
// creatorId: templates.creatorId,
|
||||
// creator: templateCreators,
|
||||
// views: templates.views,
|
||||
// stars: templates.stars,
|
||||
// status: templates.status,
|
||||
// tags: templates.tags,
|
||||
// requiredCredentials: templates.requiredCredentials,
|
||||
// state: templates.state,
|
||||
// createdAt: templates.createdAt,
|
||||
// updatedAt: templates.updatedAt,
|
||||
// })
|
||||
// .from(templates)
|
||||
// .leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
|
||||
// .where(eq(templates.status, 'approved'))
|
||||
// .orderBy(desc(templates.views), desc(templates.createdAt))
|
||||
// .then((rows) => rows.map((row) => ({ ...row, isStarred: false })))
|
||||
//
|
||||
// return (
|
||||
// <Templates
|
||||
// initialTemplates={templatesData as unknown as Template[]}
|
||||
// currentUserId={null}
|
||||
// isSuperUser={false}
|
||||
// />
|
||||
// )
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import { getDocumentIcon } from '@/components/icons/document-icons'
|
||||
import { useSession } from '@/lib/auth/auth-client'
|
||||
import {
|
||||
LandingPromptStorage,
|
||||
LandingTemplateStorage,
|
||||
type LandingWorkflowSeed,
|
||||
LandingWorkflowSeedStorage,
|
||||
} from '@/lib/core/utils/browser-storage'
|
||||
@@ -118,12 +117,12 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
return
|
||||
}
|
||||
|
||||
const templateId = LandingTemplateStorage.consume()
|
||||
if (templateId) {
|
||||
logger.info('Retrieved landing page template, redirecting to template detail')
|
||||
router.replace(`/workspace/${workspaceId}/templates/${templateId}?use=true`)
|
||||
return
|
||||
}
|
||||
// const templateId = LandingTemplateStorage.consume()
|
||||
// if (templateId) {
|
||||
// logger.info('Retrieved landing page template, redirecting to template detail')
|
||||
// router.replace(`/workspace/${workspaceId}/templates/${templateId}?use=true`)
|
||||
// return
|
||||
// }
|
||||
|
||||
const prompt = LandingPromptStorage.consume()
|
||||
if (prompt) {
|
||||
|
||||
@@ -57,13 +57,13 @@ const Credentials = dynamic(
|
||||
),
|
||||
{ loading: () => <CredentialsSkeleton /> }
|
||||
)
|
||||
const TemplateProfile = dynamic(
|
||||
() =>
|
||||
import(
|
||||
'@/app/workspace/[workspaceId]/settings/components/template-profile/template-profile'
|
||||
).then((m) => m.TemplateProfile),
|
||||
{ loading: () => <SettingsSectionSkeleton /> }
|
||||
)
|
||||
// const TemplateProfile = dynamic(
|
||||
// () =>
|
||||
// import(
|
||||
// '@/app/workspace/[workspaceId]/settings/components/template-profile/template-profile'
|
||||
// ).then((m) => m.TemplateProfile),
|
||||
// { loading: () => <SettingsSectionSkeleton /> }
|
||||
// )
|
||||
const CredentialSets = dynamic(
|
||||
() =>
|
||||
import(
|
||||
@@ -177,7 +177,7 @@ export function SettingsPage({ section }: SettingsPageProps) {
|
||||
{effectiveSection === 'general' && <General />}
|
||||
{effectiveSection === 'integrations' && <Integrations />}
|
||||
{effectiveSection === 'secrets' && <Credentials />}
|
||||
{effectiveSection === 'template-profile' && <TemplateProfile />}
|
||||
{/* {effectiveSection === 'template-profile' && <TemplateProfile />} */}
|
||||
{effectiveSection === 'credential-sets' && <CredentialSets />}
|
||||
{effectiveSection === 'access-control' && <AccessControl />}
|
||||
{effectiveSection === 'apikeys' && <ApiKeys />}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
ShieldCheck,
|
||||
TerminalWindow,
|
||||
TrashOutline,
|
||||
User,
|
||||
Users,
|
||||
Wrench,
|
||||
} from '@/components/emcn'
|
||||
@@ -84,7 +83,7 @@ export const sectionConfig: { key: NavigationSection; title: string }[] = [
|
||||
|
||||
export const allNavigationItems: NavigationItem[] = [
|
||||
{ id: 'general', label: 'General', icon: Settings, section: 'account' },
|
||||
{ id: 'template-profile', label: 'Template Profile', icon: User, section: 'account' },
|
||||
// { id: 'template-profile', label: 'Template Profile', icon: User, section: 'account' },
|
||||
{
|
||||
id: 'access-control',
|
||||
label: 'Access Control',
|
||||
|
||||
@@ -1,115 +1,103 @@
|
||||
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 { redirect } from 'next/navigation'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
|
||||
import TemplateDetails from '@/app/templates/[id]/template'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
const logger = createLogger('WorkspaceTemplateMetadata')
|
||||
// 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 { redirect } from 'next/navigation'
|
||||
// import { getSession } from '@/lib/auth'
|
||||
// import { getBaseUrl } from '@/lib/core/utils/urls'
|
||||
// import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
|
||||
// import TemplateDetails from '@/app/templates/[id]/template'
|
||||
|
||||
interface TemplatePageProps {
|
||||
params: Promise<{
|
||||
workspaceId: string
|
||||
id: string
|
||||
}>
|
||||
}
|
||||
// const logger = createLogger('WorkspaceTemplateMetadata')
|
||||
|
||||
// interface TemplatePageProps {
|
||||
// params: Promise<{
|
||||
// workspaceId: string
|
||||
// id: string
|
||||
// }>
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Generate dynamic metadata for workspace template pages.
|
||||
// * This provides OpenGraph images for social media sharing.
|
||||
// */
|
||||
// export async function generateMetadata({
|
||||
// params,
|
||||
// }: {
|
||||
// params: Promise<{ workspaceId: string; id: string }>
|
||||
// }): Promise<Metadata> {
|
||||
// const { workspaceId, 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}/workspace/${workspaceId}/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 workspace template metadata:', error)
|
||||
// return {
|
||||
// title: 'Template',
|
||||
// description: 'AI workflow template on Sim',
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Generate dynamic metadata for workspace template pages.
|
||||
* This provides OpenGraph images for social media sharing.
|
||||
* Workspace-scoped template detail page — currently disabled, returns 404.
|
||||
*/
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<{ workspaceId: string; id: string }>
|
||||
}): Promise<Metadata> {
|
||||
const { workspaceId, 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}/workspace/${workspaceId}/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 workspace template metadata:', error)
|
||||
return {
|
||||
title: 'Template',
|
||||
description: 'AI workflow template on Sim',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workspace-scoped template detail page.
|
||||
* Requires authentication and workspace membership to access.
|
||||
* Uses the shared TemplateDetails component with workspace context.
|
||||
*/
|
||||
export default async function TemplatePage({ params }: TemplatePageProps) {
|
||||
const { workspaceId, id } = await params
|
||||
const session = await getSession()
|
||||
|
||||
if (!session?.user?.id) {
|
||||
redirect(`/templates/${id}`)
|
||||
}
|
||||
|
||||
const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
|
||||
if (!hasPermission) {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
return <TemplateDetails isWorkspaceContext={true} />
|
||||
export default function TemplatePage() {
|
||||
notFound()
|
||||
}
|
||||
|
||||
@@ -1,205 +1,29 @@
|
||||
import { db } from '@sim/db'
|
||||
import { settings, templateCreators, templateStars, templates, user } from '@sim/db/schema'
|
||||
import { and, desc, eq, sql } from 'drizzle-orm'
|
||||
import type { Metadata } from 'next'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { getSession } from '@/lib/auth'
|
||||
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
|
||||
import type { Template as WorkspaceTemplate } from '@/app/workspace/[workspaceId]/templates/templates'
|
||||
import Templates from '@/app/workspace/[workspaceId]/templates/templates'
|
||||
import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
|
||||
import { notFound } from 'next/navigation'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'Templates',
|
||||
}
|
||||
// import { db } from '@sim/db'
|
||||
// import { settings, templateCreators, templateStars, templates, user } from '@sim/db/schema'
|
||||
// import { and, desc, eq, sql } from 'drizzle-orm'
|
||||
// import type { Metadata } from 'next'
|
||||
// import { redirect } from 'next/navigation'
|
||||
// import { getSession } from '@/lib/auth'
|
||||
// import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
|
||||
// import type { Template as WorkspaceTemplate } from '@/app/workspace/[workspaceId]/templates/templates'
|
||||
// import Templates from '@/app/workspace/[workspaceId]/templates/templates'
|
||||
// import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
|
||||
|
||||
interface TemplatesPageProps {
|
||||
params: Promise<{
|
||||
workspaceId: string
|
||||
}>
|
||||
}
|
||||
// export const metadata: Metadata = {
|
||||
// title: 'Templates',
|
||||
// }
|
||||
|
||||
// interface TemplatesPageProps {
|
||||
// params: Promise<{
|
||||
// workspaceId: string
|
||||
// }>
|
||||
// }
|
||||
|
||||
/**
|
||||
* Workspace-scoped Templates page.
|
||||
* Requires authentication and workspace membership to access.
|
||||
* Workspace-scoped Templates page — currently disabled, returns 404.
|
||||
*/
|
||||
export default async function TemplatesPage({ params }: TemplatesPageProps) {
|
||||
const { workspaceId } = await params
|
||||
const session = await getSession()
|
||||
|
||||
// Redirect unauthenticated users to public templates page
|
||||
if (!session?.user?.id) {
|
||||
redirect('/templates')
|
||||
}
|
||||
|
||||
// Verify workspace membership
|
||||
const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
|
||||
if (!hasPermission) {
|
||||
redirect('/')
|
||||
}
|
||||
|
||||
// Check permission group restrictions
|
||||
const permissionConfig = await getUserPermissionConfig(session.user.id)
|
||||
if (permissionConfig?.hideTemplates) {
|
||||
redirect(`/workspace/${workspaceId}`)
|
||||
}
|
||||
|
||||
// Determine effective super user (admin role AND UI mode enabled)
|
||||
const currentUser = await db
|
||||
.select({ role: user.role })
|
||||
.from(user)
|
||||
.where(eq(user.id, session.user.id))
|
||||
.limit(1)
|
||||
const userSettings = await db
|
||||
.select({ superUserModeEnabled: settings.superUserModeEnabled })
|
||||
.from(settings)
|
||||
.where(eq(settings.userId, session.user.id))
|
||||
.limit(1)
|
||||
|
||||
const isSuperUser = currentUser[0]?.role === 'admin'
|
||||
const superUserModeEnabled = userSettings[0]?.superUserModeEnabled ?? false
|
||||
const effectiveSuperUser = isSuperUser && superUserModeEnabled
|
||||
|
||||
// Load templates from database
|
||||
let rows:
|
||||
| Array<{
|
||||
id: string
|
||||
workflowId: string | null
|
||||
name: string
|
||||
details?: unknown
|
||||
creatorId: string | null
|
||||
creator: {
|
||||
id: string
|
||||
referenceType: 'user' | 'organization'
|
||||
referenceId: string
|
||||
name: string
|
||||
profileImageUrl?: string | null
|
||||
details?: unknown
|
||||
verified: boolean
|
||||
} | null
|
||||
views: number
|
||||
stars: number
|
||||
status: 'pending' | 'approved' | 'rejected'
|
||||
tags: string[]
|
||||
requiredCredentials: unknown
|
||||
state: unknown
|
||||
createdAt: Date | string
|
||||
updatedAt: Date | string
|
||||
isStarred?: boolean
|
||||
}>
|
||||
| undefined
|
||||
|
||||
if (session?.user?.id) {
|
||||
const whereCondition = effectiveSuperUser ? undefined : eq(templates.status, 'approved')
|
||||
rows = await db
|
||||
.select({
|
||||
id: templates.id,
|
||||
workflowId: templates.workflowId,
|
||||
name: templates.name,
|
||||
details: templates.details,
|
||||
creatorId: templates.creatorId,
|
||||
creator: templateCreators,
|
||||
views: templates.views,
|
||||
stars: templates.stars,
|
||||
status: templates.status,
|
||||
tags: templates.tags,
|
||||
requiredCredentials: templates.requiredCredentials,
|
||||
state: templates.state,
|
||||
createdAt: templates.createdAt,
|
||||
updatedAt: templates.updatedAt,
|
||||
isStarred: sql<boolean>`CASE WHEN ${templateStars.id} IS NOT NULL THEN true ELSE false END`,
|
||||
})
|
||||
.from(templates)
|
||||
.leftJoin(
|
||||
templateStars,
|
||||
and(eq(templateStars.templateId, templates.id), eq(templateStars.userId, session.user.id))
|
||||
)
|
||||
.leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
|
||||
.where(whereCondition)
|
||||
.orderBy(desc(templates.views), desc(templates.createdAt))
|
||||
} else {
|
||||
rows = await db
|
||||
.select({
|
||||
id: templates.id,
|
||||
workflowId: templates.workflowId,
|
||||
name: templates.name,
|
||||
details: templates.details,
|
||||
creatorId: templates.creatorId,
|
||||
creator: templateCreators,
|
||||
views: templates.views,
|
||||
stars: templates.stars,
|
||||
status: templates.status,
|
||||
tags: templates.tags,
|
||||
requiredCredentials: templates.requiredCredentials,
|
||||
state: templates.state,
|
||||
createdAt: templates.createdAt,
|
||||
updatedAt: templates.updatedAt,
|
||||
})
|
||||
.from(templates)
|
||||
.leftJoin(templateCreators, eq(templates.creatorId, templateCreators.id))
|
||||
.where(eq(templates.status, 'approved'))
|
||||
.orderBy(desc(templates.views), desc(templates.createdAt))
|
||||
.then((r) => r.map((row) => ({ ...row, isStarred: false })))
|
||||
}
|
||||
|
||||
const initialTemplates: WorkspaceTemplate[] =
|
||||
rows?.map((row) => {
|
||||
const authorType = (row.creator?.referenceType as 'user' | 'organization') ?? 'user'
|
||||
const organizationId =
|
||||
row.creator?.referenceType === 'organization' ? row.creator.referenceId : null
|
||||
const userId =
|
||||
row.creator?.referenceType === 'user' ? row.creator.referenceId : '' /* no owner context */
|
||||
|
||||
return {
|
||||
// New structure fields
|
||||
id: row.id,
|
||||
workflowId: row.workflowId,
|
||||
name: row.name,
|
||||
details: row.details as { tagline?: string; about?: string } | null,
|
||||
creatorId: row.creatorId,
|
||||
creator: row.creator
|
||||
? {
|
||||
id: row.creator.id,
|
||||
name: row.creator.name,
|
||||
profileImageUrl: row.creator.profileImageUrl,
|
||||
details: row.creator.details as {
|
||||
about?: string
|
||||
xUrl?: string
|
||||
linkedinUrl?: string
|
||||
websiteUrl?: string
|
||||
contactEmail?: string
|
||||
} | null,
|
||||
referenceType: row.creator.referenceType,
|
||||
referenceId: row.creator.referenceId,
|
||||
verified: row.creator.verified,
|
||||
}
|
||||
: null,
|
||||
views: row.views,
|
||||
stars: row.stars,
|
||||
status: row.status,
|
||||
tags: row.tags,
|
||||
requiredCredentials: row.requiredCredentials,
|
||||
state: row.state as WorkspaceTemplate['state'],
|
||||
createdAt: row.createdAt,
|
||||
updatedAt: row.updatedAt,
|
||||
isStarred: row.isStarred ?? false,
|
||||
isSuperUser: effectiveSuperUser,
|
||||
// Legacy fields for backward compatibility
|
||||
userId,
|
||||
description: (row.details as any)?.tagline ?? null,
|
||||
author: row.creator?.name ?? 'Unknown',
|
||||
authorType,
|
||||
organizationId,
|
||||
color: '#3972F6', // default color for workspace cards
|
||||
icon: 'Workflow', // default icon for workspace cards
|
||||
}
|
||||
}) ?? []
|
||||
|
||||
return (
|
||||
<Templates
|
||||
initialTemplates={initialTemplates}
|
||||
currentUserId={session?.user?.id || ''}
|
||||
isSuperUser={effectiveSuperUser}
|
||||
/>
|
||||
)
|
||||
export default function TemplatesPage() {
|
||||
notFound()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import type { GlobalCommand } from '@/app/workspace/[workspaceId]/providers/glob
|
||||
export type CommandId =
|
||||
| 'accept-diff-changes'
|
||||
| 'add-agent'
|
||||
| 'goto-templates'
|
||||
// | 'goto-templates'
|
||||
| 'goto-logs'
|
||||
| 'open-search'
|
||||
| 'run-workflow'
|
||||
@@ -52,11 +52,11 @@ export const COMMAND_DEFINITIONS: Record<CommandId, CommandDefinition> = {
|
||||
shortcut: 'Mod+Shift+A',
|
||||
allowInEditable: true,
|
||||
},
|
||||
'goto-templates': {
|
||||
id: 'goto-templates',
|
||||
shortcut: 'Mod+Y',
|
||||
allowInEditable: true,
|
||||
},
|
||||
// 'goto-templates': {
|
||||
// id: 'goto-templates',
|
||||
// shortcut: 'Mod+Y',
|
||||
// allowInEditable: true,
|
||||
// },
|
||||
'goto-logs': {
|
||||
id: 'goto-logs',
|
||||
shortcut: 'Mod+L',
|
||||
|
||||
+14
-14
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useCallback } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { Layout, Search } from 'lucide-react'
|
||||
import { Search } from 'lucide-react'
|
||||
import Image from 'next/image'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { Button, Library } from '@/components/emcn'
|
||||
@@ -29,11 +29,11 @@ interface CommandItem {
|
||||
* Available commands list
|
||||
*/
|
||||
const commands: CommandItem[] = [
|
||||
{
|
||||
label: 'Templates',
|
||||
icon: Layout,
|
||||
shortcut: 'Y',
|
||||
},
|
||||
// {
|
||||
// label: 'Templates',
|
||||
// icon: Layout,
|
||||
// shortcut: 'Y',
|
||||
// },
|
||||
{
|
||||
label: 'New Agent',
|
||||
icon: AgentIcon,
|
||||
@@ -78,14 +78,14 @@ export function CommandList() {
|
||||
(label: string) => {
|
||||
try {
|
||||
switch (label) {
|
||||
case 'Templates': {
|
||||
if (!workspaceId) {
|
||||
logger.warn('No workspace ID found, cannot navigate to templates from command list')
|
||||
return
|
||||
}
|
||||
router.push(`/workspace/${workspaceId}/templates`)
|
||||
return
|
||||
}
|
||||
// case 'Templates': {
|
||||
// if (!workspaceId) {
|
||||
// logger.warn('No workspace ID found, cannot navigate to templates from command list')
|
||||
// return
|
||||
// }
|
||||
// router.push(`/workspace/${workspaceId}/templates`)
|
||||
// return
|
||||
// }
|
||||
case 'New Agent': {
|
||||
const event = new CustomEvent('add-block-from-toolbar', {
|
||||
detail: { type: 'agent', enableTriggerMode: false },
|
||||
|
||||
+43
-43
@@ -33,7 +33,7 @@ import {
|
||||
useDeployWorkflow,
|
||||
useUndeployWorkflow,
|
||||
} from '@/hooks/queries/deployments'
|
||||
import { useTemplateByWorkflow } from '@/hooks/queries/templates'
|
||||
// import { useTemplateByWorkflow } from '@/hooks/queries/templates'
|
||||
import { useWorkflowMcpServers } from '@/hooks/queries/workflow-mcp-servers'
|
||||
import { useWorkspaceSettings } from '@/hooks/queries/workspace'
|
||||
import { usePermissionConfig } from '@/hooks/use-permission-config'
|
||||
@@ -73,7 +73,7 @@ interface WorkflowDeploymentInfoUI {
|
||||
isPublicApi: boolean
|
||||
}
|
||||
|
||||
type TabView = 'general' | 'api' | 'chat' | 'template' | 'mcp' | 'form' | 'a2a'
|
||||
type TabView = 'general' | 'api' | 'chat' | /* 'template' | */ 'mcp' | 'form' | 'a2a'
|
||||
|
||||
export function DeployModal({
|
||||
open,
|
||||
@@ -102,8 +102,8 @@ export function DeployModal({
|
||||
const [selectedStreamingOutputs, setSelectedStreamingOutputs] = useState<string[]>([])
|
||||
|
||||
const [showUndeployConfirm, setShowUndeployConfirm] = useState(false)
|
||||
const [templateFormValid, setTemplateFormValid] = useState(false)
|
||||
const [templateSubmitting, setTemplateSubmitting] = useState(false)
|
||||
// const [templateFormValid, setTemplateFormValid] = useState(false)
|
||||
// const [templateSubmitting, setTemplateSubmitting] = useState(false)
|
||||
const [mcpToolSubmitting, setMcpToolSubmitting] = useState(false)
|
||||
const [mcpToolCanSave, setMcpToolCanSave] = useState(false)
|
||||
const [a2aSubmitting, setA2aSubmitting] = useState(false)
|
||||
@@ -159,17 +159,17 @@ export function DeployModal({
|
||||
const hasA2aAgent = !!existingA2aAgent
|
||||
const isA2aPublished = existingA2aAgent?.isPublished ?? false
|
||||
|
||||
const { data: existingTemplate } = useTemplateByWorkflow(workflowId || '', {
|
||||
enabled: !!workflowId,
|
||||
})
|
||||
const hasExistingTemplate = !!existingTemplate
|
||||
const templateStatus = existingTemplate
|
||||
? {
|
||||
status: existingTemplate.status as 'pending' | 'approved' | 'rejected' | null,
|
||||
views: existingTemplate.views,
|
||||
stars: existingTemplate.stars,
|
||||
}
|
||||
: null
|
||||
// const { data: existingTemplate } = useTemplateByWorkflow(workflowId || '', {
|
||||
// enabled: !!workflowId,
|
||||
// })
|
||||
// const hasExistingTemplate = !!existingTemplate
|
||||
// const templateStatus = existingTemplate
|
||||
// ? {
|
||||
// status: existingTemplate.status as 'pending' | 'approved' | 'rejected' | null,
|
||||
// views: existingTemplate.views,
|
||||
// stars: existingTemplate.stars,
|
||||
// }
|
||||
// : null
|
||||
|
||||
const deployMutation = useDeployWorkflow()
|
||||
const undeployMutation = useUndeployWorkflow()
|
||||
@@ -406,10 +406,10 @@ export function DeployModal({
|
||||
}
|
||||
}, [])
|
||||
|
||||
const handleTemplateFormSubmit = useCallback(() => {
|
||||
const form = document.getElementById('template-deploy-form') as HTMLFormElement
|
||||
form?.requestSubmit()
|
||||
}, [])
|
||||
// const handleTemplateFormSubmit = useCallback(() => {
|
||||
// const form = document.getElementById('template-deploy-form') as HTMLFormElement
|
||||
// form?.requestSubmit()
|
||||
// }, [])
|
||||
|
||||
const handleMcpToolFormSubmit = useCallback(() => {
|
||||
const form = document.getElementById('mcp-deploy-form') as HTMLFormElement
|
||||
@@ -453,11 +453,11 @@ export function DeployModal({
|
||||
setShowA2aDeleteConfirm(false)
|
||||
}, [])
|
||||
|
||||
const handleTemplateDelete = useCallback(() => {
|
||||
const form = document.getElementById('template-deploy-form')
|
||||
const deleteTrigger = form?.querySelector('[data-template-delete-trigger]') as HTMLButtonElement
|
||||
deleteTrigger?.click()
|
||||
}, [])
|
||||
// const handleTemplateDelete = useCallback(() => {
|
||||
// const form = document.getElementById('template-deploy-form')
|
||||
// const deleteTrigger = form?.querySelector('[data-template-delete-trigger]') as HTMLButtonElement
|
||||
// deleteTrigger?.click()
|
||||
// }, [])
|
||||
|
||||
const isSubmitting = deployMutation.isPending
|
||||
const isUndeploying = undeployMutation.isPending
|
||||
@@ -938,28 +938,28 @@ function StatusBadge({ isWarning }: StatusBadgeProps) {
|
||||
)
|
||||
}
|
||||
|
||||
interface TemplateStatusBadgeProps {
|
||||
status: 'pending' | 'approved' | 'rejected' | null
|
||||
views?: number
|
||||
stars?: number
|
||||
}
|
||||
// interface TemplateStatusBadgeProps {
|
||||
// status: 'pending' | 'approved' | 'rejected' | null
|
||||
// views?: number
|
||||
// stars?: number
|
||||
// }
|
||||
|
||||
function TemplateStatusBadge({ status, views, stars }: TemplateStatusBadgeProps) {
|
||||
const isPending = status === 'pending'
|
||||
const label = isPending ? 'Under review' : 'Live'
|
||||
// function TemplateStatusBadge({ status, views, stars }: TemplateStatusBadgeProps) {
|
||||
// const isPending = status === 'pending'
|
||||
// const label = isPending ? 'Under review' : 'Live'
|
||||
|
||||
const statsText =
|
||||
status === 'approved' && views !== undefined && views > 0
|
||||
? `${views} views${stars !== undefined && stars > 0 ? ` • ${stars} stars` : ''}`
|
||||
: null
|
||||
// const statsText =
|
||||
// status === 'approved' && views !== undefined && views > 0
|
||||
// ? `${views} views${stars !== undefined && stars > 0 ? ` • ${stars} stars` : ''}`
|
||||
// : null
|
||||
|
||||
return (
|
||||
<Badge variant={isPending ? 'amber' : 'green'} size='lg' dot>
|
||||
{label}
|
||||
{statsText && <span>• {statsText}</span>}
|
||||
</Badge>
|
||||
)
|
||||
}
|
||||
// return (
|
||||
// <Badge variant={isPending ? 'amber' : 'green'} size='lg' dot>
|
||||
// {label}
|
||||
// {statsText && <span>• {statsText}</span>}
|
||||
// </Badge>
|
||||
// )
|
||||
// }
|
||||
|
||||
interface GeneralFooterProps {
|
||||
isDeployed?: boolean
|
||||
|
||||
+2
-14
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useLeaveWorkspace } from '@/hooks/queries/invitations'
|
||||
import {
|
||||
useCreateWorkspace,
|
||||
@@ -33,7 +33,6 @@ export function useWorkspaceManagement({
|
||||
sessionUserId,
|
||||
}: UseWorkspaceManagementProps) {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const queryClient = useQueryClient()
|
||||
const switchToWorkspace = useWorkflowRegistry((state) => state.switchToWorkspace)
|
||||
|
||||
@@ -50,12 +49,10 @@ export function useWorkspaceManagement({
|
||||
|
||||
const workspaceIdRef = useRef<string>(workspaceId)
|
||||
const routerRef = useRef<ReturnType<typeof useRouter>>(router)
|
||||
const pathnameRef = useRef<string | null>(pathname || null)
|
||||
const hasValidatedRef = useRef<boolean>(false)
|
||||
|
||||
workspaceIdRef.current = workspaceId
|
||||
routerRef.current = router
|
||||
pathnameRef.current = pathname || null
|
||||
|
||||
const activeWorkspace = useMemo(() => {
|
||||
if (!workspaces.length) return null
|
||||
@@ -114,16 +111,7 @@ export function useWorkspaceManagement({
|
||||
|
||||
try {
|
||||
await switchToWorkspace(workspace.id)
|
||||
const currentPath = pathnameRef.current || ''
|
||||
const templateDetailMatch = currentPath.match(/^\/workspace\/[^/]+\/templates\/([^/]+)$/)
|
||||
if (templateDetailMatch) {
|
||||
const templateId = templateDetailMatch[1]
|
||||
routerRef.current?.push(`/workspace/${workspace.id}/templates/${templateId}`)
|
||||
} else if (/^\/workspace\/[^/]+\/templates$/.test(currentPath)) {
|
||||
routerRef.current?.push(`/workspace/${workspace.id}/templates`)
|
||||
} else {
|
||||
routerRef.current?.push(`/workspace/${workspace.id}/home`)
|
||||
}
|
||||
routerRef.current?.push(`/workspace/${workspace.id}/home`)
|
||||
logger.info(`Switched to workspace: ${workspace.name} (${workspace.id})`)
|
||||
} catch (error) {
|
||||
logger.error('Error switching workspace:', error)
|
||||
|
||||
@@ -919,22 +919,22 @@ export const Sidebar = memo(function Sidebar() {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'goto-templates',
|
||||
handler: () => {
|
||||
try {
|
||||
const pathWorkspaceId = resolveWorkspaceIdFromPath()
|
||||
if (pathWorkspaceId) {
|
||||
navigateToPage(`/workspace/${pathWorkspaceId}/templates`)
|
||||
logger.info('Navigated to templates', { workspaceId: pathWorkspaceId })
|
||||
} else {
|
||||
logger.warn('No workspace ID found, cannot navigate to templates')
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Failed to navigate to templates', { err })
|
||||
}
|
||||
},
|
||||
},
|
||||
// {
|
||||
// id: 'goto-templates',
|
||||
// handler: () => {
|
||||
// try {
|
||||
// const pathWorkspaceId = resolveWorkspaceIdFromPath()
|
||||
// if (pathWorkspaceId) {
|
||||
// navigateToPage(`/workspace/${pathWorkspaceId}/templates`)
|
||||
// logger.info('Navigated to templates', { workspaceId: pathWorkspaceId })
|
||||
// } else {
|
||||
// logger.warn('No workspace ID found, cannot navigate to templates')
|
||||
// }
|
||||
// } catch (err) {
|
||||
// logger.error('Failed to navigate to templates', { err })
|
||||
// }
|
||||
// },
|
||||
// },
|
||||
{
|
||||
id: 'goto-logs',
|
||||
handler: () => {
|
||||
|
||||
Reference in New Issue
Block a user