Feature 4: Implement Ticket Templates
Add ticket template system for quick ticket creation: - Created TemplateModel.php with full CRUD operations for templates - Added get_template.php API endpoint to fetch template data - Updated TicketController to load templates in create() method - Modified CreateTicketView.php to include template selector dropdown - Added loadTemplate() JavaScript function to populate form fields - Templates include: title, description, category, type, and default priority - Database already seeded with default templates (Hardware Failure, Software Installation, Network Issue, Maintenance Request) Users can now select from predefined templates when creating tickets, speeding up common ticket creation workflows. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
45
api/get_template.php
Normal file
45
api/get_template.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once dirname(__DIR__) . '/config/db.php';
|
||||
require_once dirname(__DIR__) . '/models/TemplateModel.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Check authentication
|
||||
if (!isset($_SESSION['user']) || !isset($_SESSION['user']['user_id'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Not authenticated']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get template ID from query parameter
|
||||
$templateId = $_GET['template_id'] ?? null;
|
||||
|
||||
if (!$templateId) {
|
||||
echo json_encode(['success' => false, 'error' => 'Template ID required']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Create database connection
|
||||
$conn = new mysqli(
|
||||
$GLOBALS['config']['DB_HOST'],
|
||||
$GLOBALS['config']['DB_USER'],
|
||||
$GLOBALS['config']['DB_PASS'],
|
||||
$GLOBALS['config']['DB_NAME']
|
||||
);
|
||||
|
||||
if ($conn->connect_error) {
|
||||
echo json_encode(['success' => false, 'error' => 'Database connection failed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get template
|
||||
$templateModel = new TemplateModel($conn);
|
||||
$template = $templateModel->getTemplateById($templateId);
|
||||
|
||||
$conn->close();
|
||||
|
||||
if ($template) {
|
||||
echo json_encode(['success' => true, 'template' => $template]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'error' => 'Template not found']);
|
||||
}
|
||||
@@ -852,3 +852,64 @@ function saveTicket() {
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Load template data into the create ticket form
|
||||
*/
|
||||
function loadTemplate() {
|
||||
const templateSelect = document.getElementById('templateSelect');
|
||||
const templateId = templateSelect.value;
|
||||
|
||||
if (!templateId) {
|
||||
// Clear form when "No Template" is selected
|
||||
document.getElementById('title').value = '';
|
||||
document.getElementById('description').value = '';
|
||||
document.getElementById('priority').value = '4';
|
||||
document.getElementById('category').value = 'General';
|
||||
document.getElementById('type').value = 'Issue';
|
||||
return;
|
||||
}
|
||||
|
||||
// Fetch template data
|
||||
fetch(`/api/get_template.php?template_id=${templateId}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch template');
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
if (data.success && data.template) {
|
||||
const template = data.template;
|
||||
|
||||
// Populate form fields with template data
|
||||
if (template.title_template) {
|
||||
document.getElementById('title').value = template.title_template;
|
||||
}
|
||||
|
||||
if (template.description_template) {
|
||||
document.getElementById('description').value = template.description_template;
|
||||
}
|
||||
|
||||
if (template.category) {
|
||||
document.getElementById('category').value = template.category;
|
||||
}
|
||||
|
||||
if (template.type) {
|
||||
document.getElementById('type').value = template.type;
|
||||
}
|
||||
|
||||
if (template.default_priority) {
|
||||
document.getElementById('priority').value = template.default_priority;
|
||||
}
|
||||
|
||||
console.log('Template loaded:', template.template_name);
|
||||
} else {
|
||||
console.error('Failed to load template:', data.error);
|
||||
alert('Failed to load template: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error loading template:', error);
|
||||
alert('Error loading template: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ require_once dirname(__DIR__) . '/models/CommentModel.php';
|
||||
require_once dirname(__DIR__) . '/models/AuditLogModel.php';
|
||||
require_once dirname(__DIR__) . '/models/UserModel.php';
|
||||
require_once dirname(__DIR__) . '/models/WorkflowModel.php';
|
||||
require_once dirname(__DIR__) . '/models/TemplateModel.php';
|
||||
|
||||
class TicketController {
|
||||
private $ticketModel;
|
||||
@@ -12,6 +13,7 @@ class TicketController {
|
||||
private $auditLogModel;
|
||||
private $userModel;
|
||||
private $workflowModel;
|
||||
private $templateModel;
|
||||
private $envVars;
|
||||
|
||||
public function __construct($conn) {
|
||||
@@ -20,6 +22,7 @@ class TicketController {
|
||||
$this->auditLogModel = new AuditLogModel($conn);
|
||||
$this->userModel = new UserModel($conn);
|
||||
$this->workflowModel = new WorkflowModel($conn);
|
||||
$this->templateModel = new TemplateModel($conn);
|
||||
|
||||
// Load environment variables for Discord webhook
|
||||
$envPath = dirname(__DIR__) . '/.env';
|
||||
@@ -95,6 +98,7 @@ class TicketController {
|
||||
// Validate input
|
||||
if (empty($ticketData['title'])) {
|
||||
$error = "Title is required";
|
||||
$templates = $this->templateModel->getAllTemplates();
|
||||
include dirname(__DIR__) . '/views/CreateTicketView.php';
|
||||
return;
|
||||
}
|
||||
@@ -116,10 +120,14 @@ class TicketController {
|
||||
exit;
|
||||
} else {
|
||||
$error = $result['error'];
|
||||
$templates = $this->templateModel->getAllTemplates();
|
||||
include dirname(__DIR__) . '/views/CreateTicketView.php';
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// Get all templates for the template selector
|
||||
$templates = $this->templateModel->getAllTemplates();
|
||||
|
||||
// Display the create ticket form
|
||||
include dirname(__DIR__) . '/views/CreateTicketView.php';
|
||||
}
|
||||
|
||||
120
models/TemplateModel.php
Normal file
120
models/TemplateModel.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
/**
|
||||
* TemplateModel - Handles ticket template operations
|
||||
*/
|
||||
class TemplateModel {
|
||||
private $conn;
|
||||
|
||||
public function __construct($conn) {
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active templates
|
||||
*
|
||||
* @return array Array of template records
|
||||
*/
|
||||
public function getAllTemplates() {
|
||||
$sql = "SELECT * FROM ticket_templates WHERE is_active = TRUE ORDER BY template_name";
|
||||
$result = $this->conn->query($sql);
|
||||
|
||||
$templates = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$templates[] = $row;
|
||||
}
|
||||
return $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get template by ID
|
||||
*
|
||||
* @param int $templateId Template ID
|
||||
* @return array|null Template record or null if not found
|
||||
*/
|
||||
public function getTemplateById($templateId) {
|
||||
$sql = "SELECT * FROM ticket_templates WHERE template_id = ? AND is_active = TRUE";
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->bind_param("i", $templateId);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$template = $result->fetch_assoc();
|
||||
$stmt->close();
|
||||
return $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new template
|
||||
*
|
||||
* @param array $data Template data
|
||||
* @param int $createdBy User ID creating the template
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function createTemplate($data, $createdBy) {
|
||||
$sql = "INSERT INTO ticket_templates (template_name, title_template, description_template,
|
||||
category, type, default_priority, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->bind_param("sssssii",
|
||||
$data['template_name'],
|
||||
$data['title_template'],
|
||||
$data['description_template'],
|
||||
$data['category'],
|
||||
$data['type'],
|
||||
$data['default_priority'],
|
||||
$createdBy
|
||||
);
|
||||
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing template
|
||||
*
|
||||
* @param int $templateId Template ID
|
||||
* @param array $data Template data to update
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function updateTemplate($templateId, $data) {
|
||||
$sql = "UPDATE ticket_templates SET
|
||||
template_name = ?,
|
||||
title_template = ?,
|
||||
description_template = ?,
|
||||
category = ?,
|
||||
type = ?,
|
||||
default_priority = ?
|
||||
WHERE template_id = ?";
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->bind_param("ssssiii",
|
||||
$data['template_name'],
|
||||
$data['title_template'],
|
||||
$data['description_template'],
|
||||
$data['category'],
|
||||
$data['type'],
|
||||
$data['default_priority'],
|
||||
$templateId
|
||||
);
|
||||
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deactivate a template (soft delete)
|
||||
*
|
||||
* @param int $templateId Template ID
|
||||
* @return bool Success status
|
||||
*/
|
||||
public function deactivateTemplate($templateId) {
|
||||
$sql = "UPDATE ticket_templates SET is_active = FALSE WHERE template_id = ?";
|
||||
$stmt = $this->conn->prepare($sql);
|
||||
$stmt->bind_param("i", $templateId);
|
||||
|
||||
$result = $stmt->execute();
|
||||
$stmt->close();
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,20 @@
|
||||
|
||||
<form method="POST" action="<?php echo $GLOBALS['config']['BASE_URL']; ?>/ticket/create" class="ticket-form">
|
||||
<div class="ticket-details">
|
||||
<div class="detail-group">
|
||||
<label for="template">Use Template (Optional)</label>
|
||||
<select id="templateSelect" class="editable" onchange="loadTemplate()">
|
||||
<option value="">-- No Template --</option>
|
||||
<?php if (isset($templates) && !empty($templates)): ?>
|
||||
<?php foreach ($templates as $template): ?>
|
||||
<option value="<?php echo $template['template_id']; ?>">
|
||||
<?php echo htmlspecialchars($template['template_name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<label for="title">Title</label>
|
||||
<input type="text" id="title" name="title" class="editable" required>
|
||||
|
||||
Reference in New Issue
Block a user