Files
AutonetSellCar/temp_grantech_api.ts

763 lines
20 KiB
TypeScript

const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8001/api";
interface FetchOptions extends RequestInit {
token?: string;
}
async function fetchApi<T>(endpoint: string, options: FetchOptions = {}): Promise<T> {
const { token, ...fetchOptions } = options;
const headers: HeadersInit = {
"Content-Type": "application/json",
...options.headers,
};
if (token) {
(headers as Record<string, string>)["Authorization"] = `Bearer ${token}`;
}
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
...fetchOptions,
headers,
});
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Unknown error" }));
const errorMessage = typeof error.detail === 'string'
? error.detail
: JSON.stringify(error.detail) || `HTTP error! status: ${response.status}`;
throw new Error(errorMessage);
}
return response.json();
}
// Auth API
export const authApi = {
login: (username: string, password: string) =>
fetchApi<{ access_token: string; token_type: string }>("/auth/login", {
method: "POST",
body: JSON.stringify({ username, password }),
}),
getMe: (token: string) =>
fetchApi<{ id: number; username: string }>("/auth/me", { token }),
};
// Projects API (Public)
export interface Project {
id: number;
title: string;
subtitle?: string;
description?: string;
client?: string;
period?: string;
year?: number;
category?: string;
main_image?: string;
is_featured: boolean;
images: { id: number; image_url: string; caption?: string }[];
}
export interface ProjectAdmin {
id: number;
title_ko: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
subtitle_ko?: string;
subtitle_en?: string;
subtitle_ja?: string;
subtitle_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
client?: string;
period?: string;
year?: number;
category?: string;
main_image?: string;
is_featured: boolean;
is_active: boolean;
display_order: number;
images: { id: number; image_url: string; caption_ko?: string; caption_en?: string; caption_ja?: string; caption_zh?: string }[];
}
export const projectsApi = {
// Public
getAll: (lang: string = "ko", category?: string) => {
const params = new URLSearchParams({ lang });
if (category) params.append("category", category);
return fetchApi<Project[]>(`/projects/?${params}`);
},
getOne: (id: number, lang: string = "ko") =>
fetchApi<Project>(`/projects/${id}?lang=${lang}`),
// Admin
adminGetAll: (token: string) =>
fetchApi<ProjectAdmin[]>("/projects/admin/list", { token }),
adminGetOne: (id: number, token: string) =>
fetchApi<ProjectAdmin>(`/projects/admin/${id}`, { token }),
adminCreate: (data: Partial<ProjectAdmin>, token: string) =>
fetchApi<ProjectAdmin>("/projects/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<ProjectAdmin>, token: string) =>
fetchApi<ProjectAdmin>(`/projects/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/projects/admin/${id}`, {
method: "DELETE",
token,
}),
adminUploadImage: async (projectId: number, file: File, isMain: boolean, token: string) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${API_BASE_URL}/projects/admin/${projectId}/upload-image?is_main=${isMain}`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
},
body: formData,
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Upload failed" }));
throw new Error(error.detail);
}
return response.json();
},
adminDeleteImage: (imageId: number, token: string) =>
fetchApi<{ message: string }>(`/projects/admin/images/${imageId}`, {
method: "DELETE",
token,
}),
};
// Get upload URL
export function getUploadUrl(path: string): string {
if (!path) return "";
if (path.startsWith("http")) return path;
const baseUrl = process.env.NEXT_PUBLIC_API_URL?.replace(/\/api$/, "") || "http://localhost:8001";
// Handle API endpoints (e.g., /api/downloads/1/file)
if (path.startsWith("/api/")) {
return `${baseUrl}${path}`;
}
// Remove leading slash from path to avoid double slashes
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
return `${baseUrl}/${cleanPath}`;
}
// Banner API
export interface Banner {
id: number;
title?: string;
subtitle?: string;
image_url: string;
link_url?: string;
}
export interface BannerAdmin {
id: number;
title_ko?: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
subtitle_ko?: string;
subtitle_en?: string;
subtitle_ja?: string;
subtitle_zh?: string;
image_url: string;
link_url?: string;
is_active: boolean;
display_order: number;
}
export interface BannerSettings {
id: number;
transition_type: "fade" | "slide";
slide_interval: number;
}
// Solution API
export interface Solution {
id: number;
title: string;
subtitle?: string;
description?: string;
features: string[];
icon?: string;
color?: string;
main_image?: string;
images: { id: number; image_url: string; caption?: string }[];
}
export interface SolutionAdmin {
id: number;
title_ko: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
subtitle_ko?: string;
subtitle_en?: string;
subtitle_ja?: string;
subtitle_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
features_ko?: string;
features_en?: string;
features_ja?: string;
features_zh?: string;
icon?: string;
color?: string;
main_image?: string;
is_active: boolean;
display_order: number;
images: { id: number; image_url: string; caption_ko?: string; caption_en?: string; caption_ja?: string; caption_zh?: string }[];
}
export const solutionsApi = {
// Public
getAll: (lang: string = "ko") =>
fetchApi<Solution[]>(`/solutions/?lang=${lang}`),
getOne: (id: number, lang: string = "ko") =>
fetchApi<Solution>(`/solutions/${id}?lang=${lang}`),
// Admin
adminGetAll: (token: string) =>
fetchApi<SolutionAdmin[]>("/solutions/admin/list", { token }),
adminGetOne: (id: number, token: string) =>
fetchApi<SolutionAdmin>(`/solutions/admin/${id}`, { token }),
adminCreate: (data: Partial<SolutionAdmin>, token: string) =>
fetchApi<SolutionAdmin>("/solutions/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<SolutionAdmin>, token: string) =>
fetchApi<SolutionAdmin>(`/solutions/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/solutions/admin/${id}`, {
method: "DELETE",
token,
}),
adminUploadImage: async (solutionId: number, file: File, isMain: boolean, token: string) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${API_BASE_URL}/solutions/admin/${solutionId}/upload-image?is_main=${isMain}`,
{
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: formData,
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Upload failed" }));
throw new Error(error.detail);
}
return response.json();
},
adminDeleteImage: (imageId: number, token: string) =>
fetchApi<{ message: string }>(`/solutions/admin/images/${imageId}`, {
method: "DELETE",
token,
}),
};
// Product API
export interface Product {
id: number;
name: string;
category?: string;
description?: string;
detail?: string;
specifications?: string;
icon?: string;
main_image?: string;
images: { id: number; image_url: string; caption?: string }[];
}
export interface ProductAdmin {
id: number;
name_ko: string;
name_en?: string;
name_ja?: string;
name_zh?: string;
category_ko?: string;
category_en?: string;
category_ja?: string;
category_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
detail_ko?: string;
detail_en?: string;
detail_ja?: string;
detail_zh?: string;
specifications?: string;
icon?: string;
main_image?: string;
is_active: boolean;
display_order: number;
images: { id: number; image_url: string; caption_ko?: string; caption_en?: string; caption_ja?: string; caption_zh?: string }[];
}
export const productsApi = {
// Public
getAll: (lang: string = "ko") =>
fetchApi<Product[]>(`/products/?lang=${lang}`),
getOne: (id: number, lang: string = "ko") =>
fetchApi<Product>(`/products/${id}?lang=${lang}`),
// Admin
adminGetAll: (token: string) =>
fetchApi<ProductAdmin[]>("/products/admin/list", { token }),
adminGetOne: (id: number, token: string) =>
fetchApi<ProductAdmin>(`/products/admin/${id}`, { token }),
adminCreate: (data: Partial<ProductAdmin>, token: string) =>
fetchApi<ProductAdmin>("/products/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<ProductAdmin>, token: string) =>
fetchApi<ProductAdmin>(`/products/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/products/admin/${id}`, {
method: "DELETE",
token,
}),
adminUploadImage: async (productId: number, file: File, isMain: boolean, token: string) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${API_BASE_URL}/products/admin/${productId}/upload-image?is_main=${isMain}`,
{
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: formData,
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Upload failed" }));
throw new Error(error.detail);
}
return response.json();
},
adminDeleteImage: (imageId: number, token: string) =>
fetchApi<{ message: string }>(`/products/admin/images/${imageId}`, {
method: "DELETE",
token,
}),
};
// Contact API
export interface ContactSettings {
phone?: string;
email?: string;
address?: string;
weekday_hours?: string;
weekend_hours?: string;
}
export interface ContactSettingsAdmin {
id: number;
phone?: string;
email?: string;
address_ko?: string;
address_en?: string;
address_ja?: string;
address_zh?: string;
weekday_hours?: string;
weekend_hours?: string;
}
export interface ContactInquiry {
id: number;
name: string;
email: string;
phone?: string;
company?: string;
message: string;
status: string;
created_at?: string;
}
export const contactApi = {
// Public
getSettings: (lang: string = "ko") =>
fetchApi<ContactSettings>(`/contact/settings?lang=${lang}`),
submitInquiry: (data: { name: string; email: string; phone?: string; company?: string; message: string }) =>
fetchApi<ContactInquiry>("/contact/inquiry", {
method: "POST",
body: JSON.stringify(data),
}),
// Admin
adminGetSettings: (token: string) =>
fetchApi<ContactSettingsAdmin>("/contact/admin/settings", { token }),
adminUpdateSettings: (data: Partial<ContactSettingsAdmin>, token: string) =>
fetchApi<ContactSettingsAdmin>("/contact/admin/settings", {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminGetInquiries: (token: string, status?: string) => {
const params = status ? `?status=${status}` : "";
return fetchApi<ContactInquiry[]>(`/contact/admin/inquiries${params}`, { token });
},
adminGetInquiry: (id: number, token: string) =>
fetchApi<ContactInquiry>(`/contact/admin/inquiries/${id}`, { token }),
adminUpdateInquiryStatus: (id: number, status: string, token: string) =>
fetchApi<ContactInquiry>(`/contact/admin/inquiries/${id}/status`, {
method: "PUT",
body: JSON.stringify({ status }),
token,
}),
};
// Banner API
export const bannerApi = {
// Public
getAll: (lang: string = "ko") =>
fetchApi<Banner[]>(`/banners/?lang=${lang}`),
getSettings: () =>
fetchApi<BannerSettings>("/banners/settings"),
// Admin
adminGetAll: (token: string) =>
fetchApi<BannerAdmin[]>("/banners/admin/list", { token }),
adminGetOne: (id: number, token: string) =>
fetchApi<BannerAdmin>(`/banners/admin/${id}`, { token }),
adminCreate: (data: Partial<BannerAdmin>, token: string) =>
fetchApi<BannerAdmin>("/banners/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<BannerAdmin>, token: string) =>
fetchApi<BannerAdmin>(`/banners/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/banners/admin/${id}`, {
method: "DELETE",
token,
}),
adminUploadImage: async (file: File, token: string) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${API_BASE_URL}/banners/admin/upload-image`,
{
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: formData,
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Upload failed" }));
throw new Error(error.detail);
}
return response.json();
},
adminUpdateSettings: (data: Partial<BannerSettings>, token: string) =>
fetchApi<BannerSettings>("/banners/admin/settings", {
method: "PUT",
body: JSON.stringify(data),
token,
}),
};
// Downloads API
export interface Download {
id: number;
title: string;
description?: string;
category?: string;
file_url: string;
thumbnail?: string;
download_count: number;
}
export interface DownloadAdmin {
id: number;
title_ko: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
category_ko?: string;
category_en?: string;
category_ja?: string;
category_zh?: string;
file_url: string;
file_name?: string;
file_size?: number;
version?: string;
thumbnail?: string;
is_active: boolean;
display_order: number;
download_count: number;
}
export interface DownloadRequest {
id: number;
download_id: number;
download_title?: string;
email: string;
newsletter_agreed: boolean;
country?: string;
country_code?: string;
requested_at?: string;
}
export const downloadsApi = {
// Public
getAll: (lang: string = "ko") =>
fetchApi<Download[]>(`/downloads/?lang=${lang}`),
getOne: (id: number, lang: string = "ko") =>
fetchApi<Download>(`/downloads/${id}?lang=${lang}`),
requestDownload: (id: number, email?: string, newsletterAgreed?: boolean, skipEmail?: boolean) =>
fetchApi<{ download_url: string; file_name: string }>(`/downloads/${id}/request`, {
method: "POST",
body: JSON.stringify({
email: email || null,
newsletter_agreed: newsletterAgreed || false,
skip_email: skipEmail || false
}),
}),
// Admin
adminGetAll: (token: string) =>
fetchApi<DownloadAdmin[]>("/downloads/admin/list", { token }),
adminGetOne: (id: number, token: string) =>
fetchApi<DownloadAdmin>(`/downloads/admin/${id}`, { token }),
adminCreate: (data: Partial<DownloadAdmin>, token: string) =>
fetchApi<DownloadAdmin>("/downloads/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<DownloadAdmin>, token: string) =>
fetchApi<DownloadAdmin>(`/downloads/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/downloads/admin/${id}`, {
method: "DELETE",
token,
}),
adminUpload: async (file: File, fileType: string = "file", token: string) => {
const formData = new FormData();
formData.append("file", file);
const response = await fetch(
`${API_BASE_URL}/downloads/admin/upload?file_type=${fileType}`,
{
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: formData,
}
);
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: "Upload failed" }));
throw new Error(error.detail);
}
return response.json();
},
adminGetRequests: (token: string, newsletterOnly: boolean = false) =>
fetchApi<DownloadRequest[]>(`/downloads/admin/requests?newsletter_only=${newsletterOnly}`, { token }),
adminGetStats: (token: string) =>
fetchApi<{ total_requests: number; newsletter_subscribers: number; unique_emails: number }>(
"/downloads/admin/requests/stats",
{ token }
),
};
// Content Videos API (YouTube)
export interface ContentVideo {
id: number;
youtube_id: string;
title_ko: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
entity_type: "project" | "solution" | "product";
entity_id: number;
display_order: number;
is_active: boolean;
youtube_url: string;
youtube_embed_url: string;
thumbnail_url: string;
created_at?: string;
updated_at?: string;
}
export interface ContentVideoPublic {
id: number;
youtube_id: string;
title: string;
description?: string;
youtube_url: string;
youtube_embed_url: string;
thumbnail_url: string;
display_order: number;
}
export interface ContentVideoCreate {
youtube_id: string;
title_ko: string;
title_en?: string;
title_ja?: string;
title_zh?: string;
description_ko?: string;
description_en?: string;
description_ja?: string;
description_zh?: string;
entity_type: "project" | "solution" | "product";
entity_id: number;
display_order?: number;
is_active?: boolean;
}
export const contentVideosApi = {
// Public
getForEntity: (entityType: string, entityId: number, locale: string = "ko") =>
fetchApi<ContentVideoPublic[]>(
`/content-videos/entity/${entityType}/${entityId}?locale=${locale}`
),
// Admin
adminList: (token: string, entityType?: string, entityId?: number) => {
const params = new URLSearchParams();
if (entityType) params.append("entity_type", entityType);
if (entityId) params.append("entity_id", entityId.toString());
const query = params.toString();
return fetchApi<ContentVideo[]>(
`/content-videos/admin/list${query ? "?" + query : ""}`,
{ token }
);
},
adminGet: (id: number, token: string) =>
fetchApi<ContentVideo>(`/content-videos/admin/${id}`, { token }),
adminCreate: (data: ContentVideoCreate, token: string) =>
fetchApi<ContentVideo>("/content-videos/admin", {
method: "POST",
body: JSON.stringify(data),
token,
}),
adminUpdate: (id: number, data: Partial<ContentVideoCreate>, token: string) =>
fetchApi<ContentVideo>(`/content-videos/admin/${id}`, {
method: "PUT",
body: JSON.stringify(data),
token,
}),
adminDelete: (id: number, token: string) =>
fetchApi<{ message: string }>(`/content-videos/admin/${id}`, {
method: "DELETE",
token,
}),
adminReorder: (entityType: string, entityId: number, videoIds: number[], token: string) =>
fetchApi<{ message: string }>(
`/content-videos/admin/reorder?entity_type=${entityType}&entity_id=${entityId}`,
{
method: "PUT",
body: JSON.stringify(videoIds),
token,
}
),
};