update
This commit is contained in:
237
gnx-react/lib/api/careerService.ts
Normal file
237
gnx-react/lib/api/careerService.ts
Normal file
@@ -0,0 +1,237 @@
|
||||
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) {
|
||||
console.error('Error fetching jobs:', 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) {
|
||||
console.error('Error fetching job:', 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) {
|
||||
console.error('Error fetching featured jobs:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a job application
|
||||
*/
|
||||
async submitApplication(applicationData: JobApplication): Promise<any> {
|
||||
try {
|
||||
const formData = new FormData();
|
||||
|
||||
// Append all fields to FormData
|
||||
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());
|
||||
|
||||
// Append resume file
|
||||
if (applicationData.resume) {
|
||||
formData.append('resume', applicationData.resume);
|
||||
}
|
||||
|
||||
// Append optional fields
|
||||
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) 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,
|
||||
// Don't set Content-Type header - browser will set it with boundary for multipart/form-data
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || `Failed to submit application: ${response.statusText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error('Error submitting application:', 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) {
|
||||
console.error('Error fetching jobs by department:', 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) {
|
||||
console.error('Error fetching jobs by employment type:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const careerService = new CareerService();
|
||||
|
||||
406
gnx-react/lib/api/supportService.ts
Normal file
406
gnx-react/lib/api/supportService.ts
Normal file
@@ -0,0 +1,406 @@
|
||||
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) {
|
||||
console.error('Error fetching ticket categories:', 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) {
|
||||
console.error('Error fetching ticket statuses:', 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) {
|
||||
console.error('Error fetching ticket priorities:', 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 validation errors
|
||||
if (response.status === 400 && errorData.user_email) {
|
||||
throw new Error(errorData.user_email[0] || 'Email validation failed');
|
||||
}
|
||||
|
||||
throw new Error(errorData.detail || errorData.message || 'Failed to create ticket');
|
||||
}
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.error('Error creating ticket:', 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) {
|
||||
console.error('Error checking ticket status:', 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) {
|
||||
console.error('Error adding ticket message:', 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) {
|
||||
console.error('Error fetching knowledge base categories:', 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) {
|
||||
console.error('Error fetching knowledge base articles:', 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) {
|
||||
console.error('Error fetching featured articles:', 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) {
|
||||
console.error('Error fetching knowledge base article:', 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) {
|
||||
console.error('Error fetching articles by category:', 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) {
|
||||
console.error('Error marking article helpful:', 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) {
|
||||
console.error('Error fetching support settings:', 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) {
|
||||
console.error('Error fetching support setting:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
95
gnx-react/lib/hooks/useCareer.ts
Normal file
95
gnx-react/lib/hooks/useCareer.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
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');
|
||||
console.error('Error fetching jobs:', err);
|
||||
} 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(() => {
|
||||
if (!slug) {
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchJob = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const data = await careerService.getJobBySlug(slug);
|
||||
setJob(data);
|
||||
setError(null);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : 'An error occurred');
|
||||
console.error('Error fetching job:', err);
|
||||
} finally {
|
||||
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');
|
||||
console.error('Error fetching featured jobs:', err);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchFeaturedJobs();
|
||||
}, []);
|
||||
|
||||
return { jobs, loading, error };
|
||||
};
|
||||
|
||||
251
gnx-react/lib/hooks/useSupport.ts
Normal file
251
gnx-react/lib/hooks/useSupport.ts
Normal 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 };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user