update
This commit is contained in:
@@ -8,27 +8,27 @@ import {
|
||||
} from 'react-router-dom';
|
||||
import { ToastContainer } from 'react-toastify';
|
||||
import 'react-toastify/dist/ReactToastify.css';
|
||||
import { LoadingProvider, useNavigationLoading, useLoading } from './contexts/LoadingContext';
|
||||
import { CookieConsentProvider } from './contexts/CookieConsentContext';
|
||||
import { CurrencyProvider } from './contexts/CurrencyContext';
|
||||
import { CompanySettingsProvider } from './contexts/CompanySettingsContext';
|
||||
import { AuthModalProvider } from './contexts/AuthModalContext';
|
||||
import { AntibotProvider } from './contexts/AntibotContext';
|
||||
import OfflineIndicator from './components/common/OfflineIndicator';
|
||||
import CookieConsentBanner from './components/common/CookieConsentBanner';
|
||||
import CookiePreferencesModal from './components/common/CookiePreferencesModal';
|
||||
import AnalyticsLoader from './components/common/AnalyticsLoader';
|
||||
import Loading from './components/common/Loading';
|
||||
import Preloader from './components/common/Preloader';
|
||||
import ScrollToTop from './components/common/ScrollToTop';
|
||||
import AuthModalManager from './components/modals/AuthModalManager';
|
||||
import ResetPasswordRouteHandler from './components/auth/ResetPasswordRouteHandler';
|
||||
import ErrorBoundaryRoute from './components/common/ErrorBoundaryRoute';
|
||||
import { LoadingProvider, useNavigationLoading, useLoading } from './shared/contexts/LoadingContext';
|
||||
import { CookieConsentProvider } from './shared/contexts/CookieConsentContext';
|
||||
import { CurrencyProvider } from './features/payments/contexts/CurrencyContext';
|
||||
import { CompanySettingsProvider } from './shared/contexts/CompanySettingsContext';
|
||||
import { AuthModalProvider } from './features/auth/contexts/AuthModalContext';
|
||||
import { AntibotProvider } from './features/auth/contexts/AntibotContext';
|
||||
import OfflineIndicator from './shared/components/OfflineIndicator';
|
||||
import CookieConsentBanner from './shared/components/CookieConsentBanner';
|
||||
import CookiePreferencesModal from './shared/components/CookiePreferencesModal';
|
||||
import AnalyticsLoader from './shared/components/AnalyticsLoader';
|
||||
import Loading from './shared/components/Loading';
|
||||
import Preloader from './shared/components/Preloader';
|
||||
import ScrollToTop from './shared/components/ScrollToTop';
|
||||
import AuthModalManager from './features/auth/components/AuthModalManager';
|
||||
import ResetPasswordRouteHandler from './features/auth/components/ResetPasswordRouteHandler';
|
||||
import ErrorBoundaryRoute from './shared/components/ErrorBoundaryRoute';
|
||||
|
||||
import useAuthStore from './store/useAuthStore';
|
||||
import useFavoritesStore from './store/useFavoritesStore';
|
||||
|
||||
import { LayoutMain } from './components/layout';
|
||||
import LayoutMain from './shared/components/LayoutMain';
|
||||
import AdminLayout from './pages/AdminLayout';
|
||||
|
||||
import {
|
||||
@@ -37,9 +37,9 @@ import {
|
||||
StaffRoute,
|
||||
AccountantRoute,
|
||||
CustomerRoute
|
||||
} from './components/auth';
|
||||
} from './features/auth/components';
|
||||
|
||||
const HomePage = lazy(() => import('./pages/HomePage'));
|
||||
const HomePage = lazy(() => import('./features/content/pages/HomePage'));
|
||||
const DashboardPage = lazy(() => import('./pages/customer/DashboardPage'));
|
||||
const RoomListPage = lazy(() => import('./pages/customer/RoomListPage'));
|
||||
const RoomDetailPage = lazy(() => import('./pages/customer/RoomDetailPage'));
|
||||
@@ -59,16 +59,16 @@ const InvoiceEditPage = lazy(() => import('./pages/admin/InvoiceEditPage'));
|
||||
const ProfilePage = lazy(() => import('./pages/customer/ProfilePage'));
|
||||
const LoyaltyPage = lazy(() => import('./pages/customer/LoyaltyPage'));
|
||||
const GroupBookingPage = lazy(() => import('./pages/customer/GroupBookingPage'));
|
||||
const AboutPage = lazy(() => import('./pages/AboutPage'));
|
||||
const ContactPage = lazy(() => import('./pages/ContactPage'));
|
||||
const PrivacyPolicyPage = lazy(() => import('./pages/PrivacyPolicyPage'));
|
||||
const TermsPage = lazy(() => import('./pages/TermsPage'));
|
||||
const RefundsPolicyPage = lazy(() => import('./pages/RefundsPolicyPage'));
|
||||
const CancellationPolicyPage = lazy(() => import('./pages/CancellationPolicyPage'));
|
||||
const AccessibilityPage = lazy(() => import('./pages/AccessibilityPage'));
|
||||
const FAQPage = lazy(() => import('./pages/FAQPage'));
|
||||
const BlogPage = lazy(() => import('./pages/BlogPage'));
|
||||
const BlogDetailPage = lazy(() => import('./pages/BlogDetailPage'));
|
||||
const AboutPage = lazy(() => import('./features/content/pages/AboutPage'));
|
||||
const ContactPage = lazy(() => import('./features/content/pages/ContactPage'));
|
||||
const PrivacyPolicyPage = lazy(() => import('./features/content/pages/PrivacyPolicyPage'));
|
||||
const TermsPage = lazy(() => import('./features/content/pages/TermsPage'));
|
||||
const RefundsPolicyPage = lazy(() => import('./features/content/pages/RefundsPolicyPage'));
|
||||
const CancellationPolicyPage = lazy(() => import('./features/content/pages/CancellationPolicyPage'));
|
||||
const AccessibilityPage = lazy(() => import('./features/content/pages/AccessibilityPage'));
|
||||
const FAQPage = lazy(() => import('./features/content/pages/FAQPage'));
|
||||
const BlogPage = lazy(() => import('./features/content/pages/BlogPage'));
|
||||
const BlogDetailPage = lazy(() => import('./features/content/pages/BlogDetailPage'));
|
||||
|
||||
const AdminDashboardPage = lazy(() => import('./pages/admin/DashboardPage'));
|
||||
const InvoiceManagementPage = lazy(() => import('./pages/admin/InvoiceManagementPage'));
|
||||
@@ -111,7 +111,7 @@ const AccountantInvoiceManagementPage = lazy(() => import('./pages/accountant/In
|
||||
const AccountantAnalyticsDashboardPage = lazy(() => import('./pages/accountant/AnalyticsDashboardPage'));
|
||||
const AccountantLayout = lazy(() => import('./pages/AccountantLayout'));
|
||||
|
||||
const NotFoundPage = lazy(() => import('./pages/NotFoundPage'));
|
||||
const NotFoundPage = lazy(() => import('./shared/pages/NotFoundPage'));
|
||||
|
||||
// Component to track navigation changes - must be inside Router
|
||||
const NavigationTracker: React.FC = () => {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export { default as EmptyState } from './EmptyState';
|
||||
export { default as ErrorBoundary } from './ErrorBoundary';
|
||||
export { default as Loading } from './Loading';
|
||||
export { default as OptimizedImage } from './OptimizedImage';
|
||||
export { default as Pagination } from './Pagination';
|
||||
export { default as PaymentMethodSelector } from './PaymentMethodSelector';
|
||||
export { default as PaymentStatusBadge } from './PaymentStatusBadge';
|
||||
export { default as ConfirmationDialog } from './ConfirmationDialog';
|
||||
export { default as GlobalLoading } from './GlobalLoading';
|
||||
export { default as OfflineIndicator } from './OfflineIndicator';
|
||||
export { default as Skeleton } from './Skeleton';
|
||||
export { default as ScrollToTop } from './ScrollToTop';
|
||||
export { default as ExportButton } from './ExportButton';
|
||||
|
||||
249
Frontend/src/features/ai/components/AIAssistantWidget.tsx
Normal file
249
Frontend/src/features/ai/components/AIAssistantWidget.tsx
Normal file
@@ -0,0 +1,249 @@
|
||||
/**
|
||||
* AI Assistant Widget Component
|
||||
* Provides an AI-powered chat interface for hotel management queries
|
||||
*/
|
||||
|
||||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import { Send, Bot, X, Minimize2, Maximize2, Loader2 } from 'lucide-react';
|
||||
import { chatWithAI, AIChatResponse } from '../services/aiAssistantService';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
interface Message {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
intent?: string;
|
||||
}
|
||||
|
||||
interface AIAssistantWidgetProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const AIAssistantWidget: React.FC<AIAssistantWidgetProps> = ({ className = '' }) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const [isMinimized, setIsMinimized] = useState(false);
|
||||
const [messages, setMessages] = useState<Message[]>([
|
||||
{
|
||||
id: '1',
|
||||
role: 'assistant',
|
||||
content: 'Hello! I\'m your AI assistant. I can help you with questions about rooms, bookings, invoices, payments, and chat messages. How can I assist you today?',
|
||||
timestamp: new Date(),
|
||||
},
|
||||
]);
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const scrollToBottom = () => {
|
||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom();
|
||||
}, [messages]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
const handleSend = async () => {
|
||||
if (!inputValue.trim() || isLoading) return;
|
||||
|
||||
const userMessage: Message = {
|
||||
id: Date.now().toString(),
|
||||
role: 'user',
|
||||
content: inputValue.trim(),
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
setMessages((prev) => [...prev, userMessage]);
|
||||
setInputValue('');
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const response: AIChatResponse = await chatWithAI(userMessage.content);
|
||||
|
||||
const assistantMessage: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
role: 'assistant',
|
||||
content: response.response,
|
||||
timestamp: new Date(),
|
||||
intent: response.intent,
|
||||
};
|
||||
|
||||
setMessages((prev) => [...prev, assistantMessage]);
|
||||
} catch (error: any) {
|
||||
console.error('AI Assistant Error:', error);
|
||||
|
||||
// Extract error message
|
||||
let errorMessage = 'I apologize, but I encountered an error. Please try again.';
|
||||
|
||||
if (error.response) {
|
||||
// Server responded with error
|
||||
const detail = error.response.data?.detail || error.response.data?.message;
|
||||
if (detail) {
|
||||
errorMessage = `Error: ${detail}`;
|
||||
toast.error(detail);
|
||||
} else {
|
||||
toast.error(`Error ${error.response.status}: ${error.response.statusText}`);
|
||||
}
|
||||
} else if (error.request) {
|
||||
// Request made but no response
|
||||
errorMessage = 'Unable to connect to the server. Please check your connection.';
|
||||
toast.error('Connection error. Please check your network.');
|
||||
} else {
|
||||
// Something else happened
|
||||
errorMessage = `Error: ${error.message || 'Unknown error'}`;
|
||||
toast.error(error.message || 'An unexpected error occurred');
|
||||
}
|
||||
|
||||
const errorMsg: Message = {
|
||||
id: (Date.now() + 1).toString(),
|
||||
role: 'assistant',
|
||||
content: errorMessage,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
setMessages((prev) => [...prev, errorMsg]);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
handleSend();
|
||||
}
|
||||
};
|
||||
|
||||
const clearChat = () => {
|
||||
setMessages([
|
||||
{
|
||||
id: '1',
|
||||
role: 'assistant',
|
||||
content: 'Hello! I\'m your AI assistant. How can I help you today?',
|
||||
timestamp: new Date(),
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
if (!isOpen) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsOpen(true)}
|
||||
className={`fixed bottom-6 right-6 w-14 h-14 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-full shadow-2xl hover:from-amber-600 hover:to-amber-700 transition-all duration-300 flex items-center justify-center z-50 ${className}`}
|
||||
aria-label="Open AI Assistant"
|
||||
>
|
||||
<Bot className="w-6 h-6" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`fixed bottom-6 right-6 w-96 ${isMinimized ? 'h-16' : 'h-[600px]'} bg-white rounded-2xl shadow-2xl border border-slate-200 flex flex-col z-50 transition-all duration-300 ${className}`}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 px-4 py-3 rounded-t-2xl flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Bot className="w-5 h-5 text-amber-400" />
|
||||
<h3 className="text-white font-semibold">AI Assistant</h3>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={clearChat}
|
||||
className="text-amber-200 hover:text-white text-xs px-2 py-1 rounded hover:bg-slate-700/50 transition-colors"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsMinimized(!isMinimized)}
|
||||
className="text-amber-200 hover:text-white p-1.5 rounded hover:bg-slate-700/50 transition-colors"
|
||||
aria-label={isMinimized ? 'Maximize' : 'Minimize'}
|
||||
>
|
||||
{isMinimized ? <Maximize2 className="w-4 h-4" /> : <Minimize2 className="w-4 h-4" />}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="text-amber-200 hover:text-white p-1.5 rounded hover:bg-slate-700/50 transition-colors"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isMinimized && (
|
||||
<>
|
||||
{/* Messages */}
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-4 bg-gradient-to-b from-slate-50 to-white">
|
||||
{messages.map((message) => (
|
||||
<div
|
||||
key={message.id}
|
||||
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
||||
>
|
||||
<div
|
||||
className={`max-w-[80%] rounded-2xl px-4 py-2 ${
|
||||
message.role === 'user'
|
||||
? 'bg-gradient-to-r from-amber-500 to-amber-600 text-white'
|
||||
: 'bg-white border border-slate-200 text-slate-700 shadow-sm'
|
||||
}`}
|
||||
>
|
||||
<p className="text-sm whitespace-pre-wrap">{message.content}</p>
|
||||
{message.intent && message.role === 'assistant' && (
|
||||
<p className="text-xs opacity-70 mt-1">Intent: {message.intent}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{isLoading && (
|
||||
<div className="flex justify-start">
|
||||
<div className="bg-white border border-slate-200 rounded-2xl px-4 py-2 shadow-sm">
|
||||
<Loader2 className="w-4 h-4 animate-spin text-amber-500" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
{/* Input */}
|
||||
<div className="border-t border-slate-200 p-4 bg-white rounded-b-2xl">
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
value={inputValue}
|
||||
onChange={(e) => setInputValue(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
placeholder="Ask about rooms, bookings, invoices..."
|
||||
className="flex-1 px-4 py-2 border-2 border-slate-200 rounded-xl focus:border-amber-400 focus:ring-4 focus:ring-amber-100 transition-all duration-200 text-slate-700"
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<button
|
||||
onClick={handleSend}
|
||||
disabled={!inputValue.trim() || isLoading}
|
||||
className="px-4 py-2 bg-gradient-to-r from-amber-500 to-amber-600 text-white rounded-xl hover:from-amber-600 hover:to-amber-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 shadow-lg hover:shadow-xl flex items-center justify-center"
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
<Send className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<p className="text-xs text-slate-500 mt-2">
|
||||
Try: "Which rooms are occupied?" or "Show me booking BK12345"
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AIAssistantWidget;
|
||||
|
||||
169
Frontend/src/features/ai/services/aiAssistantService.ts
Normal file
169
Frontend/src/features/ai/services/aiAssistantService.ts
Normal file
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* AI Assistant Service
|
||||
* Handles communication with the AI assistant backend
|
||||
*/
|
||||
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface AIChatRequest {
|
||||
message: string;
|
||||
context?: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface AIChatResponse {
|
||||
response: string;
|
||||
intent: string;
|
||||
data_used: Record<string, any>;
|
||||
timestamp: string;
|
||||
user_role?: string; // Added: user role information
|
||||
}
|
||||
|
||||
export interface SystemStatus {
|
||||
user_role?: string; // Added: user role information
|
||||
timestamp: string;
|
||||
room_status: {
|
||||
total_rooms: number;
|
||||
available: number;
|
||||
occupied: number;
|
||||
maintenance?: number;
|
||||
cleaning?: number;
|
||||
occupancy_rate: number;
|
||||
rooms_with_active_bookings?: number;
|
||||
};
|
||||
booking_summary?: {
|
||||
upcoming_checkins: number;
|
||||
upcoming_checkouts: number;
|
||||
active_bookings: number;
|
||||
pending_bookings: number;
|
||||
};
|
||||
user_bookings?: Array<{
|
||||
booking_number: string;
|
||||
status: string;
|
||||
check_in: string;
|
||||
check_out: string;
|
||||
total_price: number;
|
||||
room_number?: string;
|
||||
room_type?: string;
|
||||
}>;
|
||||
payment_summary?: {
|
||||
total_revenue: number;
|
||||
pending_amount: number;
|
||||
completed_payments: number;
|
||||
};
|
||||
invoice_summary?: {
|
||||
total_invoices: number;
|
||||
overdue_invoices: number;
|
||||
status_breakdown?: Record<string, number>;
|
||||
};
|
||||
user_invoices?: Array<{
|
||||
invoice_number: string;
|
||||
status: string;
|
||||
total_amount: number;
|
||||
balance_due: number;
|
||||
due_date: string;
|
||||
booking_number?: string;
|
||||
}>;
|
||||
user_payments?: Array<{
|
||||
id: number;
|
||||
amount: number;
|
||||
payment_method: string;
|
||||
payment_status: string;
|
||||
payment_date: string;
|
||||
booking_number?: string;
|
||||
}>;
|
||||
unanswered_chats?: Array<{
|
||||
chat_id: number;
|
||||
visitor_name: string;
|
||||
last_message: string;
|
||||
waiting_hours: number;
|
||||
}>;
|
||||
room_problems?: Array<{
|
||||
room_number: string;
|
||||
issue_type: string;
|
||||
description: string;
|
||||
}>;
|
||||
application_knowledge?: {
|
||||
features: string[];
|
||||
role_info: Record<string, any>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface OccupiedRoom {
|
||||
room_number: string;
|
||||
room_type: string;
|
||||
floor: number;
|
||||
booking_id: number;
|
||||
guest_name: string;
|
||||
check_in: string;
|
||||
check_out: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the AI assistant
|
||||
*/
|
||||
export const chatWithAI = async (
|
||||
message: string,
|
||||
context?: Record<string, any>
|
||||
): Promise<AIChatResponse> => {
|
||||
const response = await apiClient.post<{ status: string; data: AIChatResponse }>(
|
||||
'/ai-assistant/chat',
|
||||
{ message, context }
|
||||
);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get comprehensive system status
|
||||
*/
|
||||
export const getAIStatus = async (): Promise<SystemStatus> => {
|
||||
const response = await apiClient.get<{ status: string; data: SystemStatus }>(
|
||||
'/ai-assistant/status'
|
||||
);
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get list of occupied rooms
|
||||
*/
|
||||
export const getOccupiedRooms = async (
|
||||
roomNumber?: string
|
||||
): Promise<OccupiedRoom[]> => {
|
||||
const response = await apiClient.get<{ status: string; data: { rooms: OccupiedRoom[] } }>(
|
||||
'/ai-assistant/rooms/occupied',
|
||||
{ params: roomNumber ? { room_number: roomNumber } : {} }
|
||||
);
|
||||
return response.data.data.rooms;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get rooms with problems
|
||||
*/
|
||||
export const getRoomProblems = async (): Promise<Array<{
|
||||
room_number: string;
|
||||
issue_type: string;
|
||||
description: string;
|
||||
}>> => {
|
||||
const response = await apiClient.get<{ status: string; data: { problems: any[] } }>(
|
||||
'/ai-assistant/rooms/problems'
|
||||
);
|
||||
return response.data.data.problems;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get unanswered chat messages
|
||||
*/
|
||||
export const getUnansweredChats = async (
|
||||
hours: number = 24
|
||||
): Promise<Array<{
|
||||
chat_id: number;
|
||||
visitor_name: string;
|
||||
last_message: string;
|
||||
waiting_hours: number;
|
||||
}>> => {
|
||||
const response = await apiClient.get<{ status: string; data: { chats: any[] } }>(
|
||||
'/ai-assistant/chats/unanswered',
|
||||
{ params: { hours } }
|
||||
);
|
||||
return response.data.data.chats;
|
||||
};
|
||||
|
||||
0
Frontend/src/features/ai/services/index.ts
Normal file
0
Frontend/src/features/ai/services/index.ts
Normal file
@@ -6,8 +6,8 @@ import {
|
||||
Square,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { exportData } from '../../utils/exportUtils';
|
||||
import analyticsService from '../../services/api/analyticsService';
|
||||
import { exportData } from '../../../shared/utils/exportUtils';
|
||||
import analyticsService from '../services/analyticsService';
|
||||
|
||||
interface ReportMetric {
|
||||
id: string;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
// ==================== REVENUE ANALYTICS ====================
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface AuditLog {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface CustomerDashboardStats {
|
||||
total_bookings: number;
|
||||
0
Frontend/src/features/analytics/services/index.ts
Normal file
0
Frontend/src/features/analytics/services/index.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface ReportData {
|
||||
total_bookings: number;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
interface AccountantRouteProps {
|
||||
children: React.ReactNode;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
interface AdminRouteProps {
|
||||
children: React.ReactNode;
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
import LoginModal from './LoginModal';
|
||||
import RegisterModal from './RegisterModal';
|
||||
import ForgotPasswordModal from './ForgotPasswordModal';
|
||||
import ResetPasswordModal from './ResetPasswordModal';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
|
||||
const AuthModalManager: React.FC = () => {
|
||||
const { isOpen, modalType, resetPasswordParams, openModal } = useAuthModal();
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
interface CustomerRouteProps {
|
||||
children: React.ReactNode;
|
||||
@@ -2,12 +2,12 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { X, Mail, ArrowLeft, Send, Loader2, CheckCircle } from 'lucide-react';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { forgotPasswordSchema, ForgotPasswordFormData } from '../../utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import { useAntibotForm } from '../../hooks/useAntibotForm';
|
||||
import HoneypotField from '../common/HoneypotField';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { forgotPasswordSchema, ForgotPasswordFormData } from '../../../shared/utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
import { useAntibotForm } from '../hooks/useAntibotForm';
|
||||
import HoneypotField from '../../../shared/components/HoneypotField';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
const ForgotPasswordModal: React.FC = () => {
|
||||
@@ -2,16 +2,16 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { X, Eye, EyeOff, LogIn, Loader2, Mail, Lock, Shield, ArrowLeft } from 'lucide-react';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { loginSchema, LoginFormData } from '../../utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { loginSchema, LoginFormData } from '../../../shared/utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
import * as yup from 'yup';
|
||||
import { toast } from 'react-toastify';
|
||||
import Recaptcha from '../common/Recaptcha';
|
||||
import { recaptchaService } from '../../services/api/systemSettingsService';
|
||||
import { useAntibotForm } from '../../hooks/useAntibotForm';
|
||||
import HoneypotField from '../common/HoneypotField';
|
||||
import Recaptcha from '../../../shared/components/Recaptcha';
|
||||
import { recaptchaService } from '../../../features/system/services/systemSettingsService';
|
||||
import { useAntibotForm } from '../hooks/useAntibotForm';
|
||||
import HoneypotField from '../../../shared/components/HoneypotField';
|
||||
|
||||
const mfaTokenSchema = yup.object().shape({
|
||||
mfaToken: yup
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
interface ProtectedRouteProps {
|
||||
children: React.ReactNode;
|
||||
@@ -2,15 +2,15 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { X, Eye, EyeOff, UserPlus, Loader2, Mail, Lock, User, Phone, CheckCircle2, XCircle } from 'lucide-react';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { registerSchema, RegisterFormData } from '../../utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { registerSchema, RegisterFormData } from '../../../shared/utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import Recaptcha from '../common/Recaptcha';
|
||||
import { recaptchaService } from '../../services/api/systemSettingsService';
|
||||
import { useAntibotForm } from '../../hooks/useAntibotForm';
|
||||
import HoneypotField from '../common/HoneypotField';
|
||||
import Recaptcha from '../../../shared/components/Recaptcha';
|
||||
import { recaptchaService } from '../../system/services/systemSettingsService';
|
||||
import { useAntibotForm } from '../hooks/useAntibotForm';
|
||||
import HoneypotField from '../../../shared/components/HoneypotField';
|
||||
|
||||
const PasswordRequirement: React.FC<{ met: boolean; text: string }> = ({ met, text }) => (
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs font-light">
|
||||
@@ -2,10 +2,10 @@ import React, { useState, useEffect } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { yupResolver } from '@hookform/resolvers/yup';
|
||||
import { X, Eye, EyeOff, Lock, Loader2, CheckCircle2, XCircle, AlertCircle, KeyRound } from 'lucide-react';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { resetPasswordSchema, ResetPasswordFormData } from '../../utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { resetPasswordSchema, ResetPasswordFormData } from '../../../shared/utils/validationSchemas';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
const PasswordRequirement: React.FC<{ met: boolean; text: string }> = ({ met, text }) => (
|
||||
<div className="flex items-center gap-1.5 sm:gap-2 text-[10px] sm:text-xs">
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
const ResetPasswordRouteHandler: React.FC = () => {
|
||||
const { token } = useParams<{ token: string }>();
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useAuthModal } from '../../contexts/AuthModalContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useAuthModal } from '../contexts/AuthModalContext';
|
||||
|
||||
interface StaffRouteProps {
|
||||
children: React.ReactNode;
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
clearRateLimit,
|
||||
getFingerprintHash,
|
||||
type AntibotValidationResult,
|
||||
} from '../utils/antibot';
|
||||
} from '../../../shared/utils/antibot';
|
||||
|
||||
interface AntibotContextType {
|
||||
timing: FormTiming;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface LoginCredentials {
|
||||
email: string;
|
||||
3
Frontend/src/features/auth/services/index.ts
Normal file
3
Frontend/src/features/auth/services/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// Auth services
|
||||
export * from './authService';
|
||||
export * from './userService';
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { X, AlertTriangle, XCircle, Loader2, Info } from 'lucide-react';
|
||||
import { cancelBooking, type Booking } from '../../services/api/bookingService';
|
||||
import { cancelBooking, type Booking } from '../services/bookingService';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../../payments/hooks/useFormatCurrency';
|
||||
|
||||
interface CancelBookingModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -18,28 +18,29 @@ import {
|
||||
Receipt,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getRoomById, getRoomBookedDates, type Room } from '../../services/api/roomService';
|
||||
import { getRoomById, getRoomBookedDates, type Room } from '../../rooms/services/roomService';
|
||||
import {
|
||||
createBooking,
|
||||
checkRoomAvailability,
|
||||
type BookingData,
|
||||
} from '../../services/api/bookingService';
|
||||
import { serviceService, Service, promotionService, Promotion } from '../../services/api';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
} from '../services/bookingService';
|
||||
import serviceService, { Service } from '../../hotel_services/services/serviceService';
|
||||
import promotionService, { Promotion } from '../../loyalty/services/promotionService';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import {
|
||||
bookingValidationSchema,
|
||||
type BookingFormData,
|
||||
} from '../../validators/bookingValidator';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { formatDateLocal } from '../../utils/format';
|
||||
import Recaptcha from '../common/Recaptcha';
|
||||
import { recaptchaService } from '../../services/api/systemSettingsService';
|
||||
import StripePaymentModal from '../payments/StripePaymentModal';
|
||||
import PayPalPaymentModal from '../payments/PayPalPaymentModal';
|
||||
import CashPaymentModal from '../payments/CashPaymentModal';
|
||||
import InvoiceInfoModal from '../booking/InvoiceInfoModal';
|
||||
import { useAntibotForm } from '../../hooks/useAntibotForm';
|
||||
import HoneypotField from '../common/HoneypotField';
|
||||
} from '../../../shared/utils/bookingValidator';
|
||||
import { useFormatCurrency } from '../../payments/hooks/useFormatCurrency';
|
||||
import { formatDateLocal } from '../../../shared/utils/format';
|
||||
import Recaptcha from '../../../shared/components/Recaptcha';
|
||||
import { recaptchaService } from '../../../features/system/services/systemSettingsService';
|
||||
import StripePaymentModal from '../../payments/components/StripePaymentModal';
|
||||
import PayPalPaymentModal from '../../payments/components/PayPalPaymentModal';
|
||||
import CashPaymentModal from '../../payments/components/CashPaymentModal';
|
||||
import InvoiceInfoModal from './InvoiceInfoModal';
|
||||
import { useAntibotForm } from '../../auth/hooks/useAntibotForm';
|
||||
import HoneypotField from '../../../shared/components/HoneypotField';
|
||||
|
||||
interface LuxuryBookingModalProps {
|
||||
roomId: number;
|
||||
@@ -443,7 +444,7 @@ const LuxuryBookingModal: React.FC<LuxuryBookingModalProps> = ({
|
||||
if (createdBookingId) {
|
||||
// Check booking status before showing success toast
|
||||
try {
|
||||
const { getBookingById } = await import('../../services/api/bookingService');
|
||||
const { getBookingById } = await import('../services/bookingService');
|
||||
const bookingResponse = await getBookingById(createdBookingId);
|
||||
if (bookingResponse.success && bookingResponse.data?.booking) {
|
||||
const booking = bookingResponse.data.booking;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface BookingData {
|
||||
room_id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface RoomBlock {
|
||||
room_type_id: number;
|
||||
0
Frontend/src/features/bookings/services/index.ts
Normal file
0
Frontend/src/features/bookings/services/index.ts
Normal file
@@ -10,10 +10,10 @@ import {
|
||||
} from 'lucide-react';
|
||||
import * as LucideIcons from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const AboutPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Accessibility, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const AccessibilityPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useParams, Link, useNavigate } from 'react-router-dom';
|
||||
import { Calendar, User, Tag, ArrowLeft, Eye, Share2, ArrowRight } from 'lucide-react';
|
||||
import { blogService, BlogPost } from '../services/api';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import blogService, { BlogPost } from '../services/blogService';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const BlogDetailPage: React.FC = () => {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Calendar, User, Tag, Search, ArrowRight, Eye, Sparkles, BookOpen } from 'lucide-react';
|
||||
import { blogService, BlogPost } from '../services/api';
|
||||
import Loading from '../components/common/Loading';
|
||||
import Pagination from '../components/common/Pagination';
|
||||
import blogService, { BlogPost } from '../services/blogService';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import Pagination from '../../../shared/components/Pagination';
|
||||
|
||||
const BlogPage: React.FC = () => {
|
||||
const [posts, setPosts] = useState<BlogPost[]>([]);
|
||||
@@ -1,11 +1,10 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { XCircle, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService, { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const CancellationPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,15 +1,15 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Mail, Phone, MapPin, Send, User, MessageSquare } from 'lucide-react';
|
||||
import { submitContactForm } from '../services/api/contactService';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import { submitContactForm } from '../services/contactService';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import Recaptcha from '../components/common/Recaptcha';
|
||||
import { recaptchaService } from '../services/api/systemSettingsService';
|
||||
import ChatWidget from '../components/chat/ChatWidget';
|
||||
import { useAntibotForm } from '../hooks/useAntibotForm';
|
||||
import HoneypotField from '../components/common/HoneypotField';
|
||||
import Recaptcha from '../../../shared/components/Recaptcha';
|
||||
import { recaptchaService } from '../../../features/system/services/systemSettingsService';
|
||||
import ChatWidget from '../../notifications/components/ChatWidget';
|
||||
import { useAntibotForm } from '../../../features/auth/hooks/useAntibotForm';
|
||||
import HoneypotField from '../../../shared/components/HoneypotField';
|
||||
|
||||
const ContactPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { HelpCircle, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const FAQPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -15,15 +15,13 @@ import {
|
||||
RoomCardSkeleton,
|
||||
RoomCarousel,
|
||||
SearchRoomForm,
|
||||
} from '../components/rooms';
|
||||
import {
|
||||
bannerService,
|
||||
roomService,
|
||||
pageContentService
|
||||
} from '../services/api';
|
||||
import type { Banner } from '../services/api/bannerService';
|
||||
import type { Room } from '../services/api/roomService';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
} from '../../rooms/components';
|
||||
import bannerService from '../services/bannerService';
|
||||
import roomService from '../../rooms/services/roomService';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { Banner } from '../services/bannerService';
|
||||
import type { Room } from '../../rooms/services/roomService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
|
||||
const HomePage: React.FC = () => {
|
||||
const [banners, setBanners] = useState<Banner[]>([]);
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Shield, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const PrivacyPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { RefreshCw, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const RefundsPolicyPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Scale, ArrowLeft } from 'lucide-react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pageContentService } from '../services/api';
|
||||
import type { PageContent } from '../services/api/pageContentService';
|
||||
import { useCompanySettings } from '../contexts/CompanySettingsContext';
|
||||
import Loading from '../components/common/Loading';
|
||||
import { createSanitizedHtml } from '../utils/htmlSanitizer';
|
||||
import pageContentService from '../services/pageContentService';
|
||||
import type { PageContent } from '../services/pageContentService';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import { createSanitizedHtml } from '../../../shared/utils/htmlSanitizer';
|
||||
|
||||
const TermsPage: React.FC = () => {
|
||||
const { settings } = useCompanySettings();
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export type CookiePolicySettings = {
|
||||
analytics_enabled: boolean;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Banner {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface BlogSection {
|
||||
type: 'hero' | 'text' | 'image' | 'gallery' | 'quote' | 'features' | 'cta' | 'video';
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface ContactFormData {
|
||||
name: string;
|
||||
0
Frontend/src/features/content/services/index.ts
Normal file
0
Frontend/src/features/content/services/index.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export type PageType = 'home' | 'contact' | 'about' | 'footer' | 'seo' | 'privacy' | 'terms' | 'refunds' | 'cancellation' | 'accessibility' | 'faq';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export type CookieCategoryPreferences = {
|
||||
necessary: boolean;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface GuestPreference {
|
||||
preferred_room_location?: string;
|
||||
@@ -1,10 +1,12 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { X, Search, Calendar, Users, DollarSign, CreditCard, FileText, User } from 'lucide-react';
|
||||
import { bookingService, roomService, userService } from '../../services/api';
|
||||
import type { Room } from '../../services/api/roomService';
|
||||
import type { User as UserType } from '../../services/api/userService';
|
||||
import bookingService from '../../bookings/services/bookingService';
|
||||
import roomService from '../../rooms/services/roomService';
|
||||
import userService from '../../auth/services/userService';
|
||||
import type { Room } from '../../rooms/services/roomService';
|
||||
import type { User as UserType } from '../../auth/services/userService';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../../payments/hooks/useFormatCurrency';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { X, Plus, Trash2, Calendar, DollarSign, FileText, Building2, Loader2 } from 'lucide-react';
|
||||
import { groupBookingService, roomService, Room } from '../../services/api';
|
||||
import groupBookingService from '../../bookings/services/groupBookingService';
|
||||
import roomService, { Room } from '../../rooms/services/roomService';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../../payments/hooks/useFormatCurrency';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
@@ -9,12 +9,12 @@ import {
|
||||
Clock,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import Loading from '../common/Loading';
|
||||
import Pagination from '../common/Pagination';
|
||||
import advancedRoomService, { HousekeepingTask, ChecklistItem } from '../../services/api/advancedRoomService';
|
||||
import { roomService, Room } from '../../services/api';
|
||||
import { userService, User as UserType } from '../../services/api';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import Pagination from '../../../shared/components/Pagination';
|
||||
import advancedRoomService, { HousekeepingTask, ChecklistItem } from '../../rooms/services/advancedRoomService';
|
||||
import roomService, { Room } from '../../rooms/services/roomService';
|
||||
import userService, { User as UserType } from '../../auth/services/userService';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
@@ -11,16 +11,16 @@ import {
|
||||
Star,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import Loading from '../common/Loading';
|
||||
import Pagination from '../common/Pagination';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import Pagination from '../../../shared/components/Pagination';
|
||||
import advancedRoomService, {
|
||||
RoomInspection,
|
||||
InspectionChecklistItem,
|
||||
Issue,
|
||||
} from '../../services/api/advancedRoomService';
|
||||
import { roomService, Room } from '../../services/api';
|
||||
import { userService, User as UserType } from '../../services/api';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
} from '../../rooms/services/advancedRoomService';
|
||||
import roomService, { Room } from '../../rooms/services/roomService';
|
||||
import userService, { User as UserType } from '../../auth/services/userService';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
@@ -8,12 +8,12 @@ import {
|
||||
CheckCircle,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import Loading from '../common/Loading';
|
||||
import Pagination from '../common/Pagination';
|
||||
import advancedRoomService, { MaintenanceRecord } from '../../services/api/advancedRoomService';
|
||||
import { roomService, Room } from '../../services/api';
|
||||
import { userService, User as UserType } from '../../services/api';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import Pagination from '../../../shared/components/Pagination';
|
||||
import advancedRoomService, { MaintenanceRecord } from '../../rooms/services/advancedRoomService';
|
||||
import roomService, { Room } from '../../rooms/services/roomService';
|
||||
import userService, { User as UserType } from '../../auth/services/userService';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import DatePicker from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Service {
|
||||
id: number;
|
||||
0
Frontend/src/features/loyalty/services/index.ts
Normal file
0
Frontend/src/features/loyalty/services/index.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface LoyaltyTier {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export type PackageStatus = 'active' | 'inactive' | 'scheduled' | 'expired';
|
||||
export type PackageItemType = 'room' | 'service' | 'breakfast' | 'activity' | 'amenity' | 'discount';
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Promotion {
|
||||
id: number;
|
||||
@@ -1,10 +1,11 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { MessageCircle, X, Send, Minimize2, Maximize2, Clock } from 'lucide-react';
|
||||
import { chatService, contactService, type Chat, type ChatMessage, type ContactFormData } from '../../services/api';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { useCompanySettings } from '../../contexts/CompanySettingsContext';
|
||||
import chatService, { type Chat, type ChatMessage } from '../services/chatService';
|
||||
import contactService, { type ContactFormData } from '../../content/services/contactService';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { useCompanySettings } from '../../../shared/contexts/CompanySettingsContext';
|
||||
import { toast } from 'react-toastify';
|
||||
import ConfirmationDialog from '../common/ConfirmationDialog';
|
||||
import ConfirmationDialog from '../../../shared/components/ConfirmationDialog';
|
||||
|
||||
interface ChatWidgetProps {
|
||||
onClose?: () => void;
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { Bell } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import notificationService, { Notification } from '../../services/api/notificationService';
|
||||
import { formatDate } from '../../utils/format';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import notificationService, { Notification } from '../services/notificationService';
|
||||
import { formatDate } from '../../../shared/utils/format';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
|
||||
const InAppNotificationBell: React.FC = () => {
|
||||
const { isAuthenticated, token, isLoading } = useAuthStore();
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Bell, Mail, MessageSquare, Smartphone, Save } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { Loading } from '../common';
|
||||
import notificationService, { NotificationPreferences as NotificationPreferencesType } from '../../services/api/notificationService';
|
||||
import Loading from '../../../shared/components/Loading';
|
||||
import notificationService, { NotificationPreferences as NotificationPreferencesType } from '../services/notificationService';
|
||||
|
||||
const NotificationPreferences: React.FC = () => {
|
||||
const [preferences, setPreferences] = useState<NotificationPreferencesType | null>(null);
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { X, Plus } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import notificationService, { NotificationTemplate } from '../../services/api/notificationService';
|
||||
import notificationService, { NotificationTemplate } from '../services/notificationService';
|
||||
|
||||
interface NotificationTemplatesModalProps {
|
||||
onClose: () => void;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import notificationService from '../../services/api/notificationService';
|
||||
import notificationService from '../services/notificationService';
|
||||
|
||||
interface SendNotificationModalProps {
|
||||
onClose: () => void;
|
||||
@@ -2,9 +2,9 @@ import React, { useEffect, useState } from 'react';
|
||||
import { Bell } from 'lucide-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { toast } from 'react-toastify';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import { type Chat } from '../../services/api';
|
||||
import { useChatNotifications } from '../../contexts/ChatNotificationContext';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import { type Chat } from '../services/chatService';
|
||||
import { useChatNotifications } from '../contexts/ChatNotificationContext';
|
||||
|
||||
const StaffChatNotification: React.FC = () => {
|
||||
const [notificationWs, setNotificationWs] = useState<WebSocket | null>(null);
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { createContext, useContext, useState, ReactNode, useEffect } from 'react';
|
||||
import useAuthStore from '../store/useAuthStore';
|
||||
import { chatService } from '../services/api';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
import chatService from '../services/chatService';
|
||||
|
||||
interface ChatNotificationContextType {
|
||||
unreadCount: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Chat {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Campaign {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Notification {
|
||||
id: number;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { createBoricaPayment } from '../../services/api/paymentService';
|
||||
import { createBoricaPayment } from '../services/paymentService';
|
||||
import { X, Loader2, AlertCircle, CreditCard } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../hooks/useFormatCurrency';
|
||||
|
||||
interface BoricaPaymentModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import { X, CheckCircle, CreditCard } from 'lucide-react';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../hooks/useFormatCurrency';
|
||||
import DepositPaymentModal from './DepositPaymentModal';
|
||||
|
||||
interface CashPaymentModalProps {
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
Loader2,
|
||||
} from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { getBookingById, type Booking } from '../../services/api/bookingService';
|
||||
import CancelBookingModal from '../booking/CancelBookingModal';
|
||||
import { getBookingById, type Booking } from '../../bookings/services/bookingService';
|
||||
import CancelBookingModal from '../../bookings/components/CancelBookingModal';
|
||||
import {
|
||||
getPaymentsByBookingId,
|
||||
type Payment,
|
||||
} from '../../services/api/paymentService';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
} from '../services/paymentService';
|
||||
import { useFormatCurrency } from '../hooks/useFormatCurrency';
|
||||
import StripePaymentModal from './StripePaymentModal';
|
||||
import PayPalPaymentModal from './PayPalPaymentModal';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { createPayPalOrder } from '../../services/api/paymentService';
|
||||
import { createPayPalOrder } from '../services/paymentService';
|
||||
import { X, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../hooks/useFormatCurrency';
|
||||
|
||||
interface PayPalPaymentModalProps {
|
||||
isOpen: boolean;
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { createPayPalOrder } from '../../services/api/paymentService';
|
||||
import { createPayPalOrder } from '../services/paymentService';
|
||||
import { Loader2, AlertCircle } from 'lucide-react';
|
||||
import { useFormatCurrency } from '../../hooks/useFormatCurrency';
|
||||
import { useFormatCurrency } from '../hooks/useFormatCurrency';
|
||||
|
||||
interface PayPalPaymentWrapperProps {
|
||||
bookingId: number;
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { loadStripe, StripeElementsOptions } from '@stripe/stripe-js';
|
||||
import { Elements } from '@stripe/react-stripe-js';
|
||||
import StripePaymentForm from './StripePaymentForm';
|
||||
import { createStripePaymentIntent, confirmStripePayment } from '../../services/api/paymentService';
|
||||
import { createStripePaymentIntent, confirmStripePayment } from '../services/paymentService';
|
||||
import { X, Loader2, AlertCircle } from 'lucide-react';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
||||
import { loadStripe, StripeElementsOptions } from '@stripe/stripe-js';
|
||||
import { Elements } from '@stripe/react-stripe-js';
|
||||
import StripePaymentForm from './StripePaymentForm';
|
||||
import { createStripePaymentIntent, confirmStripePayment } from '../../services/api/paymentService';
|
||||
import { createStripePaymentIntent, confirmStripePayment } from '../services/paymentService';
|
||||
import { Loader2, AlertCircle } from 'lucide-react';
|
||||
|
||||
interface StripePaymentWrapperProps {
|
||||
@@ -5,8 +5,8 @@ import React, {
|
||||
useState,
|
||||
ReactNode,
|
||||
} from 'react';
|
||||
import { CURRENCY } from '../utils/constants';
|
||||
import systemSettingsService from '../services/api/systemSettingsService';
|
||||
import { CURRENCY } from '../../../shared/utils/constants';
|
||||
import systemSettingsService from '../../../features/system/services/systemSettingsService';
|
||||
|
||||
type CurrencyContextValue = {
|
||||
currency: string;
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useCurrency } from '../contexts/CurrencyContext';
|
||||
import { formatCurrency as formatCurrencyUtil } from '../utils/format';
|
||||
import { formatCurrency as formatCurrencyUtil } from '../../../shared/utils/format';
|
||||
|
||||
export const useFormatCurrency = () => {
|
||||
const { currency } = useCurrency();
|
||||
0
Frontend/src/features/payments/services/index.ts
Normal file
0
Frontend/src/features/payments/services/index.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface InvoiceItem {
|
||||
id: number;
|
||||
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface PaymentData {
|
||||
booking_id: number;
|
||||
@@ -1,5 +1,5 @@
|
||||
import apiClient from './apiClient';
|
||||
import type { Room } from './roomService';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
import type { Room } from '../../rooms/services/roomService';
|
||||
|
||||
export interface Favorite {
|
||||
id: number;
|
||||
0
Frontend/src/features/reviews/services/index.ts
Normal file
0
Frontend/src/features/reviews/services/index.ts
Normal file
@@ -1,4 +1,4 @@
|
||||
import apiClient from './apiClient';
|
||||
import apiClient from '../../../shared/services/apiClient';
|
||||
|
||||
export interface Review {
|
||||
id: number;
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import type { Banner } from '../../services/api/bannerService';
|
||||
import type { Banner } from '../../content/services/bannerService';
|
||||
|
||||
interface BannerCarouselProps {
|
||||
banners: Banner[];
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Heart } from 'lucide-react';
|
||||
import useFavoritesStore from '../../store/useFavoritesStore';
|
||||
import useAuthStore from '../../store/useAuthStore';
|
||||
import useFavoritesStore from '../../../store/useFavoritesStore';
|
||||
import useAuthStore from '../../../store/useAuthStore';
|
||||
|
||||
interface FavoriteButtonProps {
|
||||
roomId: number;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user