diff --git a/resources/js/Pages/Ticketing/Settings.vue b/resources/js/Pages/Ticketing/Settings.vue
index f0d90e0..6191488 100644
--- a/resources/js/Pages/Ticketing/Settings.vue
+++ b/resources/js/Pages/Ticketing/Settings.vue
@@ -21,6 +21,10 @@
{{ $page.props.errors.priority }}
+
+ {{ $page.props.errors.project }}
+
+
+
+
+
+
Projects
+
+
+
+
+
+
+
No projects defined yet.
+
+
+
{{ project.name }}
+
{{ groups.find(g => g.id === project.group_id)?.name || 'Unknown Group' }} ยท {{ project.status }}
+
{{ project.description }}
+
+
+
+
+
+
+
+
+
+
Edit Project: {{ editingProject.name }}
+
+
+
@@ -286,6 +379,19 @@
+
+
+
+
Delete project?
+
+ Delete {{ pendingProjectDelete?.name }}? This cannot be undone.
+
+
+
+
+
+
+
diff --git a/src/Http/Controllers/TicketingSettingsController.php b/src/Http/Controllers/TicketingSettingsController.php
index 27cf16a..2516743 100644
--- a/src/Http/Controllers/TicketingSettingsController.php
+++ b/src/Http/Controllers/TicketingSettingsController.php
@@ -5,6 +5,7 @@ namespace Dashboard\Ticketing\Http\Controllers;
use Dashboard\Ticketing\Models\PriorityLevel;
use Dashboard\Ticketing\Models\TicketingAgentAccess;
use Dashboard\Ticketing\Models\TicketingGroup;
+use Dashboard\Ticketing\Models\TicketingProject;
use Illuminate\Validation\Rule;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
@@ -87,10 +88,17 @@ class TicketingSettingsController extends Controller
: PriorityLevel::where(fn($q) => $q->whereNull('group_id')->orWhereIn('group_id', $myGroupIds))
->orderBy('sort_order')->get();
+ $projects = $isBootstrap
+ ? collect()
+ : TicketingProject::whereIn('group_id', $myGroupIds)
+ ->orderBy('name')
+ ->get();
+
return Inertia::render('Ticketing/Settings', [
'groups' => $groups,
'agents' => $agents,
'priorities' => $priorities,
+ 'projects' => $projects,
'myGroupIds' => $myGroupIds,
'isBootstrap' => $isBootstrap,
'isSiteAdmin' => $this->isSiteAdmin(),
@@ -261,4 +269,57 @@ class TicketingSettingsController extends Controller
return back()->with('success', 'Priority level removed.');
}
+
+ public function storeProject(Request $request)
+ {
+ $this->requireAgentAccess();
+
+ $validated = $request->validate([
+ 'group_id' => 'required|exists:ticketing_groups,id',
+ 'name' => 'required|string|max:100',
+ 'description' => 'nullable|string',
+ 'status' => 'required|in:active,archived',
+ ]);
+
+ $this->requireManagerAccess($validated['group_id']);
+
+ TicketingProject::create([
+ ...$validated,
+ 'created_by' => Auth::id(),
+ ]);
+
+ return back()->with('success', 'Project created.');
+ }
+
+ public function updateProject(Request $request, TicketingProject $project)
+ {
+ $this->requireAgentAccess();
+ $this->requireManagerAccess($project->group_id);
+
+ $validated = $request->validate([
+ 'name' => 'required|string|max:100',
+ 'description' => 'nullable|string',
+ 'status' => 'required|in:active,archived',
+ ]);
+
+ $project->update($validated);
+
+ return back()->with('success', 'Project updated.');
+ }
+
+ public function destroyProject(TicketingProject $project)
+ {
+ $this->requireAgentAccess();
+ $this->requireManagerAccess($project->group_id);
+
+ if ($project->tickets()->exists()) {
+ return back()->withErrors([
+ 'project' => 'Cannot delete a project that is in use by tickets.',
+ ]);
+ }
+
+ $project->delete();
+
+ return back()->with('success', 'Project removed.');
+ }
}
diff --git a/src/routes/ticketing.php b/src/routes/ticketing.php
index dc9cff4..63decdd 100644
--- a/src/routes/ticketing.php
+++ b/src/routes/ticketing.php
@@ -19,6 +19,9 @@ Route::middleware(['web', 'auth', 'app.access:ticketing'])->prefix('app/ticketin
Route::post('/settings/priorities', [TicketingSettingsController::class, 'storePriority'])->name('settings.priorities.store');
Route::put('/settings/priorities/{priority}', [TicketingSettingsController::class, 'updatePriority'])->name('settings.priorities.update');
Route::delete('/settings/priorities/{priority}', [TicketingSettingsController::class, 'destroyPriority'])->name('settings.priorities.destroy');
+ Route::post('/settings/projects', [TicketingSettingsController::class, 'storeProject'])->name('settings.projects.store');
+ Route::put('/settings/projects/{project}', [TicketingSettingsController::class, 'updateProject'])->name('settings.projects.update');
+ Route::delete('/settings/projects/{project}', [TicketingSettingsController::class, 'destroyProject'])->name('settings.projects.destroy');
// Ticket routes
Route::get('/', [TicketController::class, 'index'])->name('index');