407 lines
11 KiB
TypeScript
407 lines
11 KiB
TypeScript
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;
|
|
}
|
|
};
|
|
|