flnxi-Wu/app/admin/_components/template-manager/CustomTemplateSection.tsx

212 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useThemeStyles, cn } from '@/lib/ui-styles'
import type { DisplayTemplate } from './types'
interface CustomTemplateSectionProps {
loading: boolean
error: string | null
templates: DisplayTemplate[]
filteredTemplates: DisplayTemplate[]
deletingId: string | null
canManageCustomTemplates: boolean
onRequestUpgrade: () => void
onCreate: () => void
onPreview: (template: DisplayTemplate) => void
onActivate: (id: string) => void
onDeactivate: (id: string) => void
onEdit: (template: DisplayTemplate) => void
onDuplicate: (template: DisplayTemplate) => void
onDelete: (id: string) => void
}
export default function CustomTemplateSection({
loading,
error,
templates,
filteredTemplates,
deletingId,
canManageCustomTemplates,
onRequestUpgrade,
onCreate,
onPreview,
onActivate,
onDeactivate,
onEdit,
onDuplicate,
onDelete,
}: CustomTemplateSectionProps) {
const styles = useThemeStyles()
const runTemplateAction = (action: () => void) => {
if (!canManageCustomTemplates) {
onRequestUpgrade()
return
}
action()
}
return (
<div className="px-6 pt-5 pb-4">
<div className="flex flex-col gap-3 mb-3 sm:flex-row sm:items-center sm:justify-between">
<div>
<div className="flex items-center gap-2">
<h3 className="text-sm font-semibold text-slate-900 dark:text-white">自定义模板</h3>
{!canManageCustomTemplates && (
<span className="inline-flex items-center rounded-md border border-slate-200 bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300">
授权受限
</span>
)}
</div>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">
{canManageCustomTemplates
? '这里列出你自己创建的模板,可继续编辑、复制或删除'
: '当前许可证未包含自定义模板功能,请联系管理员处理授权'}
</p>
</div>
<button
type="button"
onClick={() => {
if (!canManageCustomTemplates) {
onRequestUpgrade()
return
}
onCreate()
}}
className={cn(
canManageCustomTemplates ? styles.buttonStyles.primary : styles.buttonStyles.secondary,
styles.buttonModifiers.compactXs,
'w-full sm:w-auto'
)}
>
{canManageCustomTemplates ? '新建自定义模板' : '授权后可新建'}
</button>
</div>
{loading ? (
<div className="p-8 text-center text-slate-500 dark:text-slate-400">加载中...</div>
) : error ? (
<div className="p-8 text-center text-red-500 dark:text-red-300">{error}</div>
) : (
<div className="space-y-2">
{templates.length === 0 ? (
<div className="rounded-lg border border-dashed border-slate-300 p-6 text-center text-sm text-slate-500 dark:border-slate-700 dark:text-slate-400">暂无自定义模板。</div>
) : filteredTemplates.length === 0 ? (
<div className="rounded-lg border border-dashed border-slate-300 p-6 text-center text-sm text-slate-500 dark:border-slate-700 dark:text-slate-400">未找到匹配的模板名称,请调整搜索关键词。</div>
) : (
filteredTemplates.map((tpl) => (
<div
key={tpl.id}
onClick={() => onPreview(tpl)}
className="group relative cursor-pointer rounded-lg border border-slate-200 bg-white px-4 py-3 transition-colors hover:border-slate-300 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:hover:border-slate-600 dark:hover:bg-slate-800/70"
>
<div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
<div className="min-w-0 space-y-1">
<p className="text-sm font-semibold text-slate-900 dark:text-white">{tpl.name}</p>
{tpl.description && (
<p className="text-xs text-slate-500 dark:text-slate-400 leading-snug line-clamp-2">{tpl.description}</p>
)}
</div>
<div className="flex flex-wrap items-center justify-start gap-2 lg:justify-end">
<span
className={`px-2 py-0.5 text-xs font-medium rounded-md transition-colors ${
tpl.status === 'active'
? 'bg-green-100 dark:bg-green-900/40 text-green-700 dark:text-green-300'
: tpl.status === 'draft'
? 'bg-yellow-100 dark:bg-yellow-900/40 text-yellow-700 dark:text-yellow-300'
: 'bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300'
}`}
>
{tpl.statusLabel}
</span>
<span className="text-[11px] text-slate-400 dark:text-slate-500">
{tpl.updatedLabel} · 版本 {tpl.version}
</span>
{tpl.status === 'active' ? (
<button
type="button"
onClick={(e) => {
e.stopPropagation()
runTemplateAction(() => onDeactivate(tpl.id))
}}
className={cn(
styles.buttonStyles.secondary,
styles.buttonModifiers.compactXs,
!canManageCustomTemplates && 'opacity-70'
)}
>
取消启用
</button>
) : (
<button
type="button"
onClick={(e) => {
e.stopPropagation()
runTemplateAction(() => onActivate(tpl.id))
}}
className={cn(
styles.buttonStyles.success,
styles.buttonModifiers.compactXs,
!canManageCustomTemplates && 'opacity-70'
)}
>
启用
</button>
)}
<button
type="button"
onClick={(e) => {
e.stopPropagation()
runTemplateAction(() => onEdit(tpl))
}}
className={cn(
styles.buttonStyles.secondary,
styles.buttonModifiers.compactXs,
!canManageCustomTemplates && 'opacity-70'
)}
>
编辑
</button>
<button
type="button"
onClick={(e) => {
e.stopPropagation()
runTemplateAction(() => onDuplicate(tpl))
}}
className={cn(
styles.buttonStyles.secondary,
styles.buttonModifiers.compactXs,
'bg-white dark:bg-slate-800 border-slate-300 dark:border-slate-600 text-slate-700 dark:text-slate-200',
!canManageCustomTemplates && 'opacity-70'
)}
>
复制
</button>
<button
type="button"
onClick={(e) => {
e.stopPropagation()
runTemplateAction(() => onDelete(tpl.id))
}}
disabled={deletingId === tpl.id}
className={cn(
styles.buttonStyles.danger,
styles.buttonModifiers.compactXs,
'bg-red-500 hover:bg-red-600 text-white',
!canManageCustomTemplates && 'opacity-70',
deletingId === tpl.id && 'opacity-70 cursor-wait',
)}
>
{deletingId === tpl.id ? '删除中...' : '删除'}
</button>
</div>
</div>
</div>
))
)}
</div>
)}
</div>
)
}