@escalated-dev/escalated 0.2.1 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/package.json +1 -1
  2. package/src/components/ActivityTimeline.vue +8 -4
  3. package/src/components/AssigneeSelect.vue +6 -2
  4. package/src/components/AttachmentList.vue +9 -3
  5. package/src/components/EscalatedLayout.vue +219 -213
  6. package/src/components/FileDropzone.vue +9 -4
  7. package/src/components/PriorityBadge.vue +20 -2
  8. package/src/components/ReplyComposer.vue +52 -3
  9. package/src/components/ReplyThread.vue +14 -6
  10. package/src/components/SlaTimer.vue +12 -6
  11. package/src/components/StatsCard.vue +26 -6
  12. package/src/components/StatusBadge.vue +24 -2
  13. package/src/components/TagSelect.vue +8 -4
  14. package/src/components/TicketFilters.vue +17 -11
  15. package/src/components/TicketList.vue +45 -2
  16. package/src/components/TicketSidebar.vue +21 -14
  17. package/src/pages/Admin/CannedResponses/Index.vue +29 -17
  18. package/src/pages/Admin/Departments/Form.vue +12 -11
  19. package/src/pages/Admin/Departments/Index.vue +26 -18
  20. package/src/pages/Admin/EscalationRules/Form.vue +21 -20
  21. package/src/pages/Admin/EscalationRules/Index.vue +26 -18
  22. package/src/pages/Admin/Reports.vue +30 -16
  23. package/src/pages/Admin/Settings.vue +260 -0
  24. package/src/pages/Admin/SlaPolicies/Form.vue +21 -20
  25. package/src/pages/Admin/SlaPolicies/Index.vue +28 -20
  26. package/src/pages/Admin/Tags/Index.vue +48 -23
  27. package/src/pages/Admin/Tickets/Index.vue +22 -0
  28. package/src/pages/Admin/Tickets/Show.vue +109 -0
  29. package/src/pages/Agent/Dashboard.vue +37 -25
  30. package/src/pages/Agent/TicketShow.vue +12 -12
  31. package/src/pages/Customer/Show.vue +2 -2
  32. package/src/pages/Guest/Create.vue +97 -0
  33. package/src/pages/Guest/Show.vue +86 -0
@@ -0,0 +1,97 @@
1
+ <script setup>
2
+ import EscalatedLayout from '../../components/EscalatedLayout.vue';
3
+ import FileDropzone from '../../components/FileDropzone.vue';
4
+ import { useForm, Link } from '@inertiajs/vue3';
5
+
6
+ const props = defineProps({
7
+ departments: Array,
8
+ priorities: Array,
9
+ });
10
+
11
+ const form = useForm({
12
+ guest_name: '',
13
+ guest_email: '',
14
+ subject: '',
15
+ description: '',
16
+ priority: 'medium',
17
+ department_id: '',
18
+ attachments: [],
19
+ });
20
+
21
+ function submit() {
22
+ form.post(route('escalated.guest.tickets.store'));
23
+ }
24
+ </script>
25
+
26
+ <template>
27
+ <EscalatedLayout title="Submit a Ticket">
28
+ <div class="mx-auto max-w-2xl">
29
+ <div class="mb-6 text-center">
30
+ <h1 class="text-xl font-semibold text-gray-900">Submit a Support Ticket</h1>
31
+ <p class="mt-1 text-sm text-gray-500">No account needed. We'll give you a link to track your ticket.</p>
32
+ <Link :href="route('login')" class="mt-2 inline-block text-sm text-indigo-600 hover:text-indigo-700">
33
+ Already have an account? Sign in
34
+ </Link>
35
+ </div>
36
+
37
+ <form @submit.prevent="submit" class="space-y-5 rounded-lg border border-gray-200 bg-white p-6">
38
+ <div class="grid grid-cols-2 gap-4">
39
+ <div>
40
+ <label class="block text-sm font-medium text-gray-700">Your Name</label>
41
+ <input v-model="form.guest_name" type="text" required
42
+ class="mt-1 w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
43
+ <div v-if="form.errors.guest_name" class="mt-1 text-sm text-red-600">{{ form.errors.guest_name }}</div>
44
+ </div>
45
+ <div>
46
+ <label class="block text-sm font-medium text-gray-700">Email Address</label>
47
+ <input v-model="form.guest_email" type="email" required
48
+ class="mt-1 w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
49
+ <div v-if="form.errors.guest_email" class="mt-1 text-sm text-red-600">{{ form.errors.guest_email }}</div>
50
+ </div>
51
+ </div>
52
+
53
+ <div>
54
+ <label class="block text-sm font-medium text-gray-700">Subject</label>
55
+ <input v-model="form.subject" type="text" required
56
+ class="mt-1 w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
57
+ <div v-if="form.errors.subject" class="mt-1 text-sm text-red-600">{{ form.errors.subject }}</div>
58
+ </div>
59
+
60
+ <div>
61
+ <label class="block text-sm font-medium text-gray-700">Description</label>
62
+ <textarea v-model="form.description" rows="6" required
63
+ class="mt-1 w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea>
64
+ <div v-if="form.errors.description" class="mt-1 text-sm text-red-600">{{ form.errors.description }}</div>
65
+ </div>
66
+
67
+ <div class="grid grid-cols-2 gap-4">
68
+ <div>
69
+ <label class="block text-sm font-medium text-gray-700">Priority</label>
70
+ <select v-model="form.priority" class="mt-1 w-full rounded-lg border-gray-300 shadow-sm">
71
+ <option v-for="p in priorities" :key="p" :value="p" class="capitalize">{{ p }}</option>
72
+ </select>
73
+ </div>
74
+ <div>
75
+ <label class="block text-sm font-medium text-gray-700">Department</label>
76
+ <select v-model="form.department_id" class="mt-1 w-full rounded-lg border-gray-300 shadow-sm">
77
+ <option value="">General</option>
78
+ <option v-for="d in departments" :key="d.id" :value="d.id">{{ d.name }}</option>
79
+ </select>
80
+ </div>
81
+ </div>
82
+
83
+ <div>
84
+ <label class="block text-sm font-medium text-gray-700 mb-1">Attachments</label>
85
+ <FileDropzone v-model="form.attachments" />
86
+ </div>
87
+
88
+ <div class="flex justify-end">
89
+ <button type="submit" :disabled="form.processing"
90
+ class="rounded-lg bg-indigo-600 px-6 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:opacity-50">
91
+ {{ form.processing ? 'Submitting...' : 'Submit Ticket' }}
92
+ </button>
93
+ </div>
94
+ </form>
95
+ </div>
96
+ </EscalatedLayout>
97
+ </template>
@@ -0,0 +1,86 @@
1
+ <script setup>
2
+ import EscalatedLayout from '../../components/EscalatedLayout.vue';
3
+ import StatusBadge from '../../components/StatusBadge.vue';
4
+ import PriorityBadge from '../../components/PriorityBadge.vue';
5
+ import ReplyThread from '../../components/ReplyThread.vue';
6
+ import AttachmentList from '../../components/AttachmentList.vue';
7
+ import { useForm } from '@inertiajs/vue3';
8
+ import { ref } from 'vue';
9
+
10
+ const props = defineProps({ ticket: Object, token: String });
11
+
12
+ const replyForm = useForm({ body: '', attachments: [] });
13
+ const showCopied = ref(false);
14
+
15
+ function submitReply() {
16
+ replyForm.post(route('escalated.guest.tickets.reply', props.token), {
17
+ onSuccess: () => replyForm.reset(),
18
+ });
19
+ }
20
+
21
+ function copyLink() {
22
+ navigator.clipboard.writeText(window.location.href);
23
+ showCopied.value = true;
24
+ setTimeout(() => showCopied.value = false, 2000);
25
+ }
26
+ </script>
27
+
28
+ <template>
29
+ <EscalatedLayout :title="ticket.subject">
30
+ <div class="mx-auto max-w-3xl">
31
+ <!-- Bookmark notice -->
32
+ <div class="mb-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3">
33
+ <div class="flex items-start gap-3">
34
+ <svg class="mt-0.5 h-5 w-5 shrink-0 text-amber-500" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m9-.75a9 9 0 11-18 0 9 9 0 0118 0zm-9 3.75h.008v.008H12v-.008z" /></svg>
35
+ <div>
36
+ <p class="text-sm font-medium text-amber-800">Bookmark this page</p>
37
+ <p class="mt-0.5 text-xs text-amber-600">This is your private link to view and reply to this ticket.</p>
38
+ </div>
39
+ <button @click="copyLink"
40
+ class="ml-auto shrink-0 rounded-md border border-amber-300 bg-white px-2.5 py-1 text-xs font-medium text-amber-700 hover:bg-amber-50">
41
+ {{ showCopied ? 'Copied!' : 'Copy Link' }}
42
+ </button>
43
+ </div>
44
+ </div>
45
+
46
+ <!-- Ticket header -->
47
+ <div class="mb-4 flex flex-wrap items-center gap-3">
48
+ <span class="font-mono text-sm text-gray-500">{{ ticket.reference }}</span>
49
+ <StatusBadge :status="ticket.status" />
50
+ <PriorityBadge :priority="ticket.priority" />
51
+ <span v-if="ticket.department" class="text-sm text-gray-500">{{ ticket.department.name }}</span>
52
+ </div>
53
+
54
+ <!-- Description -->
55
+ <div class="mb-6 rounded-lg border border-gray-200 bg-white p-4">
56
+ <p class="whitespace-pre-wrap text-sm text-gray-700">{{ ticket.description }}</p>
57
+ <AttachmentList v-if="ticket.attachments?.length" :attachments="ticket.attachments" class="mt-3" />
58
+ </div>
59
+
60
+ <!-- Replies -->
61
+ <div class="mb-6">
62
+ <h2 class="mb-4 text-lg font-semibold text-gray-900">Replies</h2>
63
+ <ReplyThread :replies="ticket.replies || []" />
64
+ </div>
65
+
66
+ <!-- Reply form -->
67
+ <div v-if="ticket.status !== 'closed'" class="rounded-lg border border-gray-200 bg-white p-4">
68
+ <h2 class="mb-4 text-lg font-semibold text-gray-900">Reply</h2>
69
+ <form @submit.prevent="submitReply">
70
+ <textarea v-model="replyForm.body" rows="4" required placeholder="Write your reply..."
71
+ class="w-full rounded-lg border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea>
72
+ <div v-if="replyForm.errors.body" class="mt-1 text-sm text-red-600">{{ replyForm.errors.body }}</div>
73
+ <div class="mt-3 flex justify-end">
74
+ <button type="submit" :disabled="replyForm.processing"
75
+ class="rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-700 disabled:opacity-50">
76
+ {{ replyForm.processing ? 'Sending...' : 'Send Reply' }}
77
+ </button>
78
+ </div>
79
+ </form>
80
+ </div>
81
+ <div v-else class="rounded-lg border border-gray-200 bg-gray-50 px-4 py-6 text-center text-sm text-gray-500">
82
+ This ticket is closed. No further replies can be added.
83
+ </div>
84
+ </div>
85
+ </EscalatedLayout>
86
+ </template>