UNPKG

@restnfeel/agentc-starter-kit

Version:

한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템

343 lines (298 loc) 9.69 kB
import { SiteTemplate, TemplateLibrary, TemplateCategory, } from "@/types/templates"; import { businessTemplate } from "./business-template"; import { portfolioTemplate } from "./portfolio-template"; import { Prisma } from "@prisma/client"; // 모든 템플릿 등록 export const templates: SiteTemplate[] = [businessTemplate, portfolioTemplate]; // 카테고리 정의 export const templateCategories = [ { id: "BUSINESS" as TemplateCategory, name: "비즈니스", description: "전문적인 기업 웹사이트를 위한 템플릿", icon: "briefcase", }, { id: "PORTFOLIO" as TemplateCategory, name: "포트폴리오", description: "개인 포트폴리오 및 창작 활동을 위한 템플릿", icon: "folder", }, { id: "ECOMMERCE" as TemplateCategory, name: "이커머스", description: "온라인 쇼핑몰을 위한 템플릿", icon: "shopping-cart", }, { id: "BLOG" as TemplateCategory, name: "블로그", description: "개인 블로그 및 콘텐츠 사이트를 위한 템플릿", icon: "edit", }, { id: "LANDING" as TemplateCategory, name: "랜딩 페이지", description: "마케팅 캠페인을 위한 랜딩 페이지 템플릿", icon: "target", }, { id: "AGENCY" as TemplateCategory, name: "에이전시", description: "디자인 에이전시 및 스튜디오를 위한 템플릿", icon: "users", }, ]; // 템플릿 라이브러리 구성 export const templateLibrary: TemplateLibrary = { templates, categories: templateCategories, featured: ["business-standard", "portfolio-creative"], popular: ["business-standard"], recent: ["portfolio-creative", "business-standard"], }; // 템플릿 검색 및 필터링 유틸리티 export class TemplateManager { private templates: SiteTemplate[]; constructor(templates: SiteTemplate[]) { this.templates = templates; } // ID로 템플릿 찾기 getById(id: string): SiteTemplate | undefined { return this.templates.find((template) => template.id === id); } // Slug로 템플릿 찾기 getBySlug(slug: string): SiteTemplate | undefined { return this.templates.find((template) => template.slug === slug); } // 카테고리별 템플릿 가져오기 getByCategory(category: TemplateCategory): SiteTemplate[] { return this.templates.filter((template) => template.category === category); } // 무료 템플릿만 가져오기 getFreeTemplates(): SiteTemplate[] { return this.templates.filter((template) => template.pricing === "FREE"); } // 프리미엄 템플릿만 가져오기 getPremiumTemplates(): SiteTemplate[] { return this.templates.filter((template) => template.pricing === "PREMIUM"); } // 활성화된 템플릿만 가져오기 getActiveTemplates(): SiteTemplate[] { return this.templates.filter((template) => template.isActive); } // 공개 템플릿만 가져오기 getPublicTemplates(): SiteTemplate[] { return this.templates.filter((template) => template.isPublic); } // 텍스트 검색 search(query: string): SiteTemplate[] { const searchTerm = query.toLowerCase().trim(); if (!searchTerm) { return this.templates; } return this.templates.filter( (template) => template.name.toLowerCase().includes(searchTerm) || template.description?.toLowerCase().includes(searchTerm) || template.tags.some((tag) => tag.toLowerCase().includes(searchTerm)) || template.features.some((feature) => feature.toLowerCase().includes(searchTerm) ) ); } // 고급 필터링 filter(options: { category?: TemplateCategory; pricing?: "FREE" | "PREMIUM" | "ENTERPRISE"; features?: string[]; tags?: string[]; search?: string; }): SiteTemplate[] { let filtered = [...this.templates]; // 카테고리 필터 if (options.category) { filtered = filtered.filter( (template) => template.category === options.category ); } // 가격 필터 if (options.pricing) { filtered = filtered.filter( (template) => template.pricing === options.pricing ); } // 기능 필터 if (options.features && options.features.length > 0) { filtered = filtered.filter((template) => options.features!.every((feature) => template.features.includes(feature) ) ); } // 태그 필터 if (options.tags && options.tags.length > 0) { filtered = filtered.filter((template) => options.tags!.some((tag) => template.tags.includes(tag)) ); } // 텍스트 검색 if (options.search) { const searchTerm = options.search.toLowerCase().trim(); filtered = filtered.filter( (template) => template.name.toLowerCase().includes(searchTerm) || template.description?.toLowerCase().includes(searchTerm) || template.tags.some((tag) => tag.toLowerCase().includes(searchTerm)) ); } return filtered; } // 정렬 sort( templates: SiteTemplate[], sortBy: "name" | "popularity" | "rating" | "date", order: "asc" | "desc" = "asc" ): SiteTemplate[] { const sorted = [...templates].sort((a, b) => { let comparison = 0; switch (sortBy) { case "name": comparison = a.name.localeCompare(b.name); break; case "popularity": comparison = (a.downloads || 0) - (b.downloads || 0); break; case "rating": comparison = (a.rating || 0) - (b.rating || 0); break; case "date": const dateA = a.updatedAt || a.createdAt || new Date(); const dateB = b.updatedAt || b.createdAt || new Date(); comparison = dateA.getTime() - dateB.getTime(); break; } return order === "desc" ? -comparison : comparison; }); return sorted; } // 추천 템플릿 가져오기 getRecommendations( currentTemplate?: SiteTemplate, limit: number = 3 ): SiteTemplate[] { let candidates = this.getActiveTemplates(); if (currentTemplate) { // 현재 템플릿 제외 candidates = candidates.filter( (template) => template.id !== currentTemplate.id ); // 같은 카테고리 우선 const sameCategory = candidates.filter( (template) => template.category === currentTemplate.category ); const otherCategory = candidates.filter( (template) => template.category !== currentTemplate.category ); candidates = [...sameCategory, ...otherCategory]; } return candidates.slice(0, limit); } // 템플릿 통계 getStats() { const total = this.templates.length; const active = this.getActiveTemplates().length; const free = this.getFreeTemplates().length; const premium = this.getPremiumTemplates().length; const categoryCounts = templateCategories.reduce((acc, category) => { acc[category.id] = this.getByCategory(category.id).length; return acc; }, {} as Record<TemplateCategory, number>); return { total, active, free, premium, categories: categoryCounts, }; } } // 전역 템플릿 매니저 인스턴스 export const templateManager = new TemplateManager(templates); // 유틸리티 함수들 export const getTemplateById = (id: string) => templateManager.getById(id); export const getTemplateBySlug = (slug: string) => templateManager.getBySlug(slug); export const getTemplatesByCategory = (category: TemplateCategory) => templateManager.getByCategory(category); export const searchTemplates = (query: string) => templateManager.search(query); export const filterTemplates = ( options: Parameters<typeof templateManager.filter>[0] ) => templateManager.filter(options); export const getTemplateStats = () => templateManager.getStats(); // 템플릿 생성을 위한 헬퍼 함수 export const createSiteFromTemplate = ( templateId: string, siteData: { name: string; slug: string; domain?: string; } ) => { const template = getTemplateById(templateId); if (!template) { throw new Error(`Template with ID ${templateId} not found`); } // 템플릿을 기반으로 사이트 데이터 생성 const site = { name: siteData.name, slug: siteData.slug, domain: siteData.domain, templateId: template.id, templateData: { version: template.version, installedAt: new Date(), } as unknown as Prisma.InputJsonValue, settings: { ...template.settings, seo: { ...template.settings.seo, defaultTitle: siteData.name, }, } as unknown as Prisma.InputJsonValue, sections: Object.values(template.sections).map((section) => ({ type: section.type, name: section.name, content: section.defaultContent as unknown as Prisma.InputJsonValue, styles: section.settings as unknown as Prisma.InputJsonValue, order: section.order, isVisible: true, isPublished: false, })), }; return site; }; // 템플릿 복제 함수 export const cloneTemplate = ( templateId: string, newData: Partial<SiteTemplate> ): SiteTemplate => { const original = getTemplateById(templateId); if (!original) { throw new Error(`Template with ID ${templateId} not found`); } const cloned: SiteTemplate = { ...original, id: newData.id || `${original.id}-copy`, name: newData.name || `${original.name} (Copy)`, slug: newData.slug || `${original.slug}-copy`, ...newData, createdAt: new Date(), updatedAt: new Date(), }; return cloned; };