175 lines
5.9 KiB
TypeScript
175 lines
5.9 KiB
TypeScript
/*
|
|
* M17 CMS 실배선 API 계약(SCR-35·36·37). 공용 클라이언트(../../api/client)만 사용 — client/endpoints/types 무수정.
|
|
* 정본: 백엔드 컨트롤러
|
|
* - GET /api/cms/contents → PageResponse<CmsContent>
|
|
* - POST /api/cms/contents → CmsContent(신규 초안)
|
|
* - PATCH /api/cms/contents/{id}/status?value= → CmsContent(전이·역전이 400)
|
|
* - GET/PUT /api/cms/contents/{id}/translations → CmsTranslation[]
|
|
* - GET/PUT /api/exhibitors/{exhibitorId}/microsite → Microsite
|
|
* - GET /api/public/microsites/{exhibitorId} → Microsite(공개·무인증)
|
|
*/
|
|
import { api } from '../../api/client';
|
|
import type { PageResponse } from '../../api/types';
|
|
|
|
export type FlowStatus = 'draft' | 'review' | 'approved' | 'published';
|
|
export type TransStatus = 'none' | 'ai' | 'reviewed';
|
|
export type Lang = 'ko' | 'en' | 'zh' | 'ja';
|
|
|
|
export interface CmsContent {
|
|
id: string;
|
|
eventId: string | null;
|
|
contentType: string | null;
|
|
title: string;
|
|
body: string | null;
|
|
status: FlowStatus;
|
|
lang: string | null;
|
|
scheduledAt: string | null;
|
|
signage: boolean;
|
|
mailing: boolean;
|
|
authorName: string | null;
|
|
publishedAt: string | null;
|
|
createdAt: string | null;
|
|
updatedAt: string | null;
|
|
}
|
|
|
|
export interface CmsTranslation {
|
|
contentId: string;
|
|
lang: Lang;
|
|
title: string | null;
|
|
body: string | null;
|
|
transStatus: TransStatus;
|
|
updatedAt: string | null;
|
|
}
|
|
|
|
export interface CmsContentVersion {
|
|
id: string;
|
|
contentId: string;
|
|
versionNo: number;
|
|
title: string | null;
|
|
body: string | null;
|
|
status: FlowStatus;
|
|
authorName: string | null;
|
|
note: string | null;
|
|
createdAt: string | null;
|
|
}
|
|
|
|
export interface CmsMedia {
|
|
id: string;
|
|
eventId: string | null;
|
|
fileName: string;
|
|
url: string;
|
|
contentType: string | null;
|
|
sizeBytes: number | null;
|
|
altText: string | null;
|
|
uploaderName: string | null;
|
|
createdAt: string | null;
|
|
}
|
|
|
|
export interface ContentUpdateRequest {
|
|
title: string;
|
|
body?: string | null;
|
|
scheduledAt?: string | null;
|
|
signage?: boolean | null;
|
|
mailing?: boolean | null;
|
|
}
|
|
|
|
export interface MicrositeSection {
|
|
id: string;
|
|
type: string;
|
|
visible: boolean;
|
|
order?: number;
|
|
payload?: Record<string, unknown>;
|
|
}
|
|
|
|
export interface Microsite {
|
|
exhibitorId: string;
|
|
eventId: string | null;
|
|
slug: string | null;
|
|
exhibitorName: string | null;
|
|
theme: string;
|
|
sections: MicrositeSection[];
|
|
seoTitle: string | null;
|
|
seoMeta: string | null;
|
|
langs: string | null;
|
|
status: 'draft' | 'published';
|
|
publishedAt: string | null;
|
|
updatedAt: string | null;
|
|
}
|
|
|
|
export interface ContentCreateRequest {
|
|
eventId?: string | null;
|
|
contentType?: string;
|
|
title: string;
|
|
body?: string;
|
|
lang?: string;
|
|
}
|
|
|
|
export interface TranslationSaveRequest {
|
|
lang: Lang;
|
|
title?: string | null;
|
|
body?: string | null;
|
|
transStatus?: TransStatus;
|
|
}
|
|
|
|
export interface MicrositeSaveRequest {
|
|
eventId?: string | null;
|
|
slug?: string | null;
|
|
exhibitorName?: string | null;
|
|
theme?: string;
|
|
sections?: MicrositeSection[];
|
|
seoTitle?: string | null;
|
|
seoMeta?: string | null;
|
|
langs?: string | null;
|
|
status?: 'draft' | 'published';
|
|
}
|
|
|
|
export const cmsApi = {
|
|
listContents: (p: { status?: string; type?: string; keyword?: string; page?: number; size?: number } = {}) => {
|
|
const qs = new URLSearchParams();
|
|
if (p.status) qs.set('status', p.status);
|
|
if (p.type) qs.set('type', p.type);
|
|
if (p.keyword) qs.set('keyword', p.keyword);
|
|
qs.set('page', String(p.page ?? 0));
|
|
qs.set('size', String(p.size ?? 50));
|
|
return api.get<PageResponse<CmsContent>>(`/api/cms/contents?${qs.toString()}`);
|
|
},
|
|
createContent: (body: ContentCreateRequest) => api.post<CmsContent>('/api/cms/contents', body),
|
|
getContent: (id: string) => api.get<CmsContent>(`/api/cms/contents/${encodeURIComponent(id)}`),
|
|
updateContent: (id: string, body: ContentUpdateRequest) =>
|
|
api.put<CmsContent>(`/api/cms/contents/${encodeURIComponent(id)}`, body),
|
|
transition: (id: string, value: FlowStatus) =>
|
|
api.patch<CmsContent>(`/api/cms/contents/${encodeURIComponent(id)}/status?value=${value}`),
|
|
getVersions: (id: string) =>
|
|
api.get<CmsContentVersion[]>(`/api/cms/contents/${encodeURIComponent(id)}/versions`),
|
|
rollback: (id: string, versionNo: number) =>
|
|
api.post<CmsContent>(`/api/cms/contents/${encodeURIComponent(id)}/rollback?versionNo=${versionNo}`),
|
|
getTranslations: (id: string) =>
|
|
api.get<CmsTranslation[]>(`/api/cms/contents/${encodeURIComponent(id)}/translations`),
|
|
saveTranslation: (id: string, body: TranslationSaveRequest) =>
|
|
api.put<CmsTranslation[]>(`/api/cms/contents/${encodeURIComponent(id)}/translations`, body),
|
|
/** AI 자동 번역(초벌) — 백엔드 미배선 시 501. 프론트는 degraded("준비 중")로 처리. */
|
|
aiTranslate: (id: string, lang: Lang) =>
|
|
api.post<CmsTranslation>(
|
|
`/api/cms/contents/${encodeURIComponent(id)}/translations/ai?lang=${lang}`,
|
|
),
|
|
// ── 미디어 라이브러리 ──
|
|
listMedia: (limit = 60) => api.get<CmsMedia[]>(`/api/cms/media?limit=${limit}`),
|
|
uploadMedia: (file: File, altText?: string) => {
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
if (altText) form.append('altText', altText);
|
|
return api.postForm<CmsMedia>('/api/cms/media', form);
|
|
},
|
|
// ── 공개 조회(무인증·게시 상태만) ──
|
|
publicByType: (type: string, limit = 50) =>
|
|
api.get<CmsContent[]>(`/api/public/cms/${encodeURIComponent(type)}?limit=${limit}`, {
|
|
anonymous: true,
|
|
}),
|
|
getMicrosite: (exhibitorId: string) =>
|
|
api.get<Microsite>(`/api/exhibitors/${encodeURIComponent(exhibitorId)}/microsite`),
|
|
saveMicrosite: (exhibitorId: string, body: MicrositeSaveRequest) =>
|
|
api.put<Microsite>(`/api/exhibitors/${encodeURIComponent(exhibitorId)}/microsite`, body),
|
|
getPublicMicrosite: (exhibitorId: string) =>
|
|
api.get<Microsite>(`/api/public/microsites/${encodeURIComponent(exhibitorId)}`, { anonymous: true }),
|
|
};
|