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,325 @@
import { API_BASE_URL } from '../config/api';
// Types for About Us data
export interface AboutStat {
number: string;
label: string;
order: number;
}
export interface AboutSocialLink {
platform: string;
url: string;
icon: string;
aria_label: string;
order: number;
}
export interface AboutBanner {
id: number;
title: string;
subtitle: string;
description: string;
badge_text: string;
badge_icon: string;
cta_text: string;
cta_link: string;
cta_icon: string;
image_url: string | null;
is_active: boolean;
stats: AboutStat[];
social_links: AboutSocialLink[];
created_at: string;
updated_at: string;
}
export interface AboutFeature {
title: string;
description: string;
icon: string;
order: number;
}
export interface AboutService {
id: number;
title: string;
subtitle: string;
description: string;
badge_text: string;
badge_icon: string;
image_url: string | null;
cta_text: string;
cta_link: string;
is_active: boolean;
features: AboutFeature[];
created_at: string;
updated_at: string;
}
export interface AboutProcessStep {
step_number: string;
title: string;
description: string;
order: number;
}
export interface AboutProcess {
id: number;
title: string;
subtitle: string;
description: string;
badge_text: string;
badge_icon: string;
image_url: string | null;
cta_text: string;
cta_link: string;
is_active: boolean;
steps: AboutProcessStep[];
created_at: string;
updated_at: string;
}
export interface AboutMilestone {
year: string;
title: string;
description: string;
order: number;
}
export interface AboutJourney {
id: number;
title: string;
subtitle: string;
description: string;
badge_text: string;
badge_icon: string;
image_url: string | null;
cta_text: string;
cta_link: string;
is_active: boolean;
milestones: AboutMilestone[];
created_at: string;
updated_at: string;
}
export interface AboutPageData {
banner: AboutBanner;
service: AboutService;
process: AboutProcess;
journey: AboutJourney;
}
class AboutServiceAPI {
private baseUrl = `${API_BASE_URL}/api/about`;
/**
* Get all about page data in one request
*/
async getAboutPageData(): Promise<AboutPageData> {
try {
const response = await fetch(`${this.baseUrl}/page/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get all about banners
*/
async getBanners(): Promise<AboutBanner[]> {
try {
const response = await fetch(`${this.baseUrl}/banner/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a specific about banner by ID
*/
async getBanner(id: number): Promise<AboutBanner> {
try {
const response = await fetch(`${this.baseUrl}/banner/${id}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get all about services
*/
async getServices(): Promise<AboutService[]> {
try {
const response = await fetch(`${this.baseUrl}/service/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a specific about service by ID
*/
async getService(id: number): Promise<AboutService> {
try {
const response = await fetch(`${this.baseUrl}/service/${id}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get all about processes
*/
async getProcesses(): Promise<AboutProcess[]> {
try {
const response = await fetch(`${this.baseUrl}/process/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a specific about process by ID
*/
async getProcess(id: number): Promise<AboutProcess> {
try {
const response = await fetch(`${this.baseUrl}/process/${id}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get all about journeys
*/
async getJourneys(): Promise<AboutJourney[]> {
try {
const response = await fetch(`${this.baseUrl}/journey/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a specific about journey by ID
*/
async getJourney(id: number): Promise<AboutJourney> {
try {
const response = await fetch(`${this.baseUrl}/journey/${id}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
}
// Export a singleton instance
export const aboutService = new AboutServiceAPI();
export default aboutService;

View File

@@ -0,0 +1,422 @@
import { API_CONFIG } from '../config/api';
// Types for Blog API
export interface BlogAuthor {
id: number;
name: string;
email?: string;
bio?: string;
avatar?: string;
}
export interface BlogCategory {
id: number;
title: string;
slug: string;
description?: string;
display_order: number;
posts_count?: number;
}
export interface BlogTag {
id: number;
name: string;
slug: string;
}
export interface BlogPost {
id: number;
title: string;
slug: string;
content?: string;
excerpt: string;
thumbnail?: string;
featured_image?: string;
author?: BlogAuthor;
author_name?: string;
category?: BlogCategory;
category_title?: string;
category_slug?: string;
tags?: BlogTag[];
meta_description?: string;
meta_keywords?: string;
published: boolean;
featured: boolean;
views_count: number;
reading_time: number;
published_at: string;
created_at: string;
updated_at: string;
related_posts?: BlogPost[];
}
export interface BlogPostListResponse {
count: number;
next: string | null;
previous: string | null;
results: BlogPost[];
}
export interface BlogComment {
id: number;
post: number;
name: string;
email: string;
content: string;
parent?: number;
is_approved: boolean;
created_at: string;
updated_at: string;
replies?: BlogComment[];
}
export interface BlogCommentCreateData {
post: number;
name: string;
email: string;
content: string;
parent?: number;
}
// Helper function to build query string
const buildQueryString = (params: Record<string, any>): string => {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
searchParams.append(key, value.toString());
}
});
return searchParams.toString();
};
// Blog API functions
export const blogService = {
// Get all blog posts with optional filtering
getPosts: async (params?: {
category?: string;
tag?: string;
author?: number;
search?: string;
featured?: boolean;
ordering?: string;
page?: number;
page_size?: number;
}): Promise<BlogPostListResponse> => {
try {
const queryString = params ? buildQueryString(params) : '';
const url = `${API_CONFIG.BASE_URL}/api/blog/posts/${queryString ? `?${queryString}` : ''}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get a single blog post by slug
getPostBySlug: async (slug: string): Promise<BlogPost> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/${slug}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get featured blog posts
getFeaturedPosts: async (): Promise<BlogPost[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/featured/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get latest blog posts
getLatestPosts: async (limit: number = 5): Promise<BlogPost[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/latest/?limit=${limit}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get popular blog posts
getPopularPosts: async (limit: number = 5): Promise<BlogPost[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/popular/?limit=${limit}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get related posts for a specific post
getRelatedPosts: async (postSlug: string): Promise<BlogPost[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/posts/${postSlug}/related/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get all blog categories
getCategories: async (): Promise<BlogCategory[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return Array.isArray(data) ? data : data.results || [];
} catch (error) {
throw error;
}
},
// Get categories with posts
getCategoriesWithPosts: async (): Promise<BlogCategory[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/with_posts/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get a single category by slug
getCategoryBySlug: async (slug: string): Promise<BlogCategory> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/categories/${slug}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get all blog tags
getTags: async (): Promise<BlogTag[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/tags/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return Array.isArray(data) ? data : data.results || [];
} catch (error) {
throw error;
}
},
// Get posts by tag
getPostsByTag: async (tagSlug: string): Promise<BlogPostListResponse> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/tags/${tagSlug}/posts/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get all blog authors
getAuthors: async (): Promise<BlogAuthor[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/authors/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return Array.isArray(data) ? data : data.results || [];
} catch (error) {
throw error;
}
},
// Get posts by author
getPostsByAuthor: async (authorId: number): Promise<BlogPostListResponse> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/authors/${authorId}/posts/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get comments for a post
getComments: async (postId: number): Promise<BlogComment[]> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/comments/?post=${postId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return Array.isArray(data) ? data : data.results || [];
} catch (error) {
throw error;
}
},
// Create a new comment
createComment: async (commentData: BlogCommentCreateData): Promise<{ message: string; data: BlogComment }> => {
try {
const response = await fetch(`${API_CONFIG.BASE_URL}/api/blog/comments/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(commentData),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
};

View File

@@ -0,0 +1,229 @@
import { API_BASE_URL } from '../config/api';
export interface JobPosition {
id: number;
title: string;
slug: string;
department: string;
employment_type: string;
location_type: string;
location: string;
open_positions: number;
experience_required?: string;
salary_min?: number;
salary_max?: number;
salary_currency: string;
salary_period: string;
salary_additional?: string;
short_description?: string;
about_role?: string;
requirements?: string[];
responsibilities?: string[];
qualifications?: string[];
bonus_points?: string[];
benefits?: string[];
start_date: string;
posted_date: string;
updated_date: string;
deadline?: string;
status: string;
featured?: boolean;
}
export interface JobApplication {
job: number;
first_name: string;
last_name: string;
email: string;
phone?: string;
current_position?: string;
current_company?: string;
years_of_experience?: string;
cover_letter?: string;
resume: File;
portfolio_url?: string;
linkedin_url?: string;
github_url?: string;
website_url?: string;
available_from?: string;
notice_period?: string;
expected_salary?: number;
salary_currency?: string;
consent: boolean;
}
class CareerService {
private baseUrl = `${API_BASE_URL}/api/career`;
/**
* Get all active job positions
*/
async getAllJobs(): Promise<JobPosition[]> {
try {
const response = await fetch(`${this.baseUrl}/jobs`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch jobs: ${response.statusText}`);
}
const data = await response.json();
// Handle paginated response - extract results array
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a single job position by slug
*/
async getJobBySlug(slug: string): Promise<JobPosition> {
try {
const response = await fetch(`${this.baseUrl}/jobs/${slug}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch job: ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get featured job positions
*/
async getFeaturedJobs(): Promise<JobPosition[]> {
try {
const response = await fetch(`${this.baseUrl}/jobs/featured`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch featured jobs: ${response.statusText}`);
}
const data = await response.json();
// Handle paginated response - extract results array
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Submit a job application
*/
async submitApplication(applicationData: JobApplication): Promise<any> {
try {
const formData = new FormData();
// Required fields
formData.append('job', applicationData.job.toString());
formData.append('first_name', applicationData.first_name);
formData.append('last_name', applicationData.last_name);
formData.append('email', applicationData.email);
formData.append('consent', applicationData.consent.toString());
formData.append('resume', applicationData.resume);
// Optional fields (only append if they exist)
if (applicationData.phone) formData.append('phone', applicationData.phone);
if (applicationData.current_position) formData.append('current_position', applicationData.current_position);
if (applicationData.current_company) formData.append('current_company', applicationData.current_company);
if (applicationData.years_of_experience) formData.append('years_of_experience', applicationData.years_of_experience);
if (applicationData.cover_letter) formData.append('cover_letter', applicationData.cover_letter);
if (applicationData.portfolio_url) formData.append('portfolio_url', applicationData.portfolio_url);
if (applicationData.linkedin_url) formData.append('linkedin_url', applicationData.linkedin_url);
if (applicationData.github_url) formData.append('github_url', applicationData.github_url);
if (applicationData.website_url) formData.append('website_url', applicationData.website_url);
if (applicationData.available_from) formData.append('available_from', applicationData.available_from);
if (applicationData.notice_period) formData.append('notice_period', applicationData.notice_period);
if (applicationData.expected_salary !== undefined) formData.append('expected_salary', applicationData.expected_salary.toString());
if (applicationData.salary_currency) formData.append('salary_currency', applicationData.salary_currency);
const response = await fetch(`${this.baseUrl}/applications`, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
const errorMessage = errorData.error || errorData.message || errorData.detail || `HTTP ${response.status}: ${response.statusText}`;
throw new Error(errorMessage);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Filter jobs by department
*/
async getJobsByDepartment(department: string): Promise<JobPosition[]> {
try {
const response = await fetch(`${this.baseUrl}/jobs?department=${department}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch jobs: ${response.statusText}`);
}
const data = await response.json();
// Handle paginated response - extract results array
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Filter jobs by employment type
*/
async getJobsByEmploymentType(employmentType: string): Promise<JobPosition[]> {
try {
const response = await fetch(`${this.baseUrl}/jobs?employment_type=${employmentType}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`Failed to fetch jobs: ${response.statusText}`);
}
const data = await response.json();
// Handle paginated response - extract results array
return data.results || data;
} catch (error) {
throw error;
}
}
}
export const careerService = new CareerService();

View File

@@ -0,0 +1,364 @@
import { API_CONFIG } from '../config/api';
// Types for Case Study API
export interface CaseStudyCategory {
id: number;
name: string;
slug: string;
description?: string;
display_order: number;
case_studies_count?: number;
}
export interface Client {
id: number;
name: string;
slug: string;
logo?: string;
description?: string;
website?: string;
}
export interface CaseStudyImage {
id: number;
image: string;
caption?: string;
display_order: number;
}
export interface CaseStudyProcess {
id: number;
title: string;
description: string;
step_number: number;
}
export interface CaseStudy {
id: number;
title: string;
slug: string;
subtitle?: string;
description?: string;
excerpt: string;
thumbnail?: string;
featured_image?: string;
poster_image?: string;
project_image?: string;
project_overview?: string;
site_map_content?: string;
category?: CaseStudyCategory;
category_name?: string;
category_slug?: string;
client?: Client;
client_name?: string;
gallery_images?: CaseStudyImage[];
process_steps?: CaseStudyProcess[];
meta_description?: string;
meta_keywords?: string;
published: boolean;
featured: boolean;
views_count: number;
display_order: number;
published_at: string;
created_at: string;
updated_at: string;
related_case_studies?: CaseStudy[];
}
export interface CaseStudyListResponse {
count: number;
next: string | null;
previous: string | null;
results: CaseStudy[];
}
// Helper function to build query string
const buildQueryString = (params: Record<string, any>): string => {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null && value !== '') {
searchParams.append(key, value.toString());
}
});
return searchParams.toString();
};
// Case Study API functions
export const caseStudyService = {
// Get all case studies with optional filtering
getCaseStudies: async (params?: {
category?: string;
client?: string;
search?: string;
featured?: boolean;
ordering?: string;
page?: number;
page_size?: number;
}): Promise<CaseStudyListResponse> => {
try {
const queryString = params ? buildQueryString(params) : '';
const url = queryString
? `${API_CONFIG.BASE_URL}/api/case-studies/case-studies/?${queryString}`
: `${API_CONFIG.BASE_URL}/api/case-studies/case-studies/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get a single case study by slug
getCaseStudyBySlug: async (slug: string): Promise<CaseStudy> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/${slug}/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get featured case studies
getFeaturedCaseStudies: async (): Promise<CaseStudy[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/featured/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get latest case studies
getLatestCaseStudies: async (limit: number = 6): Promise<CaseStudy[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/latest/?limit=${limit}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get popular case studies
getPopularCaseStudies: async (limit: number = 6): Promise<CaseStudy[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/popular/?limit=${limit}`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get related case studies for a specific case study
getRelatedCaseStudies: async (slug: string): Promise<CaseStudy[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/case-studies/${slug}/related/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get all categories
getCategories: async (): Promise<CaseStudyCategory[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/categories/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get categories with case studies
getCategoriesWithCaseStudies: async (): Promise<CaseStudyCategory[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/categories/with_case_studies/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get all clients
getClients: async (): Promise<Client[]> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/clients/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get a client by slug
getClientBySlug: async (slug: string): Promise<Client> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/clients/${slug}/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
// Get case studies for a specific client
getClientCaseStudies: async (slug: string): Promise<CaseStudyListResponse> => {
try {
const response = await fetch(
`${API_CONFIG.BASE_URL}/api/case-studies/clients/${slug}/case_studies/`,
{
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
}
);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
},
};
export default caseStudyService;

View File

@@ -0,0 +1,131 @@
/**
* Contact API Service
* Handles communication with the Django REST API for contact form submissions
*/
import { API_CONFIG } from '@/lib/config/api';
export interface ContactFormData {
first_name: string;
last_name: string;
email: string;
phone?: string;
company: string;
job_title: string;
industry?: string;
company_size?: string;
project_type?: string;
timeline?: string;
budget?: string;
message: string;
newsletter_subscription: boolean;
privacy_consent: boolean;
}
export interface ContactSubmissionResponse {
message: string;
submission_id: number;
status: string;
}
export interface ApiError {
message: string;
errors?: Record<string, string[]>;
status: number;
}
class ContactApiService {
private baseUrl: string;
constructor() {
this.baseUrl = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.CONTACT}`;
}
/**
* Submit a contact form to the Django API
*/
async submitContactForm(data: ContactFormData): Promise<ContactSubmissionResponse> {
try {
const response = await fetch(`${this.baseUrl}/submissions/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
const result = await response.json();
return result;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to submit contact form: ${error.message}`);
}
throw new Error('Failed to submit contact form: Unknown error');
}
}
/**
* Get contact submission statistics (admin only)
*/
async getContactStats(): Promise<any> {
try {
const response = await fetch(`${this.baseUrl}/submissions/stats/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch contact stats: ${error.message}`);
}
throw new Error('Failed to fetch contact stats: Unknown error');
}
}
/**
* Get recent contact submissions (admin only)
*/
async getRecentSubmissions(): Promise<any[]> {
try {
const response = await fetch(`${this.baseUrl}/submissions/recent/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch recent submissions: ${error.message}`);
}
throw new Error('Failed to fetch recent submissions: Unknown error');
}
}
}
// Create and export a singleton instance
export const contactApiService = new ContactApiService();
// Export the class for testing purposes
export default ContactApiService;

View File

@@ -0,0 +1,115 @@
import { API_BASE_URL } from '../config/api';
export interface PolicySection {
id: number;
heading: string;
content: string;
order: number;
}
export interface Policy {
id: number;
type: 'privacy' | 'terms' | 'support';
title: string;
slug: string;
description: string;
last_updated: string;
version: string;
effective_date: string;
sections: PolicySection[];
}
export interface PolicyListItem {
id: number;
type: 'privacy' | 'terms' | 'support';
title: string;
slug: string;
description: string;
last_updated: string;
version: string;
}
class PolicyServiceAPI {
private baseUrl = `${API_BASE_URL}/api/policies`;
/**
* Get all policies
*/
async getPolicies(): Promise<PolicyListItem[]> {
try {
const response = await fetch(`${this.baseUrl}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data.results || data;
} catch (error) {
throw error;
}
}
/**
* Get a specific policy by type
*/
async getPolicyByType(type: 'privacy' | 'terms' | 'support'): Promise<Policy> {
try {
const response = await fetch(`${this.baseUrl}/${type}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
/**
* Get a specific policy by ID
*/
async getPolicyById(id: number): Promise<Policy> {
try {
const response = await fetch(`${this.baseUrl}/${id}/`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
throw error;
}
}
}
// Export a singleton instance
export const policyService = new PolicyServiceAPI();
// Export individual functions for convenience
export const getPolicies = () => policyService.getPolicies();
export const getPolicyByType = (type: 'privacy' | 'terms' | 'support') => policyService.getPolicyByType(type);
export const getPolicyById = (id: number) => policyService.getPolicyById(id);
export default policyService;

View File

@@ -0,0 +1,495 @@
import { API_CONFIG } from '../config/api';
// Types for Service API
export interface ServiceFeature {
id: number;
title: string;
description: string;
icon: string;
display_order: number;
}
export interface ServiceExpertise {
id: number;
title: string;
description: string;
icon: string;
display_order: number;
}
export interface ServiceCategory {
id: number;
name: string;
slug: string;
description: string;
display_order: number;
}
export interface Service {
id: number;
title: string;
description: string;
short_description?: string;
slug: string;
icon: string;
image?: File | string;
image_url?: string;
price: string;
formatted_price: string;
category?: ServiceCategory;
duration?: string;
deliverables?: string;
technologies?: string;
process_steps?: string;
features_description?: string;
deliverables_description?: string;
process_description?: string;
why_choose_description?: string;
expertise_description?: string;
featured: boolean;
display_order: number;
is_active: boolean;
created_at: string;
updated_at: string;
features?: ServiceFeature[];
expertise_items?: ServiceExpertise[];
}
export interface ServiceListResponse {
count: number;
next: string | null;
previous: string | null;
results: Service[];
}
export interface ServiceStats {
total_services: number;
featured_services: number;
categories: number;
average_price: number;
}
export interface ServiceSearchResponse {
query: string;
count: number;
results: Service[];
}
// Helper function to build query string
const buildQueryString = (params: Record<string, any>): string => {
const searchParams = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== null) {
searchParams.append(key, value.toString());
}
});
return searchParams.toString();
};
// Service API functions
export const serviceService = {
// Get all services with optional filtering
getServices: async (params?: {
featured?: boolean;
category?: string;
min_price?: number;
max_price?: number;
search?: string;
ordering?: string;
page?: number;
}): Promise<ServiceListResponse> => {
try {
const queryString = params ? buildQueryString(params) : '';
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/${queryString ? `?${queryString}` : ''}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch services: ${error.message}`);
}
throw new Error('Failed to fetch services: Unknown error');
}
},
// Get a single service by slug
getServiceBySlug: async (slug: string): Promise<Service> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/${slug}/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch service: ${error.message}`);
}
throw new Error('Failed to fetch service: Unknown error');
}
},
// Get featured services
getFeaturedServices: async (): Promise<ServiceListResponse> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES_FEATURED}/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch featured services: ${error.message}`);
}
throw new Error('Failed to fetch featured services: Unknown error');
}
},
// Search services
searchServices: async (query: string): Promise<ServiceSearchResponse> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES_SEARCH}/?q=${encodeURIComponent(query)}`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to search services: ${error.message}`);
}
throw new Error('Failed to search services: Unknown error');
}
},
// Get service statistics
getServiceStats: async (): Promise<ServiceStats> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES_STATS}/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch service stats: ${error.message}`);
}
throw new Error('Failed to fetch service stats: Unknown error');
}
},
// Get all service categories
getCategories: async (): Promise<ServiceCategory[]> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES_CATEGORIES}/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch categories: ${error.message}`);
}
throw new Error('Failed to fetch categories: Unknown error');
}
},
// Get a single category by slug
getCategoryBySlug: async (slug: string): Promise<ServiceCategory> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES_CATEGORIES}/${slug}/`;
const response = await fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to fetch category: ${error.message}`);
}
throw new Error('Failed to fetch category: Unknown error');
}
},
// Admin functions (require authentication)
createService: async (serviceData: Partial<Service>): Promise<Service> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/admin/create/`;
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(serviceData),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to create service: ${error.message}`);
}
throw new Error('Failed to create service: Unknown error');
}
},
updateService: async (slug: string, serviceData: Partial<Service>): Promise<Service> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/admin/${slug}/update/`;
const response = await fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(serviceData),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
return await response.json();
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to update service: ${error.message}`);
}
throw new Error('Failed to update service: Unknown error');
}
},
deleteService: async (slug: string): Promise<void> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/admin/${slug}/delete/`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to delete service: ${error.message}`);
}
throw new Error('Failed to delete service: Unknown error');
}
},
// Upload image for a service
uploadServiceImage: async (slug: string, imageFile: File): Promise<Service> => {
try {
const url = `${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.SERVICES}/admin/${slug}/upload-image/`;
const formData = new FormData();
formData.append('image', imageFile);
const response = await fetch(url, {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.message ||
errorData.detail ||
`HTTP error! status: ${response.status}`
);
}
const result = await response.json();
return result.service;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Failed to upload service image: ${error.message}`);
}
throw new Error('Failed to upload service image: Unknown error');
}
},
};
// Utility functions
export const serviceUtils = {
// Format price for display
formatPrice: (price: string | number): string => {
const numPrice = typeof price === 'string' ? parseFloat(price) : price;
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(numPrice);
},
// Get service image URL
getServiceImageUrl: (service: Service): string => {
// If service has an uploaded image
if (service.image && typeof service.image === 'string' && service.image.startsWith('/media/')) {
return `${API_CONFIG.BASE_URL}${service.image}`;
}
// If service has an image_url
if (service.image_url) {
if (service.image_url.startsWith('http')) {
return service.image_url;
}
return `${API_CONFIG.BASE_URL}${service.image_url}`;
}
// Fallback to default image
return '/images/service/default.png';
},
// Generate service slug from title
generateSlug: (title: string): string => {
return title
.toLowerCase()
.replace(/[^a-z0-9\s-]/g, '')
.replace(/\s+/g, '-')
.replace(/-+/g, '-')
.trim();
},
// Check if service is featured
isFeatured: (service: Service): boolean => {
return service.featured;
},
// Sort services by display order
sortByDisplayOrder: (services: Service[]): Service[] => {
return [...services].sort((a, b) => a.display_order - b.display_order);
},
// Filter services by category
filterByCategory: (services: Service[], categorySlug: string): Service[] => {
return services.filter(service => service.category?.slug === categorySlug);
},
// Get services within price range
filterByPriceRange: (services: Service[], minPrice: number, maxPrice: number): Service[] => {
return services.filter(service => {
const price = parseFloat(service.price);
return price >= minPrice && price <= maxPrice;
});
},
};

View File

@@ -0,0 +1,414 @@
import { API_CONFIG } from '../config/api';
const BASE_URL = `${API_CONFIG.BASE_URL}/api/support`;
// ==================== Types ====================
export interface TicketStatus {
id: number;
name: string;
color: string;
description: string;
is_closed: boolean;
display_order: number;
}
export interface TicketPriority {
id: number;
name: string;
level: number;
color: string;
description: string;
sla_hours: number;
}
export interface TicketCategory {
id: number;
name: string;
description: string;
color: string;
icon: string;
display_order: number;
}
export interface TicketMessage {
id: number;
ticket: number;
message_type: string;
content: string;
author_name: string;
author_email: string;
is_internal: boolean;
created_at: string;
updated_at: string;
attachments: string[];
is_read: boolean;
}
export interface TicketActivity {
id: number;
activity_type: string;
description: string;
user_name: string;
old_value: string;
new_value: string;
created_at: string;
}
export interface SupportTicket {
id: number;
ticket_number: string;
title: string;
description: string;
ticket_type: string;
user_name: string;
user_email: string;
user_phone: string;
company: string;
category: number | null;
category_name: string;
priority: number | null;
priority_name: string;
priority_color: string;
status: number | null;
status_name: string;
status_color: string;
assigned_to: number | null;
assigned_at: string | null;
created_at: string;
updated_at: string;
closed_at: string | null;
last_activity: string;
first_response_at: string | null;
sla_deadline: string | null;
tags: string;
is_escalated: boolean;
escalation_reason: string;
attachments: string[];
messages: TicketMessage[];
activities: TicketActivity[];
}
export interface CreateTicketData {
title: string;
description: string;
ticket_type: string;
user_name: string;
user_email: string;
user_phone?: string;
company?: string;
category?: number;
}
export interface KnowledgeBaseCategory {
id: number;
name: string;
slug: string;
description: string;
icon: string;
color: string;
display_order: number;
article_count: number;
}
export interface KnowledgeBaseArticle {
id: number;
title: string;
slug: string;
category: number;
category_name: string;
category_slug: string;
content?: string;
summary: string;
meta_description?: string;
keywords?: string;
is_featured: boolean;
view_count: number;
helpful_count: number;
not_helpful_count: number;
created_at: string;
updated_at: string;
published_at: string;
}
export interface SupportSettings {
id: number;
setting_name: string;
setting_value: string;
description: string;
is_active: boolean;
}
// ==================== API Functions ====================
/**
* Fetch all ticket categories
*/
export const getTicketCategories = async (): Promise<TicketCategory[]> => {
try {
const response = await fetch(`${BASE_URL}/categories/`);
if (!response.ok) throw new Error('Failed to fetch ticket categories');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Fetch all ticket statuses
*/
export const getTicketStatuses = async (): Promise<TicketStatus[]> => {
try {
const response = await fetch(`${BASE_URL}/statuses/`);
if (!response.ok) throw new Error('Failed to fetch ticket statuses');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Fetch all ticket priorities
*/
export const getTicketPriorities = async (): Promise<TicketPriority[]> => {
try {
const response = await fetch(`${BASE_URL}/priorities/`);
if (!response.ok) throw new Error('Failed to fetch ticket priorities');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Create a new support ticket
*/
export const createTicket = async (data: CreateTicketData): Promise<SupportTicket> => {
try {
const response = await fetch(`${BASE_URL}/tickets/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
// Handle specific HTTP status codes
if (response.status === 400) {
// Handle validation errors
if (errorData.user_email) {
throw new Error(errorData.user_email[0] || 'Email validation failed');
}
if (errorData.title) {
throw new Error(errorData.title[0] || 'Title is required');
}
if (errorData.description) {
throw new Error(errorData.description[0] || 'Description is required');
}
if (errorData.user_name) {
throw new Error(errorData.user_name[0] || 'Name is required');
}
// Generic validation error
throw new Error(errorData.detail || errorData.message || 'Please check all required fields and try again');
} else if (response.status === 429) {
throw new Error('Too many requests. Please wait a moment and try again');
} else if (response.status >= 500) {
throw new Error('Server error. Please try again later');
} else if (response.status === 403) {
throw new Error('Access denied. Please contact support if this persists');
} else if (response.status === 404) {
throw new Error('Service not found. Please refresh the page and try again');
}
throw new Error(errorData.detail || errorData.message || 'An unexpected error occurred');
}
return await response.json();
} catch (error) {
throw error;
}
};
/**
* Check ticket status by ticket number
*/
export const checkTicketStatus = async (ticketNumber: string): Promise<SupportTicket> => {
try {
const response = await fetch(`${BASE_URL}/tickets/check-status/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ ticket_number: ticketNumber }),
});
if (!response.ok) {
if (response.status === 404) {
throw new Error('Ticket not found');
}
throw new Error('Failed to check ticket status');
}
return await response.json();
} catch (error) {
throw error;
}
};
/**
* Add a message to a ticket
*/
export const addTicketMessage = async (
ticketId: number,
content: string,
authorName: string,
authorEmail: string
): Promise<TicketMessage> => {
try {
const response = await fetch(`${BASE_URL}/tickets/${ticketId}/add-message/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
content,
author_name: authorName,
author_email: authorEmail,
}),
});
if (!response.ok) throw new Error('Failed to add ticket message');
return await response.json();
} catch (error) {
throw error;
}
};
/**
* Fetch all knowledge base categories
*/
export const getKnowledgeBaseCategories = async (): Promise<KnowledgeBaseCategory[]> => {
try {
const response = await fetch(`${BASE_URL}/knowledge-base-categories/`);
if (!response.ok) throw new Error('Failed to fetch knowledge base categories');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Fetch all knowledge base articles
*/
export const getKnowledgeBaseArticles = async (search?: string): Promise<KnowledgeBaseArticle[]> => {
try {
const url = search
? `${BASE_URL}/knowledge-base/?search=${encodeURIComponent(search)}`
: `${BASE_URL}/knowledge-base/`;
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to fetch knowledge base articles');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Fetch featured knowledge base articles
*/
export const getFeaturedArticles = async (): Promise<KnowledgeBaseArticle[]> => {
try {
const response = await fetch(`${BASE_URL}/knowledge-base/featured/`);
if (!response.ok) throw new Error('Failed to fetch featured articles');
const data = await response.json();
// Handle both array and paginated responses
return Array.isArray(data) ? data : (data.results || data);
} catch (error) {
throw error;
}
};
/**
* Fetch a single knowledge base article by slug
*/
export const getKnowledgeBaseArticle = async (slug: string): Promise<KnowledgeBaseArticle> => {
try {
const response = await fetch(`${BASE_URL}/knowledge-base/${slug}/`);
if (!response.ok) throw new Error('Failed to fetch knowledge base article');
return await response.json();
} catch (error) {
throw error;
}
};
/**
* Fetch articles by category
*/
export const getArticlesByCategory = async (categorySlug: string): Promise<KnowledgeBaseArticle[]> => {
try {
const response = await fetch(`${BASE_URL}/knowledge-base/by-category/${categorySlug}/`);
if (!response.ok) throw new Error('Failed to fetch articles by category');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Mark an article as helpful or not helpful
*/
export const markArticleHelpful = async (slug: string, helpful: boolean): Promise<{ helpful_count: number; not_helpful_count: number }> => {
try {
const response = await fetch(`${BASE_URL}/knowledge-base/${slug}/mark-helpful/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ helpful }),
});
if (!response.ok) throw new Error('Failed to mark article helpful');
return await response.json();
} catch (error) {
throw error;
}
};
/**
* Fetch support settings
*/
export const getSupportSettings = async (): Promise<SupportSettings[]> => {
try {
const response = await fetch(`${BASE_URL}/settings/`);
if (!response.ok) throw new Error('Failed to fetch support settings');
const data = await response.json();
// Handle paginated response
return data.results || data;
} catch (error) {
throw error;
}
};
/**
* Fetch a specific support setting by name
*/
export const getSupportSetting = async (settingName: string): Promise<SupportSettings> => {
try {
const response = await fetch(`${BASE_URL}/settings/${settingName}/`);
if (!response.ok) throw new Error('Failed to fetch support setting');
return await response.json();
} catch (error) {
throw error;
}
};