diff options
Diffstat (limited to 'lib/qna/table/utils.tsx')
| -rw-r--r-- | lib/qna/table/utils.tsx | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/lib/qna/table/utils.tsx b/lib/qna/table/utils.tsx new file mode 100644 index 00000000..f7f9effe --- /dev/null +++ b/lib/qna/table/utils.tsx @@ -0,0 +1,329 @@ +import { + CheckCircle2, + AlertCircle, + TrendingUp, + Building2, + Wrench, + Shield, + User, + Users, + Crown, + MessageSquare, + MessageCircle, + Clock, + Calendar, + Eye, + EyeOff, + } from "lucide-react" + + /** + * Q&A 상태에 따른 아이콘 반환 + */ + export function getQnaStatusIcon(status: string) { + switch (status) { + case "answered": + return CheckCircle2 + case "unanswered": + return AlertCircle + case "popular": + return TrendingUp + default: + return MessageSquare + } + } + + /** + * 벤더 타입에 따른 아이콘 반환 + */ + export function getVendorTypeIcon(vendorType: string) { + switch (vendorType) { + case "vendor": + return Building2 + case "techVendor": + return Wrench + default: + return Building2 + } + } + + /** + * 사용자 도메인에 따른 아이콘 반환 + */ + export function getDomainIcon(domain: string) { + switch (domain) { + case "partners": + return Users + case "tech": + return Wrench + case "admin": + return Shield + default: + return User + } + } + + /** + * Q&A 상태에 따른 배지 색상 반환 + */ + export function getQnaStatusBadge(qna: { + hasAnswers: boolean + isPopular: boolean + totalAnswers: number + totalComments: number + }) { + const badges = [] + + if (qna.hasAnswers) { + badges.push({ + label: "답변됨", + variant: "secondary" as const, + icon: CheckCircle2, + }) + } else { + badges.push({ + label: "답변 대기", + variant: "outline" as const, + icon: AlertCircle, + }) + } + + if (qna.isPopular) { + badges.push({ + label: "인기", + variant: "default" as const, + icon: TrendingUp, + }) + } + + return badges + } + + /** + * 도메인에 따른 사용자 라벨 반환 + */ + export function getDomainLabel(domain: string) { + switch (domain) { + case "partners": + return "협력업체" + case "tech": + return "기술업체" + case "admin": + return "관리자" + default: + return domain + } + } + + /** + * 벤더 타입에 따른 라벨 반환 + */ + export function getVendorTypeLabel(vendorType: string) { + switch (vendorType) { + case "vendor": + return "일반 벤더" + case "techVendor": + return "기술 벤더" + default: + return vendorType + } + } + + /** + * Q&A 활동 통계 포맷팅 + */ + export function formatQnaStats(qna: { + totalAnswers: number + totalComments: number + lastActivityAt?: Date | null + }) { + const stats = [] + + if (qna.totalAnswers > 0) { + stats.push(`답변 ${qna.totalAnswers}개`) + } + + if (qna.totalComments > 0) { + stats.push(`댓글 ${qna.totalComments}개`) + } + + if (stats.length === 0) { + return "활동 없음" + } + + return stats.join(" • ") + } + + /** + * 상대적 시간 포맷팅 (예: "3시간 전", "2일 전") + */ + export function formatRelativeTime(date: Date | string) { + const now = new Date() + const targetDate = new Date(date) + const diffInMilliseconds = now.getTime() - targetDate.getTime() + const diffInSeconds = Math.floor(diffInMilliseconds / 1000) + const diffInMinutes = Math.floor(diffInSeconds / 60) + const diffInHours = Math.floor(diffInMinutes / 60) + const diffInDays = Math.floor(diffInHours / 24) + const diffInMonths = Math.floor(diffInDays / 30) + const diffInYears = Math.floor(diffInDays / 365) + + if (diffInSeconds < 60) { + return "방금 전" + } else if (diffInMinutes < 60) { + return `${diffInMinutes}분 전` + } else if (diffInHours < 24) { + return `${diffInHours}시간 전` + } else if (diffInDays < 30) { + return `${diffInDays}일 전` + } else if (diffInMonths < 12) { + return `${diffInMonths}개월 전` + } else { + return `${diffInYears}년 전` + } + } + + /** + * Q&A 우선순위 계산 (정렬용) + */ + export function calculateQnaPriority(qna: { + hasAnswers: boolean + isPopular: boolean + totalAnswers: number + totalComments: number + lastActivityAt?: Date | null + createdAt: Date + }) { + let priority = 0 + + // 답변이 없는 질문에 높은 우선순위 + if (!qna.hasAnswers) { + priority += 100 + } + + // 인기 질문에 우선순위 추가 + if (qna.isPopular) { + priority += 50 + } + + // 최근 활동에 따른 우선순위 + if (qna.lastActivityAt) { + const daysSinceActivity = Math.floor( + (new Date().getTime() - new Date(qna.lastActivityAt).getTime()) / (1000 * 60 * 60 * 24) + ) + priority += Math.max(0, 30 - daysSinceActivity) // 최근 30일 내 활동 + } + + // 활동량에 따른 우선순위 + priority += Math.min(qna.totalAnswers * 2, 20) // 답변 수 (최대 20점) + priority += Math.min(qna.totalComments, 10) // 댓글 수 (최대 10점) + + return priority + } + + /** + * Q&A 텍스트 요약 (미리보기용) + */ + export function truncateQnaContent(content: string, maxLength: number = 100) { + // HTML 태그 제거 + const textContent = content.replace(/<[^>]*>/g, "").trim() + + if (textContent.length <= maxLength) { + return textContent + } + + return textContent.slice(0, maxLength).trim() + "..." + } + + /** + * Q&A 검색 키워드 하이라이팅 + */ + export function highlightSearchKeywords(text: string, keywords: string) { + if (!keywords.trim()) return text + + const keywordList = keywords.trim().split(/\s+/) + let highlightedText = text + + keywordList.forEach(keyword => { + const regex = new RegExp(`(${keyword})`, "gi") + highlightedText = highlightedText.replace( + regex, + '<mark class="bg-yellow-200 dark:bg-yellow-800">$1</mark>' + ) + }) + + return highlightedText + } + + /** + * Q&A 필터 조건 검증 + */ + export function validateQnaFilters(filters: { + search?: string + authorDomain?: string[] + vendorType?: string[] + hasAnswers?: string + dateRange?: { from?: Date; to?: Date } + }) { + const errors: string[] = [] + + // 검색어 길이 체크 + if (filters.search && filters.search.length > 100) { + errors.push("검색어는 100자 이하로 입력해주세요.") + } + + // 날짜 범위 체크 + if (filters.dateRange?.from && filters.dateRange?.to) { + if (filters.dateRange.from > filters.dateRange.to) { + errors.push("시작 날짜가 종료 날짜보다 늦을 수 없습니다.") + } + } + + return { + isValid: errors.length === 0, + errors, + } + } + + /** + * Q&A URL 생성 유틸리티 + */ + export function generateQnaUrls(qnaId: number) { + const baseUrl = typeof window !== "undefined" ? window.location.origin : "" + + return { + detail: `${baseUrl}/qna/${qnaId}`, + edit: `${baseUrl}/qna/${qnaId}/edit`, + share: `${baseUrl}/qna/${qnaId}?shared=true`, + } + } + + /** + * Q&A 메타데이터 생성 (SEO용) + */ + export function generateQnaMetadata(qna: { + title: string + content: string + authorName: string + companyName?: string | null + totalAnswers: number + createdAt: Date + }) { + const description = truncateQnaContent(qna.content, 160) + const keywords = [ + "Q&A", + "질문", + "답변", + qna.authorName, + qna.companyName, + ...qna.title.split(" ").slice(0, 5), // 제목의 첫 5단어 + ].filter(Boolean).join(", ") + + return { + title: `${qna.title} - Q&A`, + description, + keywords, + author: qna.authorName, + publishedTime: qna.createdAt.toISOString(), + articleTag: ["Q&A", "질문", "답변"], + } + }
\ No newline at end of file |
