This commit is contained in:
Iliyan Angelov
2025-11-24 03:52:08 +02:00
parent dfcaebaf8c
commit 366f28677a
18241 changed files with 865352 additions and 567 deletions

View File

@@ -0,0 +1,203 @@
"use client";
import { useState, useEffect } from 'react';
import { aboutService, AboutPageData, AboutBanner, AboutService, AboutProcess, AboutJourney } from '../api/aboutService';
interface UseAboutReturn {
data: AboutPageData | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
interface UseAboutBannerReturn {
data: AboutBanner[] | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
interface UseAboutServiceReturn {
data: AboutService[] | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
interface UseAboutProcessReturn {
data: AboutProcess[] | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
interface UseAboutJourneyReturn {
data: AboutJourney[] | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
/**
* Hook to fetch all about page data
*/
export const useAbout = (): UseAboutReturn => {
const [data, setData] = useState<AboutPageData | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await aboutService.getAboutPageData();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch about banners
*/
export const useAboutBanners = (): UseAboutBannerReturn => {
const [data, setData] = useState<AboutBanner[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await aboutService.getBanners();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch about services
*/
export const useAboutServices = (): UseAboutServiceReturn => {
const [data, setData] = useState<AboutService[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await aboutService.getServices();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch about processes
*/
export const useAboutProcesses = (): UseAboutProcessReturn => {
const [data, setData] = useState<AboutProcess[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await aboutService.getProcesses();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch about journeys
*/
export const useAboutJourneys = (): UseAboutJourneyReturn => {
const [data, setData] = useState<AboutJourney[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await aboutService.getJourneys();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};

View File

@@ -0,0 +1,250 @@
import { useState, useEffect } from 'react';
import {
blogService,
BlogPost,
BlogPostListResponse,
BlogCategory,
BlogTag,
BlogAuthor
} from '../api/blogService';
// Hook for fetching all blog posts
export const useBlogPosts = (params?: {
category?: string;
tag?: string;
author?: number;
search?: string;
featured?: boolean;
ordering?: string;
page?: number;
page_size?: number;
}) => {
const [posts, setPosts] = useState<BlogPost[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const [pagination, setPagination] = useState<{
count: number;
next: string | null;
previous: string | null;
}>({ count: 0, next: null, previous: null });
useEffect(() => {
const fetchPosts = async () => {
try {
setLoading(true);
const data = await blogService.getPosts(params);
setPosts(data.results);
setPagination({
count: data.count,
next: data.next,
previous: data.previous
});
setError(null);
} catch (err) {
setError(err as Error);
setPosts([]);
} finally {
setLoading(false);
}
};
fetchPosts();
}, [params?.category, params?.tag, params?.author, params?.search, params?.page, params?.page_size]);
return { posts, loading, error, pagination, totalCount: pagination.count };
};
// Hook for fetching a single blog post by slug
export const useBlogPost = (slug: string | null) => {
const [post, setPost] = useState<BlogPost | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchPost = async () => {
try {
setLoading(true);
const data = await blogService.getPostBySlug(slug);
setPost(data);
setError(null);
} catch (err) {
setError(err as Error);
setPost(null);
} finally {
setLoading(false);
}
};
fetchPost();
}, [slug]);
return { post, loading, error };
};
// Hook for fetching featured blog posts
export const useFeaturedPosts = () => {
const [posts, setPosts] = useState<BlogPost[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchFeaturedPosts = async () => {
try {
setLoading(true);
const data = await blogService.getFeaturedPosts();
setPosts(data);
setError(null);
} catch (err) {
setError(err as Error);
setPosts([]);
} finally {
setLoading(false);
}
};
fetchFeaturedPosts();
}, []);
return { posts, loading, error };
};
// Hook for fetching latest blog posts
export const useLatestPosts = (limit: number = 5) => {
const [posts, setPosts] = useState<BlogPost[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchLatestPosts = async () => {
try {
setLoading(true);
const data = await blogService.getLatestPosts(limit);
setPosts(data);
setError(null);
} catch (err) {
setError(err as Error);
setPosts([]);
} finally {
setLoading(false);
}
};
fetchLatestPosts();
}, [limit]);
return { posts, loading, error };
};
// Hook for fetching popular blog posts
export const usePopularPosts = (limit: number = 5) => {
const [posts, setPosts] = useState<BlogPost[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchPopularPosts = async () => {
try {
setLoading(true);
const data = await blogService.getPopularPosts(limit);
setPosts(data);
setError(null);
} catch (err) {
setError(err as Error);
setPosts([]);
} finally {
setLoading(false);
}
};
fetchPopularPosts();
}, [limit]);
return { posts, loading, error };
};
// Hook for fetching blog categories
export const useBlogCategories = () => {
const [categories, setCategories] = useState<BlogCategory[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchCategories = async () => {
try {
setLoading(true);
const data = await blogService.getCategories();
setCategories(data);
setError(null);
} catch (err) {
setError(err as Error);
setCategories([]);
} finally {
setLoading(false);
}
};
fetchCategories();
}, []);
return { categories, loading, error };
};
// Hook for fetching blog tags
export const useBlogTags = () => {
const [tags, setTags] = useState<BlogTag[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchTags = async () => {
try {
setLoading(true);
const data = await blogService.getTags();
setTags(data);
setError(null);
} catch (err) {
setError(err as Error);
setTags([]);
} finally {
setLoading(false);
}
};
fetchTags();
}, []);
return { tags, loading, error };
};
// Hook for fetching blog authors
export const useBlogAuthors = () => {
const [authors, setAuthors] = useState<BlogAuthor[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchAuthors = async () => {
try {
setLoading(true);
const data = await blogService.getAuthors();
setAuthors(data);
setError(null);
} catch (err) {
setError(err as Error);
setAuthors([]);
} finally {
setLoading(false);
}
};
fetchAuthors();
}, []);
return { authors, loading, error };
};

View File

@@ -0,0 +1,99 @@
import { useState, useEffect } from 'react';
import { careerService, JobPosition } from '../api/careerService';
/**
* Custom hook to fetch all job positions
*/
export const useJobs = () => {
const [jobs, setJobs] = useState<JobPosition[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchJobs = async () => {
try {
setLoading(true);
const data = await careerService.getAllJobs();
setJobs(data);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
fetchJobs();
}, []);
return { jobs, loading, error };
};
/**
* Custom hook to fetch a single job by slug
*/
export const useJob = (slug: string) => {
const [job, setJob] = useState<JobPosition | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
console.log('🔍 useJob hook called with slug:', slug);
if (!slug) {
console.log('❌ No slug provided, setting loading to false');
setLoading(false);
return;
}
const fetchJob = async () => {
try {
console.log('📡 Fetching job data for slug:', slug);
setLoading(true);
const data = await careerService.getJobBySlug(slug);
console.log('✅ Job data fetched successfully:', data);
setJob(data);
setError(null);
} catch (err) {
console.error('❌ Error fetching job data:', err);
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
console.log('🔄 Setting loading to false');
setLoading(false);
}
};
fetchJob();
}, [slug]);
return { job, loading, error };
};
/**
* Custom hook to fetch featured jobs
*/
export const useFeaturedJobs = () => {
const [jobs, setJobs] = useState<JobPosition[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchFeaturedJobs = async () => {
try {
setLoading(true);
const data = await careerService.getFeaturedJobs();
setJobs(data);
setError(null);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
fetchFeaturedJobs();
}, []);
return { jobs, loading, error };
};

View File

@@ -0,0 +1,340 @@
import { useState, useEffect } from 'react';
import caseStudyService, {
CaseStudy,
CaseStudyCategory,
Client,
CaseStudyListResponse,
} from '../api/caseStudyService';
// Hook to fetch all case studies
export const useCaseStudies = (params?: {
category?: string;
client?: string;
search?: string;
featured?: boolean;
ordering?: string;
page?: number;
page_size?: number;
}) => {
const [caseStudies, setCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const [pagination, setPagination] = useState<{
count: number;
next: string | null;
previous: string | null;
} | null>(null);
useEffect(() => {
const fetchCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getCaseStudies(params);
setCaseStudies(data.results);
setPagination({
count: data.count,
next: data.next,
previous: data.previous,
});
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchCaseStudies();
}, [
params?.category,
params?.client,
params?.search,
params?.featured,
params?.ordering,
params?.page,
params?.page_size,
]);
return { caseStudies, loading, error, pagination };
};
// Hook to fetch a single case study by slug
export const useCaseStudy = (slug: string | null) => {
const [caseStudy, setCaseStudy] = useState<CaseStudy | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchCaseStudy = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getCaseStudyBySlug(slug);
setCaseStudy(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchCaseStudy();
}, [slug]);
return { caseStudy, loading, error };
};
// Hook to fetch featured case studies
export const useFeaturedCaseStudies = () => {
const [featuredCaseStudies, setFeaturedCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchFeaturedCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getFeaturedCaseStudies();
setFeaturedCaseStudies(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchFeaturedCaseStudies();
}, []);
return { featuredCaseStudies, loading, error };
};
// Hook to fetch latest case studies
export const useLatestCaseStudies = (limit: number = 6) => {
const [latestCaseStudies, setLatestCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchLatestCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getLatestCaseStudies(limit);
setLatestCaseStudies(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchLatestCaseStudies();
}, [limit]);
return { latestCaseStudies, loading, error };
};
// Hook to fetch popular case studies
export const usePopularCaseStudies = (limit: number = 6) => {
const [popularCaseStudies, setPopularCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchPopularCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getPopularCaseStudies(limit);
setPopularCaseStudies(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchPopularCaseStudies();
}, [limit]);
return { popularCaseStudies, loading, error };
};
// Hook to fetch related case studies
export const useRelatedCaseStudies = (slug: string | null) => {
const [relatedCaseStudies, setRelatedCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchRelatedCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getRelatedCaseStudies(slug);
setRelatedCaseStudies(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchRelatedCaseStudies();
}, [slug]);
return { relatedCaseStudies, loading, error };
};
// Hook to fetch case study categories
export const useCaseStudyCategories = () => {
const [categories, setCategories] = useState<CaseStudyCategory[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchCategories = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getCategories();
setCategories(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchCategories();
}, []);
return { categories, loading, error };
};
// Hook to fetch categories with case studies
export const useCategoriesWithCaseStudies = () => {
const [categories, setCategories] = useState<CaseStudyCategory[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchCategories = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getCategoriesWithCaseStudies();
setCategories(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchCategories();
}, []);
return { categories, loading, error };
};
// Hook to fetch all clients
export const useClients = () => {
const [clients, setClients] = useState<Client[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const fetchClients = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getClients();
setClients(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchClients();
}, []);
return { clients, loading, error };
};
// Hook to fetch a single client by slug
export const useClient = (slug: string | null) => {
const [client, setClient] = useState<Client | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchClient = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getClientBySlug(slug);
setClient(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchClient();
}, [slug]);
return { client, loading, error };
};
// Hook to fetch case studies for a specific client
export const useClientCaseStudies = (slug: string | null) => {
const [caseStudies, setCaseStudies] = useState<CaseStudy[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchClientCaseStudies = async () => {
try {
setLoading(true);
setError(null);
const data = await caseStudyService.getClientCaseStudies(slug);
setCaseStudies(data.results);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
};
fetchClientCaseStudies();
}, [slug]);
return { caseStudies, loading, error };
};

View File

@@ -0,0 +1,128 @@
"use client";
import { useState, useEffect } from 'react';
import { Policy, PolicyListItem, getPolicies, getPolicyByType, getPolicyById } from '../api/policyService';
interface UsePoliciesReturn {
data: PolicyListItem[] | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
interface UsePolicyReturn {
data: Policy | null;
isLoading: boolean;
error: Error | null;
refetch: () => Promise<void>;
}
/**
* Hook to fetch all policies
*/
export const usePolicies = (): UsePoliciesReturn => {
const [data, setData] = useState<PolicyListItem[] | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const result = await getPolicies();
setData(result);
} catch (err) {
setError(err instanceof Error ? err.message : 'An error occurred');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return {
data,
loading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch a policy by type
*/
export const usePolicy = (type: 'privacy' | 'terms' | 'support' | null): UsePolicyReturn => {
const [data, setData] = useState<Policy | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
if (!type) {
setIsLoading(false);
return;
}
try {
setIsLoading(true);
setError(null);
const result = await getPolicyByType(type);
setData(result);
} catch (err) {
setError(err instanceof Error ? err : new Error('An error occurred'));
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchData();
}, [type]);
return {
data,
isLoading,
error,
refetch: fetchData,
};
};
/**
* Hook to fetch a policy by ID
*/
export const usePolicyById = (id: number | null): UsePolicyReturn => {
const [data, setData] = useState<Policy | null>(null);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = async () => {
if (!id) {
setIsLoading(false);
return;
}
try {
setIsLoading(true);
setError(null);
const result = await getPolicyById(id);
setData(result);
} catch (err) {
setError(err instanceof Error ? err : new Error('An error occurred'));
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchData();
}, [id]);
return {
data,
isLoading,
error,
refetch: fetchData,
};
};

View 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,
};
};

View File

@@ -0,0 +1,251 @@
import { useState, useEffect } from 'react';
import {
getTicketCategories,
getTicketStatuses,
getTicketPriorities,
getKnowledgeBaseCategories,
getKnowledgeBaseArticles,
getFeaturedArticles,
getKnowledgeBaseArticle,
getArticlesByCategory,
TicketCategory,
TicketStatus,
TicketPriority,
KnowledgeBaseCategory,
KnowledgeBaseArticle
} from '../api/supportService';
/**
* Hook to fetch ticket categories
*/
export const useTicketCategories = () => {
const [categories, setCategories] = useState<TicketCategory[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchCategories = async () => {
try {
setLoading(true);
const data = await getTicketCategories();
setCategories(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch ticket categories');
} finally {
setLoading(false);
}
};
fetchCategories();
}, []);
return { categories, loading, error };
};
/**
* Hook to fetch ticket statuses
*/
export const useTicketStatuses = () => {
const [statuses, setStatuses] = useState<TicketStatus[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchStatuses = async () => {
try {
setLoading(true);
const data = await getTicketStatuses();
setStatuses(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch ticket statuses');
} finally {
setLoading(false);
}
};
fetchStatuses();
}, []);
return { statuses, loading, error };
};
/**
* Hook to fetch ticket priorities
*/
export const useTicketPriorities = () => {
const [priorities, setPriorities] = useState<TicketPriority[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchPriorities = async () => {
try {
setLoading(true);
const data = await getTicketPriorities();
setPriorities(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch ticket priorities');
} finally {
setLoading(false);
}
};
fetchPriorities();
}, []);
return { priorities, loading, error };
};
/**
* Hook to fetch knowledge base categories
*/
export const useKnowledgeBaseCategories = () => {
const [categories, setCategories] = useState<KnowledgeBaseCategory[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchCategories = async () => {
try {
setLoading(true);
const data = await getKnowledgeBaseCategories();
setCategories(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch knowledge base categories');
} finally {
setLoading(false);
}
};
fetchCategories();
}, []);
return { categories, loading, error };
};
/**
* Hook to fetch knowledge base articles with optional search
*/
export const useKnowledgeBaseArticles = (search?: string) => {
const [articles, setArticles] = useState<KnowledgeBaseArticle[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchArticles = async () => {
try {
setLoading(true);
const data = await getKnowledgeBaseArticles(search);
setArticles(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch knowledge base articles');
} finally {
setLoading(false);
}
};
fetchArticles();
}, [search]);
return { articles, loading, error };
};
/**
* Hook to fetch featured knowledge base articles
*/
export const useFeaturedArticles = () => {
const [articles, setArticles] = useState<KnowledgeBaseArticle[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchArticles = async () => {
try {
setLoading(true);
const data = await getFeaturedArticles();
setArticles(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch featured articles');
} finally {
setLoading(false);
}
};
fetchArticles();
}, []);
return { articles, loading, error };
};
/**
* Hook to fetch a single knowledge base article
*/
export const useKnowledgeBaseArticle = (slug: string | null) => {
const [article, setArticle] = useState<KnowledgeBaseArticle | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!slug) {
setLoading(false);
return;
}
const fetchArticle = async () => {
try {
setLoading(true);
const data = await getKnowledgeBaseArticle(slug);
setArticle(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch knowledge base article');
} finally {
setLoading(false);
}
};
fetchArticle();
}, [slug]);
return { article, loading, error };
};
/**
* Hook to fetch articles by category
*/
export const useArticlesByCategory = (categorySlug: string | null) => {
const [articles, setArticles] = useState<KnowledgeBaseArticle[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (!categorySlug) {
setLoading(false);
return;
}
const fetchArticles = async () => {
try {
setLoading(true);
const data = await getArticlesByCategory(categorySlug);
setArticles(data);
setError(null);
} catch (err: any) {
setError(err.message || 'Failed to fetch articles by category');
} finally {
setLoading(false);
}
};
fetchArticles();
}, [categorySlug]);
return { articles, loading, error };
};