157 lines
3.9 KiB
TypeScript
157 lines
3.9 KiB
TypeScript
import { defineStore } from 'pinia'
|
|
|
|
export type ProjectStatus = 'active' | 'archived' | 'draft'
|
|
|
|
export type ProjectSortBy = 'newest' | 'oldest' | 'most-leads' | 'most-variants'
|
|
|
|
export type ProjectStatusFilter = 'all' | ProjectStatus
|
|
|
|
export interface ProjectCard {
|
|
id: string
|
|
title: string
|
|
subtitle: string
|
|
status: ProjectStatus
|
|
leads: string
|
|
metricLabel: string
|
|
metricValue: string
|
|
trend: string
|
|
trendUp: boolean
|
|
variants: number
|
|
createdAt: string
|
|
}
|
|
|
|
const projectSource: ProjectCard[] = [
|
|
{
|
|
id: 'p1',
|
|
title: 'Q3 마케팅 캠페인',
|
|
subtitle: '2일 전 생성',
|
|
status: 'active',
|
|
leads: '1,240',
|
|
metricLabel: '전환율',
|
|
metricValue: '4.8%',
|
|
trend: '+12%',
|
|
trendUp: true,
|
|
variants: 3,
|
|
createdAt: '2026-03-01T09:00:00.000Z'
|
|
},
|
|
{
|
|
id: 'p2',
|
|
title: '블랙프라이데이 2023',
|
|
subtitle: '2023년 12월 1일 종료',
|
|
status: 'archived',
|
|
leads: '5,100',
|
|
metricLabel: '방문자',
|
|
metricValue: '42.5k',
|
|
trend: '종료',
|
|
trendUp: false,
|
|
variants: 3,
|
|
createdAt: '2023-12-01T03:00:00.000Z'
|
|
},
|
|
{
|
|
id: 'p3',
|
|
title: 'SaaS 웨비나 시리즈',
|
|
subtitle: '4시간 전 수정됨',
|
|
status: 'draft',
|
|
leads: '0',
|
|
metricLabel: '노출 수',
|
|
metricValue: '12k',
|
|
trend: '+18%',
|
|
trendUp: true,
|
|
variants: 2,
|
|
createdAt: '2026-03-04T01:20:00.000Z'
|
|
},
|
|
{
|
|
id: 'p4',
|
|
title: 'Product Hunt 런칭',
|
|
subtitle: '2주간 운영중',
|
|
status: 'active',
|
|
leads: '892',
|
|
metricLabel: '클릭률',
|
|
metricValue: '2.1%',
|
|
trend: '-2%',
|
|
trendUp: false,
|
|
variants: 1,
|
|
createdAt: '2026-02-12T16:30:00.000Z'
|
|
}
|
|
]
|
|
|
|
const toLeadNumber = (value: string) => {
|
|
const trimmed = value.trim().toLowerCase().replace(/,/g, '')
|
|
const numeric = Number(trimmed.replace('k', ''))
|
|
|
|
if (Number.isNaN(numeric)) {
|
|
return 0
|
|
}
|
|
|
|
return trimmed.endsWith('k') ? numeric * 1000 : numeric
|
|
}
|
|
|
|
export const useProjectsStore = defineStore('admin-projects', {
|
|
state: () => ({
|
|
projects: projectSource,
|
|
searchQuery: '',
|
|
statusFilter: 'all' as ProjectStatusFilter,
|
|
sortBy: 'newest' as ProjectSortBy
|
|
}),
|
|
getters: {
|
|
filteredProjects: (state): ProjectCard[] => {
|
|
const query = state.searchQuery.trim().toLowerCase()
|
|
const list = state.projects
|
|
.filter((project) => {
|
|
const target = `${project.title} ${project.subtitle}`.toLowerCase()
|
|
const matchQuery = query === '' || target.includes(query)
|
|
const matchStatus =
|
|
state.statusFilter === 'all' || project.status === state.statusFilter
|
|
|
|
return matchQuery && matchStatus
|
|
})
|
|
.sort((a, b) => {
|
|
if (state.sortBy === 'oldest') {
|
|
return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
|
|
}
|
|
|
|
if (state.sortBy === 'most-leads') {
|
|
return toLeadNumber(b.leads) - toLeadNumber(a.leads)
|
|
}
|
|
|
|
if (state.sortBy === 'most-variants') {
|
|
return b.variants - a.variants
|
|
}
|
|
|
|
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
})
|
|
|
|
return list
|
|
}
|
|
},
|
|
actions: {
|
|
createProject(input: { name: string; campaignName: string }) {
|
|
const now = new Date().toISOString()
|
|
const id = `p-${Date.now().toString(36)}`
|
|
|
|
this.projects.unshift({
|
|
id,
|
|
title: input.name.trim(),
|
|
subtitle: `${input.campaignName.trim()}에서 생성`,
|
|
status: 'draft',
|
|
leads: '0',
|
|
metricLabel: '전환율',
|
|
metricValue: '-',
|
|
trend: '신규',
|
|
trendUp: true,
|
|
variants: 0,
|
|
createdAt: now
|
|
})
|
|
},
|
|
setSearchQuery(value: string) {
|
|
this.searchQuery = value
|
|
},
|
|
setStatusFilter(value: ProjectStatusFilter) {
|
|
this.statusFilter = value
|
|
},
|
|
setSortBy(value: ProjectSortBy) {
|
|
this.sortBy = value
|
|
}
|
|
}
|
|
})
|