87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
export type CtaType = 'none' | 'link' | 'dm' | 'follow'
|
||
|
||
export type ProductContextFields = {
|
||
brand: string
|
||
product: string
|
||
features: string
|
||
placementTone?: string
|
||
ctaType?: CtaType
|
||
ctaUrl?: string
|
||
}
|
||
|
||
export const CTA_TYPE_OPTIONS: Array<{ value: CtaType; label: string }> = [
|
||
{ value: 'none', label: '(無)不主動帶 CTA' },
|
||
{ value: 'link', label: '連結(可填網址)' },
|
||
{ value: 'dm', label: '私訊' },
|
||
{ value: 'follow', label: '追蹤' },
|
||
]
|
||
|
||
export const EMPTY_PRODUCT_CONTEXT: ProductContextFields = {
|
||
brand: '',
|
||
product: '',
|
||
features: '',
|
||
placementTone: '',
|
||
ctaType: 'none',
|
||
ctaUrl: '',
|
||
}
|
||
|
||
function normalizeCtaType(value: unknown): CtaType {
|
||
return value === 'link' || value === 'dm' || value === 'follow' ? value : 'none'
|
||
}
|
||
|
||
export function parseProductContext(raw: string | null | undefined): ProductContextFields {
|
||
if (!raw?.trim()) return { ...EMPTY_PRODUCT_CONTEXT }
|
||
try {
|
||
const parsed = JSON.parse(raw) as Partial<ProductContextFields>
|
||
if (parsed && typeof parsed === 'object') {
|
||
return {
|
||
brand: parsed.brand?.trim() ?? '',
|
||
product: parsed.product?.trim() ?? '',
|
||
features: parsed.features?.trim() ?? '',
|
||
placementTone: parsed.placementTone?.trim() ?? '',
|
||
ctaType: normalizeCtaType(parsed.ctaType),
|
||
ctaUrl: parsed.ctaUrl?.trim() ?? '',
|
||
}
|
||
}
|
||
} catch {
|
||
return { ...EMPTY_PRODUCT_CONTEXT, features: raw.trim() }
|
||
}
|
||
return { ...EMPTY_PRODUCT_CONTEXT }
|
||
}
|
||
|
||
export function serializeProductContext(fields: ProductContextFields): string {
|
||
const brand = fields.brand.trim()
|
||
const product = fields.product.trim()
|
||
const features = fields.features.trim()
|
||
const placementTone = fields.placementTone?.trim() ?? ''
|
||
const ctaType = normalizeCtaType(fields.ctaType)
|
||
const ctaUrl = fields.ctaUrl?.trim() ?? ''
|
||
if (!brand && !product && !features && !placementTone && ctaType === 'none' && !ctaUrl) {
|
||
return ''
|
||
}
|
||
return JSON.stringify({ brand, product, features, placementTone, ctaType, ctaUrl })
|
||
}
|
||
|
||
export function hasProductContext(raw: string | null | undefined): boolean {
|
||
const fields = parseProductContext(raw)
|
||
return !!(fields.brand || fields.product || fields.features)
|
||
}
|
||
|
||
export function summarizeProductContext(raw: string | null | undefined): string | null {
|
||
const fields = parseProductContext(raw)
|
||
const parts = [fields.brand, fields.product, fields.features].filter(Boolean)
|
||
return parts.length > 0 ? parts.join(' · ') : null
|
||
}
|
||
|
||
export function formatProductContextForPrompt(raw: string | null | undefined): string {
|
||
const fields = parseProductContext(raw)
|
||
const lines: string[] = []
|
||
if (fields.brand) lines.push(`品牌:${fields.brand}`)
|
||
if (fields.product) lines.push(`產品:${fields.product}`)
|
||
if (fields.features) lines.push(`特色/能幫上忙的地方:${fields.features}`)
|
||
if (fields.placementTone) lines.push(`置入語氣偏好:${fields.placementTone}`)
|
||
if (fields.ctaType === 'link' && fields.ctaUrl) lines.push(`留言 CTA 連結:${fields.ctaUrl}`)
|
||
else if (fields.ctaType === 'dm') lines.push('留言 CTA:引導私訊')
|
||
else if (fields.ctaType === 'follow') lines.push('留言 CTA:引導追蹤')
|
||
return lines.join('\n')
|
||
} |