GNXSOFT.COM
This commit is contained in:
302
gnx-react/lib/hooks/useServices.ts
Normal file
302
gnx-react/lib/hooks/useServices.ts
Normal file
@@ -0,0 +1,302 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { serviceService, Service, ServiceListResponse, ServiceStats, ServiceSearchResponse } from '../api/serviceService';
|
||||
|
||||
// Hook for fetching all services
|
||||
export const useServices = (params?: {
|
||||
featured?: boolean;
|
||||
category?: string;
|
||||
min_price?: number;
|
||||
max_price?: number;
|
||||
search?: string;
|
||||
ordering?: string;
|
||||
page?: number;
|
||||
}) => {
|
||||
const [services, setServices] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [pagination, setPagination] = useState<{
|
||||
count: number;
|
||||
next: string | null;
|
||||
previous: string | null;
|
||||
} | null>(null);
|
||||
|
||||
const fetchServices = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.getServices(params);
|
||||
setServices(response.results);
|
||||
setPagination({
|
||||
count: response.count,
|
||||
next: response.next,
|
||||
previous: response.previous,
|
||||
});
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch services');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [params]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchServices();
|
||||
}, [fetchServices]);
|
||||
|
||||
return {
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
pagination,
|
||||
refetch: fetchServices,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for fetching a single service by slug
|
||||
export const useService = (slug: string) => {
|
||||
const [service, setService] = useState<Service | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchService = useCallback(async () => {
|
||||
if (!slug) return;
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.getServiceBySlug(slug);
|
||||
setService(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch service');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [slug]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchService();
|
||||
}, [fetchService]);
|
||||
|
||||
return {
|
||||
service,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchService,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for fetching featured services
|
||||
export const useFeaturedServices = () => {
|
||||
const [services, setServices] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchFeaturedServices = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.getFeaturedServices();
|
||||
setServices(response.results);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch featured services');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchFeaturedServices();
|
||||
}, [fetchFeaturedServices]);
|
||||
|
||||
return {
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchFeaturedServices,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for searching services
|
||||
export const useServiceSearch = (query: string) => {
|
||||
const [results, setResults] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
const searchServices = useCallback(async (searchQuery: string) => {
|
||||
if (!searchQuery.trim()) {
|
||||
setResults([]);
|
||||
setCount(0);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.searchServices(searchQuery);
|
||||
setResults(response.results);
|
||||
setCount(response.count);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to search services');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
searchServices(query);
|
||||
}, 300); // Debounce search
|
||||
|
||||
return () => clearTimeout(timeoutId);
|
||||
}, [query, searchServices]);
|
||||
|
||||
return {
|
||||
results,
|
||||
loading,
|
||||
error,
|
||||
count,
|
||||
search: searchServices,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for fetching service statistics
|
||||
export const useServiceStats = () => {
|
||||
const [stats, setStats] = useState<ServiceStats | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchStats = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.getServiceStats();
|
||||
setStats(response);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch service stats');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchStats();
|
||||
}, [fetchStats]);
|
||||
|
||||
return {
|
||||
stats,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchStats,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for managing service state (for admin operations)
|
||||
export const useServiceManagement = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const createService = useCallback(async (serviceData: Partial<Service>) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.createService(serviceData);
|
||||
return response;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to create service');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateService = useCallback(async (slug: string, serviceData: Partial<Service>) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.updateService(slug, serviceData);
|
||||
return response;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to update service');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const deleteService = useCallback(async (slug: string) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
await serviceService.deleteService(slug);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to delete service');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const uploadServiceImage = useCallback(async (slug: string, imageFile: File) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
const response = await serviceService.uploadServiceImage(slug, imageFile);
|
||||
return response;
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to upload service image');
|
||||
throw err;
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
loading,
|
||||
error,
|
||||
createService,
|
||||
updateService,
|
||||
deleteService,
|
||||
uploadServiceImage,
|
||||
};
|
||||
};
|
||||
|
||||
// Hook for navigation services (for header menu)
|
||||
export const useNavigationServices = () => {
|
||||
const [services, setServices] = useState<Service[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const fetchNavigationServices = useCallback(async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
// Fetch only active services, ordered by display_order
|
||||
const response = await serviceService.getServices({
|
||||
ordering: 'display_order',
|
||||
page: 1
|
||||
});
|
||||
// Filter only active services (handle null values as active)
|
||||
const activeServices = response.results.filter(service => service.is_active !== false);
|
||||
setServices(activeServices);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'Failed to fetch navigation services');
|
||||
// Set empty array on error to prevent navigation issues
|
||||
setServices([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchNavigationServices();
|
||||
}, [fetchNavigationServices]);
|
||||
|
||||
return {
|
||||
services,
|
||||
loading,
|
||||
error,
|
||||
refetch: fetchNavigationServices,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user