From 78cc41b4534b510ee8a74307ef2e869ae2790f80 Mon Sep 17 00:00:00 2001 From: merrycoral Date: Wed, 29 Oct 2025 13:22:26 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EB=B0=8F=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/app/(main)/events/page.tsx | 206 +++++++++++++++----------- src/entities/event/api/eventApi.ts | 198 +++++++++++++++++++++++++ src/entities/event/model/types.ts | 173 +++++++++++++++++++++ src/entities/event/model/useEvents.ts | 200 +++++++++++++++++++++++++ 4 files changed, 694 insertions(+), 83 deletions(-) create mode 100644 src/entities/event/api/eventApi.ts create mode 100644 src/entities/event/model/types.ts create mode 100644 src/entities/event/model/useEvents.ts diff --git a/src/app/(main)/events/page.tsx b/src/app/(main)/events/page.tsx index bf80422..40bfc67 100644 --- a/src/app/(main)/events/page.tsx +++ b/src/app/(main)/events/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { useRouter } from 'next/navigation'; import { Box, @@ -37,78 +37,12 @@ import { } from '@mui/icons-material'; import Header from '@/shared/ui/Header'; import { cardStyles, colors, responsiveText } from '@/shared/lib/button-styles'; +import { useEvents } from '@/entities/event/model/useEvents'; +import type { EventStatus as ApiEventStatus } from '@/entities/event/model/types'; -// Mock ๋ฐ์ดํ„ฐ -const mockEvents = [ - { - id: '1', - title: '์‹ ๊ทœ๊ณ ๊ฐ ์œ ์น˜ ์ด๋ฒคํŠธ', - status: 'active' as const, - daysLeft: 5, - participants: 128, - targetParticipants: 200, - roi: 450, - startDate: '2025-11-01', - endDate: '2025-11-15', - prize: '์ปคํ”ผ ์ฟ ํฐ', - method: '์ „ํ™”๋ฒˆํ˜ธ ์ž…๋ ฅ', - isUrgent: true, - isPopular: false, - isHighROI: true, - isNew: false, - }, - { - id: '2', - title: '์žฌ๋ฐฉ๋ฌธ ์œ ๋„ ์ด๋ฒคํŠธ', - status: 'active' as const, - daysLeft: 12, - participants: 56, - targetParticipants: 100, - roi: 320, - startDate: '2025-11-05', - endDate: '2025-11-20', - prize: 'ํ• ์ธ ์ฟ ํฐ', - method: 'SNS ํŒ”๋กœ์šฐ', - isUrgent: false, - isPopular: false, - isHighROI: false, - isNew: false, - }, - { - id: '3', - title: '๋งค์ถœ์ฆ๋Œ€ ํ”„๋กœ๋ชจ์…˜', - status: 'ended' as const, - daysLeft: 0, - participants: 234, - targetParticipants: 150, - roi: 580, - startDate: '2025-10-15', - endDate: '2025-10-31', - prize: '์ƒํ’ˆ๊ถŒ', - method: '๊ตฌ๋งค ์ธ์ฆ', - isUrgent: false, - isPopular: true, - isHighROI: true, - isNew: false, - }, - { - id: '4', - title: '๋ด„๋งž์ด ํŠน๋ณ„ ์ด๋ฒคํŠธ', - status: 'scheduled' as const, - daysLeft: 30, - participants: 0, - targetParticipants: 300, - roi: 0, - startDate: '2025-12-01', - endDate: '2025-12-15', - prize: '์ฒดํ—˜๊ถŒ', - method: '์ด๋ฉ”์ผ ๋“ฑ๋ก', - isUrgent: false, - isPopular: false, - isHighROI: false, - isNew: true, - }, -]; +// ==================== API ์—ฐ๋™ ==================== +// Mock ๋ฐ์ดํ„ฐ๋ฅผ ์‹ค์ œ API ํ˜ธ์ถœ๋กœ ๊ต์ฒด +// ๋ฐฑ์—… ํŒŒ์ผ: page.tsx.backup type EventStatus = 'all' | 'active' | 'scheduled' | 'ended'; type Period = '1month' | '3months' | '6months' | '1year' | 'all'; @@ -123,8 +57,57 @@ export default function EventsPage() { const [currentPage, setCurrentPage] = useState(1); const itemsPerPage = 20; + // API ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ + const { events: apiEvents, loading, error, pageInfo, refetch } = useEvents({ + page: currentPage - 1, + size: itemsPerPage, + sort: 'createdAt', + order: 'desc' + }); + + // API ์ƒํƒœ๋ฅผ UI ์ƒํƒœ๋กœ ๋งคํ•‘ + const mapApiStatus = (apiStatus: ApiEventStatus): EventStatus => { + switch (apiStatus) { + case 'PUBLISHED': + return 'active'; + case 'DRAFT': + return 'scheduled'; + case 'ENDED': + return 'ended'; + default: + return 'all'; + } + }; + + // API ์ด๋ฒคํŠธ๋ฅผ UI ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ + const transformedEvents = apiEvents.map(event => ({ + id: event.eventId, + title: event.eventName || '์ œ๋ชฉ ์—†์Œ', + status: mapApiStatus(event.status), + startDate: event.startDate ? new Date(event.startDate).toLocaleDateString('ko-KR') : '-', + endDate: event.endDate ? new Date(event.endDate).toLocaleDateString('ko-KR') : '-', + prize: event.aiRecommendations[0]?.reward || '๊ฒฝํ’ˆ ์ •๋ณด ์—†์Œ', + method: event.aiRecommendations[0]?.participationMethod || '์ฐธ์—ฌ ๋ฐฉ๋ฒ• ์—†์Œ', + participants: event.participants || 0, + targetParticipants: event.targetParticipants || 0, + roi: event.roi || 0, + daysLeft: event.endDate + ? Math.ceil((new Date(event.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)) + : 0, + isUrgent: event.endDate + ? Math.ceil((new Date(event.endDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)) <= 3 + : false, + isPopular: event.participants && event.targetParticipants + ? (event.participants / event.targetParticipants) >= 0.8 + : false, + isHighROI: event.roi ? event.roi >= 300 : false, + isNew: event.createdAt + ? (Date.now() - new Date(event.createdAt).getTime()) < (7 * 24 * 60 * 60 * 1000) + : false, + })); + // ํ•„ํ„ฐ๋ง ๋ฐ ์ •๋ ฌ - const filteredEvents = mockEvents + const filteredEvents = transformedEvents .filter((event) => { const matchesSearch = event.title.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === 'all' || event.status === statusFilter; @@ -204,22 +187,26 @@ export default function EventsPage() { } }; - const calculateProgress = (event: (typeof mockEvents)[0]) => { + const calculateProgress = (event: typeof transformedEvents[0]) => { if (event.status !== 'active') return 0; - const total = new Date(event.endDate).getTime() - new Date(event.startDate).getTime(); - const elapsed = Date.now() - new Date(event.startDate).getTime(); + const startTime = new Date(event.startDate).getTime(); + const endTime = new Date(event.endDate).getTime(); + const total = endTime - startTime; + const elapsed = Date.now() - startTime; return Math.min(Math.max((elapsed / total) * 100, 0), 100); }; // ํ†ต๊ณ„ ๊ณ„์‚ฐ const stats = { - total: mockEvents.length, - active: mockEvents.filter((e) => e.status === 'active').length, - totalParticipants: mockEvents.reduce((sum, e) => sum + e.participants, 0), - avgROI: Math.round( - mockEvents.filter((e) => e.roi > 0).reduce((sum, e) => sum + e.roi, 0) / - mockEvents.filter((e) => e.roi > 0).length - ), + total: transformedEvents.length, + active: transformedEvents.filter((e) => e.status === 'active').length, + totalParticipants: transformedEvents.reduce((sum, e) => sum + e.participants, 0), + avgROI: transformedEvents.filter((e) => e.roi > 0).length > 0 + ? Math.round( + transformedEvents.filter((e) => e.roi > 0).reduce((sum, e) => sum + e.roi, 0) / + transformedEvents.filter((e) => e.roi > 0).length + ) + : 0, }; return ( @@ -237,6 +224,59 @@ export default function EventsPage() { maxWidth="lg" sx={{ pt: { xs: 4, sm: 8 }, pb: { xs: 4, sm: 6 }, px: { xs: 3, sm: 6, md: 10 } }} > + {/* Loading State */} + {loading && ( + + + + ์ด๋ฒคํŠธ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘... + + + )} + + {/* Error State */} + {error && ( + + + + + ์ด๋ฒคํŠธ ๋ชฉ๋ก์„ ๋ถˆ๋Ÿฌ์˜ค๋Š”๋ฐ ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค + + + {error.message} + + refetch()} + sx={{ + px: 3, + py: 1.5, + borderRadius: 2, + border: 'none', + bgcolor: '#DC2626', + color: 'white', + fontSize: '0.875rem', + fontWeight: 600, + cursor: 'pointer', + '&:hover': { bgcolor: '#B91C1C' }, + }} + > + ๋‹ค์‹œ ์‹œ๋„ + + + + )} + {/* Summary Statistics */} diff --git a/src/entities/event/api/eventApi.ts b/src/entities/event/api/eventApi.ts new file mode 100644 index 0000000..929eefe --- /dev/null +++ b/src/entities/event/api/eventApi.ts @@ -0,0 +1,198 @@ +import { apiClient } from '@/shared/api'; +import type { + GetEventsRequest, + GetEventsResponse, + EventDetail, + ApiResponse, + SelectObjectiveRequest, + EventCreatedResponse, + AiRecommendationRequest, + JobAcceptedResponse, + ImageGenerationRequest, + ImageGenerationResponse, +} from '../model/types'; + +/** + * Event API ๊ธฐ๋ณธ ๊ฒฝ๋กœ + * + * ์ฐธ๊ณ : apiClient๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ user-service(8081)๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ + * ๋ณ„๋„์˜ event API ํด๋ผ์ด์–ธํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. + * + * ํ˜„์žฌ๋Š” apiClient๋ฅผ ์‚ฌ์šฉํ•˜๋˜, baseURL์„ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ฉ๋‹ˆ๋‹ค. + */ +const EVENT_API_BASE = '/api/v1/events'; +const EVENT_HOST = process.env.NEXT_PUBLIC_EVENT_HOST || 'http://localhost:8080'; + +/** + * Event Service์šฉ API ํด๋ผ์ด์–ธํŠธ + * Event Service๋Š” ๋ณ„๋„ ํฌํŠธ(8080)์—์„œ ์‹คํ–‰๋˜๋ฏ€๋กœ ๋ณ„๋„ ํด๋ผ์ด์–ธํŠธ ์ƒ์„ฑ + */ +import axios from 'axios'; + +const eventApiClient = axios.create({ + baseURL: EVENT_HOST, + timeout: 30000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +// Request interceptor - JWT ํ† ํฐ ์ถ”๊ฐ€ +eventApiClient.interceptors.request.use( + (config) => { + console.log('๐Ÿš€ Event API Request:', { + method: config.method?.toUpperCase(), + url: config.url, + baseURL: config.baseURL, + params: config.params, + }); + + const token = localStorage.getItem('accessToken'); + if (token && config.headers) { + config.headers.Authorization = `Bearer ${token}`; + console.log('๐Ÿ”‘ Token added to Event API request'); + } + return config; + }, + (error) => { + console.error('โŒ Event API Request Error:', error); + return Promise.reject(error); + } +); + +// Response interceptor - ์—๋Ÿฌ ์ฒ˜๋ฆฌ +eventApiClient.interceptors.response.use( + (response) => { + console.log('โœ… Event API Response:', { + status: response.status, + url: response.config.url, + data: response.data, + }); + return response; + }, + (error) => { + console.error('โŒ Event API Error:', { + message: error.message, + status: error.response?.status, + statusText: error.response?.statusText, + url: error.config?.url, + data: error.response?.data, + }); + + if (error.response?.status === 401) { + console.warn('๐Ÿ”’ 401 Unauthorized - Redirecting to login'); + localStorage.removeItem('accessToken'); + if (typeof window !== 'undefined') { + window.location.href = '/login'; + } + } + return Promise.reject(error); + } +); + +/** + * Event API Service + * ์ด๋ฒคํŠธ ๊ด€๋ฆฌ API + */ +export const eventApi = { + /** + * ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ + */ + getEvents: async (params?: GetEventsRequest): Promise => { + console.log('๐Ÿ“ž eventApi.getEvents ํ˜ธ์ถœ', params); + const response = await eventApiClient.get(EVENT_API_BASE, { + params, + }); + return response.data; + }, + + /** + * ์ด๋ฒคํŠธ ์ƒ์„ธ ์กฐํšŒ + */ + getEvent: async (eventId: string): Promise> => { + console.log('๐Ÿ“ž eventApi.getEvent ํ˜ธ์ถœ', eventId); + const response = await eventApiClient.get>( + `${EVENT_API_BASE}/${eventId}` + ); + return response.data; + }, + + /** + * ์ด๋ฒคํŠธ ์ƒ์„ฑ (๋ชฉ์  ์„ ํƒ) + */ + createEvent: async ( + data: SelectObjectiveRequest + ): Promise> => { + console.log('๐Ÿ“ž eventApi.createEvent ํ˜ธ์ถœ', data); + const response = await eventApiClient.post>( + `${EVENT_API_BASE}/objectives`, + data + ); + return response.data; + }, + + /** + * ์ด๋ฒคํŠธ ์‚ญ์ œ + */ + deleteEvent: async (eventId: string): Promise> => { + console.log('๐Ÿ“ž eventApi.deleteEvent ํ˜ธ์ถœ', eventId); + const response = await eventApiClient.delete>( + `${EVENT_API_BASE}/${eventId}` + ); + return response.data; + }, + + /** + * ์ด๋ฒคํŠธ ๋ฐฐํฌ + */ + publishEvent: async (eventId: string): Promise> => { + console.log('๐Ÿ“ž eventApi.publishEvent ํ˜ธ์ถœ', eventId); + const response = await eventApiClient.post>( + `${EVENT_API_BASE}/${eventId}/publish` + ); + return response.data; + }, + + /** + * ์ด๋ฒคํŠธ ์ข…๋ฃŒ + */ + endEvent: async (eventId: string): Promise> => { + console.log('๐Ÿ“ž eventApi.endEvent ํ˜ธ์ถœ', eventId); + const response = await eventApiClient.post>( + `${EVENT_API_BASE}/${eventId}/end` + ); + return response.data; + }, + + /** + * AI ์ถ”์ฒœ ์š”์ฒญ + */ + requestAiRecommendations: async ( + eventId: string, + data: AiRecommendationRequest + ): Promise> => { + console.log('๐Ÿ“ž eventApi.requestAiRecommendations ํ˜ธ์ถœ', eventId, data); + const response = await eventApiClient.post>( + `${EVENT_API_BASE}/${eventId}/ai-recommendations`, + data + ); + return response.data; + }, + + /** + * ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์š”์ฒญ + */ + requestImageGeneration: async ( + eventId: string, + data: ImageGenerationRequest + ): Promise> => { + console.log('๐Ÿ“ž eventApi.requestImageGeneration ํ˜ธ์ถœ', eventId, data); + const response = await eventApiClient.post>( + `${EVENT_API_BASE}/${eventId}/images`, + data + ); + return response.data; + }, +}; + +export default eventApi; diff --git a/src/entities/event/model/types.ts b/src/entities/event/model/types.ts new file mode 100644 index 0000000..b3de860 --- /dev/null +++ b/src/entities/event/model/types.ts @@ -0,0 +1,173 @@ +/** + * Event ๋„๋ฉ”์ธ ํƒ€์ž… ์ •์˜ + * Event Service API ์‘๋‹ต ํ˜•์‹๊ณผ ์ผ์น˜ + */ + +/** + * ์ด๋ฒคํŠธ ์ƒํƒœ + */ +export type EventStatus = 'DRAFT' | 'PUBLISHED' | 'ENDED'; + +/** + * ์ด๋ฒคํŠธ ๋ชฉ์  + */ +export type EventObjective = + | 'CUSTOMER_ACQUISITION' + | 'Sales Promotion' + | 'Customer Retention' + | 'New Customer Acquisition' + | 'awareness' + | 'sales' + | 'new_customer'; + +/** + * ๋ฐฐํฌ ์ฑ„๋„ + */ +export type DistributionChannel = 'SMS' | 'EMAIL' | 'KAKAO' | 'PUSH'; + +/** + * ์ด๋ฒคํŠธ ์ด๋ฏธ์ง€ + */ +export interface EventImage { + imageId: string; + imageUrl: string; + prompt?: string; + isSelected: boolean; + createdAt: string; +} + +/** + * AI ์ถ”์ฒœ + */ +export interface AiRecommendation { + recommendationId: string; + eventName: string; + description: string; + reward: string; + participationMethod: string; + startDate: string; + endDate: string; + targetParticipants: number; + isSelected: boolean; + createdAt: string; +} + +/** + * ์ด๋ฒคํŠธ ์ƒ์„ธ ์ •๋ณด + */ +export interface EventDetail { + eventId: string; + userId: string; + storeId: string; + eventName: string; + description: string | null; + objective: EventObjective; + startDate: string | null; + endDate: string | null; + status: EventStatus; + selectedImageId: string | null; + selectedImageUrl: string | null; + participants: number | null; + targetParticipants: number | null; + roi: number | null; + generatedImages: EventImage[]; + aiRecommendations: AiRecommendation[]; + channels: DistributionChannel[]; + createdAt: string; + updatedAt: string; +} + +/** + * ํŽ˜์ด์ง€ ์‘๋‹ต + */ +export interface PageResponse { + content: T[]; + page: number; + size: number; + totalElements: number; + totalPages: number; + first: boolean; + last: boolean; +} + +/** + * API ํ‘œ์ค€ ์‘๋‹ต + */ +export interface ApiResponse { + success: boolean; + data: T; + timestamp: string; +} + +/** + * ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ ์š”์ฒญ + */ +export interface GetEventsRequest { + status?: EventStatus; + search?: string; + objective?: string; + page?: number; + size?: number; + sort?: string; + order?: 'asc' | 'desc'; +} + +/** + * ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ ์‘๋‹ต + */ +export type GetEventsResponse = ApiResponse>; + +/** + * ์ด๋ฒคํŠธ ๋ชฉ์  ์„ ํƒ ์š”์ฒญ + */ +export interface SelectObjectiveRequest { + objective: EventObjective; +} + +/** + * ์ด๋ฒคํŠธ ์ƒ์„ฑ ์‘๋‹ต + */ +export interface EventCreatedResponse { + eventId: string; + objective: EventObjective; + status: EventStatus; + createdAt: string; +} + +/** + * AI ์ถ”์ฒœ ์š”์ฒญ + */ +export interface AiRecommendationRequest { + storeCategory?: string; + targetAudience?: string; + budget?: number; + additionalInfo?: string; +} + +/** + * Job ์ˆ˜๋ฝ ์‘๋‹ต + */ +export interface JobAcceptedResponse { + jobId: string; + eventId: string; + status: 'PENDING' | 'PROCESSING' | 'COMPLETED' | 'FAILED'; + estimatedCompletionTime?: string; +} + +/** + * ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์š”์ฒญ + */ +export interface ImageGenerationRequest { + prompt: string; + numberOfImages?: number; + style?: string; +} + +/** + * ์ด๋ฏธ์ง€ ์ƒ์„ฑ ์‘๋‹ต + */ +export interface ImageGenerationResponse { + jobId: string; + eventId: string; + status: string; +} diff --git a/src/entities/event/model/useEvents.ts b/src/entities/event/model/useEvents.ts new file mode 100644 index 0000000..7eeb1d7 --- /dev/null +++ b/src/entities/event/model/useEvents.ts @@ -0,0 +1,200 @@ +import { useState, useEffect } from 'react'; +import { eventApi } from '../api/eventApi'; +import type { + EventDetail, + GetEventsRequest, + EventStatus, + PageResponse, +} from './types'; + +/** + * useEvents Hook + * ์ด๋ฒคํŠธ ๋ชฉ๋ก ์กฐํšŒ ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ + */ +export function useEvents(initialParams?: GetEventsRequest) { + const [events, setEvents] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [pageInfo, setPageInfo] = useState, 'content'>>({ + page: 0, + size: 20, + totalElements: 0, + totalPages: 0, + first: true, + last: true, + }); + + const fetchEvents = async (params?: GetEventsRequest) => { + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Fetching events with params:', params); + + const response = await eventApi.getEvents(params); + console.log('โœ… Events fetched:', response); + + if (response.success && response.data) { + setEvents(response.data.content); + setPageInfo({ + page: response.data.page, + size: response.data.size, + totalElements: response.data.totalElements, + totalPages: response.data.totalPages, + first: response.data.first, + last: response.data.last, + }); + } + } catch (err) { + console.error('โŒ Error fetching events:', err); + setError(err as Error); + } finally { + setLoading(false); + } + }; + + // ์ดˆ๊ธฐ ๋กœ๋“œ + useEffect(() => { + fetchEvents(initialParams); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { + events, + loading, + error, + pageInfo, + refetch: fetchEvents, + }; +} + +/** + * useEvent Hook + * ๋‹จ์ผ ์ด๋ฒคํŠธ ์กฐํšŒ ๋ฐ ์ƒํƒœ ๊ด€๋ฆฌ + */ +export function useEvent(eventId: string) { + const [event, setEvent] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const fetchEvent = async () => { + if (!eventId) return; + + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Fetching event:', eventId); + + const response = await eventApi.getEvent(eventId); + console.log('โœ… Event fetched:', response); + + if (response.success && response.data) { + setEvent(response.data); + } + } catch (err) { + console.error('โŒ Error fetching event:', err); + setError(err as Error); + } finally { + setLoading(false); + } + }; + + useEffect(() => { + fetchEvent(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [eventId]); + + return { + event, + loading, + error, + refetch: fetchEvent, + }; +} + +/** + * useEventActions Hook + * ์ด๋ฒคํŠธ ์ƒ์„ฑ, ์‚ญ์ œ, ๋ฐฐํฌ ๋“ฑ์˜ ์•ก์…˜ ๊ด€๋ฆฌ + */ +export function useEventActions() { + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const createEvent = async (objective: string) => { + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Creating event with objective:', objective); + + const response = await eventApi.createEvent({ objective: objective as any }); + console.log('โœ… Event created:', response); + + return response.data; + } catch (err) { + console.error('โŒ Error creating event:', err); + setError(err as Error); + throw err; + } finally { + setLoading(false); + } + }; + + const deleteEvent = async (eventId: string) => { + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Deleting event:', eventId); + + await eventApi.deleteEvent(eventId); + console.log('โœ… Event deleted'); + } catch (err) { + console.error('โŒ Error deleting event:', err); + setError(err as Error); + throw err; + } finally { + setLoading(false); + } + }; + + const publishEvent = async (eventId: string) => { + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Publishing event:', eventId); + + await eventApi.publishEvent(eventId); + console.log('โœ… Event published'); + } catch (err) { + console.error('โŒ Error publishing event:', err); + setError(err as Error); + throw err; + } finally { + setLoading(false); + } + }; + + const endEvent = async (eventId: string) => { + try { + setLoading(true); + setError(null); + console.log('๐Ÿ”„ Ending event:', eventId); + + await eventApi.endEvent(eventId); + console.log('โœ… Event ended'); + } catch (err) { + console.error('โŒ Error ending event:', err); + setError(err as Error); + throw err; + } finally { + setLoading(false); + } + }; + + return { + createEvent, + deleteEvent, + publishEvent, + endEvent, + loading, + error, + }; +}