@restnfeel/agentc-starter-kit
Version:
한국어 기업용 CMS 모듈 - Task Master AI와 함께 빠르게 웹사이트를 구현할 수 있는 재사용 가능한 컴포넌트 시스템
343 lines (298 loc) • 9.69 kB
text/typescript
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;
};