300 lines
14 KiB
Vue
300 lines
14 KiB
Vue
<template>
|
|
<div class="max-w-5xl mx-auto py-8 px-4">
|
|
<div class="mb-6">
|
|
<Link :href="route('ticketing.index')" class="text-sm text-indigo-600 hover:underline">← Back to tickets</Link>
|
|
<h1 class="text-2xl font-bold text-gray-900 dark:text-white mt-2">Ticketing Settings</h1>
|
|
</div>
|
|
|
|
<!-- Flash message -->
|
|
<div v-if="$page.props.flash?.success" class="mb-4 px-4 py-2 bg-green-50 dark:bg-green-900/30 border border-green-200 dark:border-green-700 text-green-700 dark:text-green-300 rounded-lg text-sm">
|
|
{{ $page.props.flash.success }}
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<div class="flex gap-1 border-b border-gray-200 dark:border-gray-700 mb-6">
|
|
<button
|
|
v-for="tab in tabs"
|
|
:key="tab.key"
|
|
@click="activeTab = tab.key"
|
|
:class="[
|
|
'px-4 py-2.5 text-sm font-medium border-b-2 transition',
|
|
activeTab === tab.key
|
|
? 'border-indigo-600 text-indigo-600 dark:text-indigo-400'
|
|
: 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700'
|
|
]"
|
|
>{{ tab.label }}</button>
|
|
</div>
|
|
|
|
<!-- Groups Tab -->
|
|
<div v-if="activeTab === 'groups'">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100">Groups</h2>
|
|
<button @click="showAddGroup = !showAddGroup" class="text-sm text-indigo-600 hover:underline">
|
|
{{ showAddGroup ? 'Cancel' : '+ Add Group' }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Add Group Form -->
|
|
<div v-if="showAddGroup" class="bg-gray-50 dark:bg-gray-700 rounded-xl p-4 mb-5 space-y-3">
|
|
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">New Group</h3>
|
|
<form @submit.prevent="submitGroup" class="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Name</label>
|
|
<input v-model="groupForm.name" required type="text" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Prefix (e.g. IT)</label>
|
|
<input v-model="groupForm.prefix" required type="text" maxlength="10" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg uppercase" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Email Address</label>
|
|
<input v-model="groupForm.email_address" type="email" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Color</label>
|
|
<input v-model="groupForm.color" type="color" class="h-9 w-full border-gray-300 rounded-lg cursor-pointer" />
|
|
</div>
|
|
<div class="col-span-2 flex justify-end">
|
|
<button type="submit" :disabled="groupForm.processing" class="bg-indigo-600 text-white text-sm px-4 py-2 rounded-lg hover:bg-indigo-700 disabled:opacity-60">
|
|
Create Group
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Groups List -->
|
|
<div class="space-y-3">
|
|
<div v-if="groups.length === 0" class="text-sm text-gray-400 italic">No groups yet.</div>
|
|
<div
|
|
v-for="group in groups"
|
|
:key="group.id"
|
|
class="flex items-center justify-between px-4 py-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl"
|
|
>
|
|
<div class="flex items-center gap-3">
|
|
<span class="w-3 h-3 rounded-full" :style="{ backgroundColor: group.color }"></span>
|
|
<div>
|
|
<p class="font-medium text-sm text-gray-800 dark:text-gray-100">{{ group.name }}</p>
|
|
<p class="text-xs text-gray-400">{{ group.prefix }} · {{ group.email_address || 'No email' }}</p>
|
|
</div>
|
|
</div>
|
|
<button @click="startEditGroup(group)" class="text-xs text-indigo-600 hover:underline">Edit</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Group Form -->
|
|
<div v-if="editingGroup" class="mt-4 bg-gray-50 dark:bg-gray-700 rounded-xl p-4 space-y-3">
|
|
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-200">Edit: {{ editingGroup.name }}</h3>
|
|
<form @submit.prevent="submitEditGroup" class="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Name</label>
|
|
<input v-model="editGroupForm.name" required type="text" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Prefix</label>
|
|
<input v-model="editGroupForm.prefix" required type="text" maxlength="10" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg uppercase" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Email Address</label>
|
|
<input v-model="editGroupForm.email_address" type="email" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Color</label>
|
|
<input v-model="editGroupForm.color" type="color" class="h-9 w-full border-gray-300 rounded-lg cursor-pointer" />
|
|
</div>
|
|
<div class="col-span-2 flex justify-end gap-2">
|
|
<button type="button" @click="editingGroup = null" class="text-sm text-gray-500 px-3 py-2 rounded-lg border border-gray-300 hover:bg-gray-100">Cancel</button>
|
|
<button type="submit" :disabled="editGroupForm.processing" class="bg-indigo-600 text-white text-sm px-4 py-2 rounded-lg hover:bg-indigo-700 disabled:opacity-60">Save</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Agents Tab -->
|
|
<div v-if="activeTab === 'agents'">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100">Agents</h2>
|
|
<button @click="showAddAgent = !showAddAgent" class="text-sm text-indigo-600 hover:underline">
|
|
{{ showAddAgent ? 'Cancel' : '+ Add Agent' }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Add Agent Form -->
|
|
<div v-if="showAddAgent" class="bg-gray-50 dark:bg-gray-700 rounded-xl p-4 mb-5 space-y-3">
|
|
<form @submit.prevent="submitAgent" class="grid grid-cols-3 gap-3">
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">User ID</label>
|
|
<input v-model="agentForm.user_id" required type="number" placeholder="User ID" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Group</label>
|
|
<select v-model="agentForm.group_id" required class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg">
|
|
<option value="">Select group</option>
|
|
<option v-for="g in groups" :key="g.id" :value="g.id">{{ g.name }}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Role</label>
|
|
<select v-model="agentForm.role" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg">
|
|
<option value="agent">Agent</option>
|
|
<option value="manager">Manager</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-span-3 flex justify-end">
|
|
<button type="submit" :disabled="agentForm.processing" class="bg-indigo-600 text-white text-sm px-4 py-2 rounded-lg hover:bg-indigo-700 disabled:opacity-60">
|
|
Add Agent
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Agents List -->
|
|
<div class="space-y-2">
|
|
<div v-if="agents.length === 0" class="text-sm text-gray-400 italic">No agents configured yet.</div>
|
|
<div
|
|
v-for="access in agents"
|
|
:key="access.id"
|
|
class="flex items-center justify-between px-4 py-2.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl"
|
|
>
|
|
<div>
|
|
<p class="text-sm font-medium text-gray-800 dark:text-gray-100">{{ access.user?.name || 'User #' + access.user_id }}</p>
|
|
<p class="text-xs text-gray-400">{{ access.user?.email }} · {{ access.group?.name || 'Unknown Group' }} · <span class="capitalize">{{ access.role }}</span></p>
|
|
</div>
|
|
<button
|
|
@click="removeAgent(access)"
|
|
class="text-xs text-red-500 hover:underline"
|
|
>Remove</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Priorities Tab -->
|
|
<div v-if="activeTab === 'priorities'">
|
|
<div class="flex justify-between items-center mb-4">
|
|
<h2 class="text-lg font-semibold text-gray-800 dark:text-gray-100">Priority Levels</h2>
|
|
<button @click="showAddPriority = !showAddPriority" class="text-sm text-indigo-600 hover:underline">
|
|
{{ showAddPriority ? 'Cancel' : '+ Add Priority' }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Add Priority Form -->
|
|
<div v-if="showAddPriority" class="bg-gray-50 dark:bg-gray-700 rounded-xl p-4 mb-5 space-y-3">
|
|
<form @submit.prevent="submitPriority" class="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Name</label>
|
|
<input v-model="priorityForm.name" required type="text" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Color</label>
|
|
<input v-model="priorityForm.color" type="color" class="h-9 w-full border-gray-300 rounded-lg cursor-pointer" />
|
|
</div>
|
|
<div class="col-span-2">
|
|
<label class="block text-xs text-gray-500 mb-1">Description</label>
|
|
<input v-model="priorityForm.description" type="text" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Sort Order</label>
|
|
<input v-model="priorityForm.sort_order" type="number" min="0" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg" />
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs text-gray-500 mb-1">Group (blank = global)</label>
|
|
<select v-model="priorityForm.group_id" class="w-full text-sm border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-white rounded-lg">
|
|
<option :value="null">Global</option>
|
|
<option v-for="g in groups" :key="g.id" :value="g.id">{{ g.name }}</option>
|
|
</select>
|
|
</div>
|
|
<div class="col-span-2 flex justify-end">
|
|
<button type="submit" :disabled="priorityForm.processing" class="bg-indigo-600 text-white text-sm px-4 py-2 rounded-lg hover:bg-indigo-700 disabled:opacity-60">
|
|
Create Priority
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Priorities List -->
|
|
<div class="space-y-2">
|
|
<div v-if="priorities.length === 0" class="text-sm text-gray-400 italic">No priorities defined yet.</div>
|
|
<div
|
|
v-for="p in priorities"
|
|
:key="p.id"
|
|
class="flex items-center gap-3 px-4 py-2.5 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl"
|
|
>
|
|
<span class="w-3 h-3 rounded-full flex-shrink-0" :style="{ backgroundColor: p.color }"></span>
|
|
<div class="flex-1">
|
|
<p class="text-sm font-medium text-gray-800 dark:text-gray-100">{{ p.name }}</p>
|
|
<p v-if="p.description" class="text-xs text-gray-400">{{ p.description }}</p>
|
|
</div>
|
|
<span class="text-xs text-gray-400">{{ p.group_id ? 'Group-specific' : 'Global' }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { Link, useForm, router } from '@inertiajs/vue3'
|
|
|
|
const props = defineProps({
|
|
groups: Array,
|
|
agents: Array,
|
|
priorities: Array,
|
|
myGroupIds: Array,
|
|
})
|
|
|
|
const activeTab = ref('groups')
|
|
const tabs = [
|
|
{ key: 'groups', label: 'Groups' },
|
|
{ key: 'agents', label: 'Agents' },
|
|
{ key: 'priorities', label: 'Priorities' },
|
|
]
|
|
|
|
const showAddGroup = ref(false)
|
|
const showAddAgent = ref(false)
|
|
const showAddPriority = ref(false)
|
|
const editingGroup = ref(null)
|
|
|
|
// Forms
|
|
const groupForm = useForm({ name: '', email_address: '', color: '#6366f1', prefix: '' })
|
|
const editGroupForm = useForm({ name: '', email_address: '', color: '#6366f1', prefix: '' })
|
|
const agentForm = useForm({ user_id: '', group_id: '', role: 'agent' })
|
|
const priorityForm = useForm({ name: '', color: '#6b7280', description: '', sort_order: 0, group_id: null })
|
|
|
|
function submitGroup() {
|
|
groupForm.post(route('ticketing.settings.groups.store'), {
|
|
onSuccess: () => { showAddGroup.value = false; groupForm.reset() }
|
|
})
|
|
}
|
|
|
|
function startEditGroup(group) {
|
|
editingGroup.value = group
|
|
editGroupForm.name = group.name
|
|
editGroupForm.email_address = group.email_address || ''
|
|
editGroupForm.color = group.color
|
|
editGroupForm.prefix = group.prefix
|
|
}
|
|
|
|
function submitEditGroup() {
|
|
editGroupForm.put(route('ticketing.settings.groups.update', { group: editingGroup.value.id }), {
|
|
onSuccess: () => { editingGroup.value = null }
|
|
})
|
|
}
|
|
|
|
function submitAgent() {
|
|
agentForm.post(route('ticketing.settings.agents.store'), {
|
|
onSuccess: () => { showAddAgent.value = false; agentForm.reset() }
|
|
})
|
|
}
|
|
|
|
function removeAgent(access) {
|
|
if (confirm('Remove this agent?')) {
|
|
router.delete(route('ticketing.settings.agents.destroy', { access: access.id }))
|
|
}
|
|
}
|
|
|
|
function submitPriority() {
|
|
priorityForm.post(route('ticketing.settings.priorities.store'), {
|
|
onSuccess: () => { showAddPriority.value = false; priorityForm.reset() }
|
|
})
|
|
}
|
|
</script>
|