@dragonmastery/dragoncore-vue 0.0.4 → 0.0.6
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.
- package/dist/{CreateTeamForm-zVlGgmL9.js → CreateTeamForm-O_viMOrD.js} +5 -5
- package/dist/{CreateTeamMemberForm-DkCbsJDn.js → CreateTeamMemberForm-BzwDug0x.js} +5 -5
- package/dist/{CreditBalanceDashboard-BTW4IK66.js → CreditBalanceDashboard-CBaQsjyo.js} +5 -5
- package/dist/{CreditManagement-0JxmCIAd.js → CreditManagement-Bal5mbQC.js} +5 -5
- package/dist/CustomerCreateSupportTicketForm-CpnbsCqr.js +27 -0
- package/dist/{CustomerSupportTicketDetailPage-DQa_Zvfe.js → CustomerSupportTicketDetailPage-DZQCplSM.js} +197 -183
- package/dist/CustomerSupportTicketDetailPage-DZQCplSM.js.map +1 -0
- package/dist/CustomerSupportTicketList-CKf8Kyzu.js +27 -0
- package/dist/{CustomerSupportTicketParent-BOYIren9.js → CustomerSupportTicketParent-BnmTFigo.js} +1 -1
- package/dist/{CustomerSupportTicketParent-sT8hpgrA.js → CustomerSupportTicketParent-BzY4pmBk.js} +2 -2
- package/dist/{CustomerSupportTicketParent-sT8hpgrA.js.map → CustomerSupportTicketParent-BzY4pmBk.js.map} +1 -1
- package/dist/CustomerSupportTicketSuccess-B-1n0gP-.js +27 -0
- package/dist/{EditTeamForm-BM90JTjr.js → EditTeamForm-CKnK07nF.js} +5 -5
- package/dist/{ResetPassword-DAn7dYAp.js → ResetPassword-Q8vhelQz.js} +5 -5
- package/dist/{SavedFiltersPage-BNasEKOY.js → SavedFiltersPage-DhhcU1R1.js} +51 -23
- package/dist/SavedFiltersPage-DhhcU1R1.js.map +1 -0
- package/dist/StaffCreateSupportTicketForm-D5ne_W9A.js +27 -0
- package/dist/{StaffSupportTicketDetailPage-D49ibqrO.js → StaffSupportTicketDetailPage-DY07Ez0R.js} +176 -132
- package/dist/StaffSupportTicketDetailPage-DY07Ez0R.js.map +1 -0
- package/dist/StaffSupportTicketList-xD3FaXkS.js +27 -0
- package/dist/{StaffSupportTicketParent-CxrPxXSH.js → StaffSupportTicketParent-CWWhaM37.js} +2 -2
- package/dist/{StaffSupportTicketParent-CxrPxXSH.js.map → StaffSupportTicketParent-CWWhaM37.js.map} +1 -1
- package/dist/{StaffSupportTicketParent-C7Mm7W_0.js → StaffSupportTicketParent-Dp1G85wc.js} +1 -1
- package/dist/StaffSupportTicketSuccess-D1nBsbcC.js +27 -0
- package/dist/{SupportTicketDevLifecycleBadge-Cl4y47Sy.js → SupportTicketDevLifecycleBadge-Ba-Rm6QW.js} +1 -1
- package/dist/{SupportTicketDevLifecycleBadge-Cl4y47Sy.js.map → SupportTicketDevLifecycleBadge-Ba-Rm6QW.js.map} +1 -1
- package/dist/{TeamAttachmentsTab-BoOIuTU1.js → TeamAttachmentsTab-DaCRkUsF.js} +5 -5
- package/dist/{ViewTeam-Bb1WH_Us.js → TeamList-BaZfSOG4.js} +6 -6
- package/dist/{TeamMemberList-CQTxcWNS.js → TeamMemberList-DOG48Y0Q.js} +5 -5
- package/dist/{TeamMemberParent-Bt0kbyKQ.js → TeamMemberParent-CTrhsG1K.js} +5 -5
- package/dist/{TeamParent-BvLiiJq6.js → TeamParent-myjqz30R.js} +5 -5
- package/dist/{TimelineNoteInput-BRsQ2QTz.js → TimelineNoteInput-BVqF4MtZ.js} +31 -8
- package/dist/TimelineNoteInput-BVqF4MtZ.js.map +1 -0
- package/dist/{TimelineSystemEvent-B69B3eeL.js → TimelineSystemEvent-D58zN850.js} +288 -205
- package/dist/TimelineSystemEvent-D58zN850.js.map +1 -0
- package/dist/{TeamList-TpS3BhPd.js → ViewTeam-DRQuV1A3.js} +6 -6
- package/dist/{ViewTeamMember-CBTAnAhS.js → ViewTeamMember-DjbxMkB4.js} +5 -5
- package/dist/{displayIdFormatter-BoKcrgF5.js → displayIdFormatter-B1ZKgofu.js} +1 -1
- package/dist/{displayIdFormatter-BoKcrgF5.js.map → displayIdFormatter-B1ZKgofu.js.map} +1 -1
- package/dist/index.d.ts +962 -938
- package/dist/index.js +6 -6
- package/dist/{src-ChwBeNHB.js → src-wQ7pAFHx.js} +1488 -1416
- package/dist/src-wQ7pAFHx.js.map +1 -0
- package/package.json +2 -2
- package/dist/CustomerCreateSupportTicketForm-CQcv4vrX.js +0 -27
- package/dist/CustomerSupportTicketDetailPage-DQa_Zvfe.js.map +0 -1
- package/dist/CustomerSupportTicketList-CMPRQ_7O.js +0 -27
- package/dist/CustomerSupportTicketSuccess-CnRWm6gX.js +0 -27
- package/dist/SavedFiltersPage-BNasEKOY.js.map +0 -1
- package/dist/StaffCreateSupportTicketForm-DoHCw60c.js +0 -27
- package/dist/StaffSupportTicketDetailPage-D49ibqrO.js.map +0 -1
- package/dist/StaffSupportTicketList-BgCIa_9v.js +0 -27
- package/dist/StaffSupportTicketSuccess-DZF2WpZc.js +0 -27
- package/dist/TimelineNoteInput-BRsQ2QTz.js.map +0 -1
- package/dist/TimelineSystemEvent-B69B3eeL.js.map +0 -1
- package/dist/src-ChwBeNHB.js.map +0 -1
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import "./useRpcAuth-Dp2sec-X.js";
|
|
2
|
-
import "./useQueryCache-DqcDMsxb.js";
|
|
3
|
-
import "./useMutation-B4_S4Xoa.js";
|
|
4
|
-
import "./useQuery-B7ndu5_P.js";
|
|
5
|
-
import { I as StaffSupportTicketList_default } from "./src-ChwBeNHB.js";
|
|
6
|
-
import "./AppLink-CHMMrSFI.js";
|
|
7
|
-
import "./TimelineSystemEvent-B69B3eeL.js";
|
|
8
|
-
import "./TeamMembersTab-4gmnP9sD.js";
|
|
9
|
-
import "./Appearance-BfPdKMXw.js";
|
|
10
|
-
import "./useBreadcrumbs-DmgSucoe.js";
|
|
11
|
-
import "./EditTeamMemberForm-CKbKomrL.js";
|
|
12
|
-
import "./TeamHistoryTab-CNelXR3Q.js";
|
|
13
|
-
import "./UserProfilePage-uAIfC_NW.js";
|
|
14
|
-
import "./ChangePasswordPage-DCews8GU.js";
|
|
15
|
-
import "./TeamNotesTab-BhVRLG8h.js";
|
|
16
|
-
import "./CustomerSupportTicketParent-sT8hpgrA.js";
|
|
17
|
-
import "./SupportTicketDevLifecycleBadge-Cl4y47Sy.js";
|
|
18
|
-
import "./StaffSupportTicketParent-CxrPxXSH.js";
|
|
19
|
-
import "./LoginForm-C85U2E2r.js";
|
|
20
|
-
import "./Signup-hpV8J5cM.js";
|
|
21
|
-
import "./ForgotPassword-D3bjL48L.js";
|
|
22
|
-
import "./Logout-DZuWLh0O.js";
|
|
23
|
-
import "./UserListPage-OGYOLwlw.js";
|
|
24
|
-
import "./CreateUserPage-B8qeBZij.js";
|
|
25
|
-
import "./EditUserPage-XqF25iwz.js";
|
|
26
|
-
|
|
27
|
-
export { StaffSupportTicketList_default as default };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import "./useRpcAuth-Dp2sec-X.js";
|
|
2
|
-
import "./useQueryCache-DqcDMsxb.js";
|
|
3
|
-
import "./useMutation-B4_S4Xoa.js";
|
|
4
|
-
import "./useQuery-B7ndu5_P.js";
|
|
5
|
-
import { M as StaffSupportTicketSuccess_default } from "./src-ChwBeNHB.js";
|
|
6
|
-
import "./AppLink-CHMMrSFI.js";
|
|
7
|
-
import "./TimelineSystemEvent-B69B3eeL.js";
|
|
8
|
-
import "./TeamMembersTab-4gmnP9sD.js";
|
|
9
|
-
import "./Appearance-BfPdKMXw.js";
|
|
10
|
-
import "./useBreadcrumbs-DmgSucoe.js";
|
|
11
|
-
import "./EditTeamMemberForm-CKbKomrL.js";
|
|
12
|
-
import "./TeamHistoryTab-CNelXR3Q.js";
|
|
13
|
-
import "./UserProfilePage-uAIfC_NW.js";
|
|
14
|
-
import "./ChangePasswordPage-DCews8GU.js";
|
|
15
|
-
import "./TeamNotesTab-BhVRLG8h.js";
|
|
16
|
-
import "./CustomerSupportTicketParent-sT8hpgrA.js";
|
|
17
|
-
import "./SupportTicketDevLifecycleBadge-Cl4y47Sy.js";
|
|
18
|
-
import "./StaffSupportTicketParent-CxrPxXSH.js";
|
|
19
|
-
import "./LoginForm-C85U2E2r.js";
|
|
20
|
-
import "./Signup-hpV8J5cM.js";
|
|
21
|
-
import "./ForgotPassword-D3bjL48L.js";
|
|
22
|
-
import "./Logout-DZuWLh0O.js";
|
|
23
|
-
import "./UserListPage-OGYOLwlw.js";
|
|
24
|
-
import "./CreateUserPage-B8qeBZij.js";
|
|
25
|
-
import "./EditUserPage-XqF25iwz.js";
|
|
26
|
-
|
|
27
|
-
export { StaffSupportTicketSuccess_default as default };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineNoteInput-BRsQ2QTz.js","names":["events: SystemEvent[]","map: Record<AlertVariant, string>","copyTimeoutId: ReturnType<typeof setTimeout> | null"],"sources":["../src/slices/support_ticket/shared/SupportTicketAttachmentsCollapsible.vue","../src/slices/support_ticket/utils/parseRecordVersions.ts","../src/slices/support_ticket/shared/ActionBannerAlert.vue","../src/slices/support_ticket/shared/MetadataField.vue","../src/slices/support_ticket/shared/timelineNoteFormMetadata.ts","../src/slices/support_ticket/shared/TimelineNoteInput.vue"],"sourcesContent":["<template>\n <div v-if=\"recordId\" class=\"w-full\">\n <div class=\"collapse collapse-arrow bg-base-200\">\n <input type=\"checkbox\" v-model=\"attachmentsExpanded\" />\n <div class=\"collapse-title text-sm font-medium px-4 py-2 min-h-0\">\n <div class=\"flex items-center gap-2\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13\"\n />\n </svg>\n <span>Attachments ({{ attachmentsCount }})</span>\n </div>\n </div>\n <div class=\"collapse-content px-0\">\n <div class=\"px-4 pb-4\">\n <InlineAttachments\n :record-id=\"recordId\"\n :can-upload=\"editable && !locked\"\n :can-delete=\"editable && !locked\"\n @update:attachments-count=\"attachmentsCount = $event\"\n @uploaded=\"emit('uploaded')\"\n @deleted=\"emit('deleted')\"\n />\n </div>\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref } from 'vue';\nimport InlineAttachments from './InlineAttachments.vue';\n\nconst attachmentsCount = ref(0);\n\ninterface Props {\n recordId: string;\n locked: boolean;\n editable: boolean;\n}\n\ndefineProps<Props>();\n\nconst emit = defineEmits<{ uploaded: []; deleted: [] }>();\n\nconst attachmentsExpanded = ref(false);\n</script>\n","import {\n supportTicketNumberToPriority,\n type RecordVersionReadDto,\n} from '@dragonmastery/dragoncore-shared';\nimport { formatTicketDate } from './formatTicketDate';\n\nexport interface SystemEvent {\n id: string;\n timestamp: string;\n author: string;\n message: string;\n oldValue?: string | null;\n newValue?: string | null;\n fieldName?: string; // Field name that changed (for filtering)\n}\n\nconst SKIP_FIELDS = new Set(['updated_at', 'updated_by', 'id', 'display_id']);\n\n/** Fields that store user IDs - display names from lookup instead of raw IDs */\nconst USER_ID_FIELDS = new Set([\n 'assigned_to',\n 'created_by',\n 'updated_by',\n 'archived_by',\n 'deleted_by',\n]);\n\nfunction truncate(value: string, maxLength: number = 50): string {\n if (value.length <= maxLength) return value;\n return value.substring(0, maxLength) + '...';\n}\n\nfunction formatEnumValue(value: string): string {\n return value\n .split('_')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())\n .join(' ');\n}\n\nfunction formatFieldValue(\n fieldName: string,\n value: unknown,\n truncateLong: boolean = false,\n displayMap?: Map<string, string>,\n): string {\n if (value === null || value === undefined) return '';\n if (typeof value === 'string' && /^\\d{4}-\\d{2}-\\d{2}/.test(value)) {\n return formatTicketDate(value).formatted;\n }\n if (typeof value === 'boolean') return value ? 'Yes' : 'No';\n // User ID fields: display name from lookup when available\n if (typeof value === 'string' && USER_ID_FIELDS.has(fieldName) && displayMap) {\n return displayMap.get(value) ?? value;\n }\n // Priority: DB stores number (1-4), record_versions may have legacy string or new number\n if (fieldName === 'priority') {\n if (typeof value === 'number') {\n return formatEnumValue(supportTicketNumberToPriority(value));\n }\n if (typeof value === 'string') return formatEnumValue(value);\n }\n if (typeof value === 'string') {\n if (\n fieldName.includes('status') ||\n fieldName.includes('type') ||\n fieldName.includes('lifecycle')\n ) {\n return formatEnumValue(value);\n }\n // Truncate title and description\n if (truncateLong && (fieldName === 'title' || fieldName === 'description')) {\n return truncate(value);\n }\n }\n return String(value);\n}\n\nfunction generateActionMessage(fieldName: string, oldValue: unknown, newValue: unknown): string {\n switch (fieldName) {\n case 'assigned_to':\n if (oldValue === null || oldValue === undefined) return 'assigned to';\n return 'reassigned to';\n case 'title':\n return 'changed the title';\n case 'description':\n return 'updated the description';\n case 'type':\n return 'changed type';\n case 'priority':\n return 'changed priority';\n case 'approval_status':\n return 'changed status';\n case 'dev_lifecycle':\n return 'moved';\n case 'credit_value':\n if (oldValue === null || oldValue === undefined) return 'set credits';\n return 'changed credits';\n case 'delivered_value':\n if (oldValue === null || oldValue === undefined) return 'set delivered value';\n return 'changed delivered value';\n case 'start_at':\n if (oldValue === null || oldValue === undefined) return 'set start date';\n if (newValue === null || newValue === undefined) return 'cleared start date';\n return 'changed start date';\n case 'target_at':\n if (oldValue === null || oldValue === undefined) return 'set target date';\n if (newValue === null || newValue === undefined) return 'cleared target date';\n return 'changed target date';\n case 'completed_at':\n if (oldValue === null || oldValue === undefined) return 'marked as completed';\n if (newValue === null || newValue === undefined) return 'cleared completed date';\n return 'changed completed date';\n case 'locked_approval_at':\n if (\n oldValue === null ||\n (oldValue === undefined && newValue !== null && newValue !== undefined)\n ) {\n return 'locked the ticket';\n }\n return 'changed lock status';\n case 'archived_at':\n if (\n oldValue === null ||\n (oldValue === undefined && newValue !== null && newValue !== undefined)\n ) {\n return 'archived the ticket';\n }\n if (\n oldValue !== null &&\n oldValue !== undefined &&\n (newValue === null || newValue === undefined)\n ) {\n return 'unarchived the ticket';\n }\n return 'changed archive status';\n case 'requester_email':\n return 'changed requester email';\n case 'requester_name':\n return 'changed requester name';\n case 'attachment_added':\n return 'added attachment';\n case 'attachment_removed':\n return 'removed attachment';\n default:\n return `updated ${fieldName.replace(/_/g, ' ')}`;\n }\n}\n\nfunction parseRecord(recordJson: string | null | undefined): Record<string, unknown> | null {\n if (!recordJson) return null;\n try {\n return JSON.parse(recordJson);\n } catch {\n return null;\n }\n}\n\nexport function parseRecordVersions(\n versions: RecordVersionReadDto[],\n displayMap?: Map<string, string>,\n): SystemEvent[] {\n const events: SystemEvent[] = [];\n\n for (const version of versions) {\n const author = version.auth_username || 'Unknown';\n const timestamp = version.recorded_at;\n\n if (version.operation === 'insert') {\n events.push({\n id: `${version.id}-insert`,\n timestamp,\n author,\n message: 'created this ticket',\n });\n continue;\n }\n\n if (version.operation === 'update') {\n const oldRecord = parseRecord(version.old_record);\n const newRecord = parseRecord(version.record);\n\n if (!oldRecord || !newRecord) continue;\n\n for (const [fieldName, newValue] of Object.entries(newRecord)) {\n if (SKIP_FIELDS.has(fieldName)) continue;\n\n const oldValue = oldRecord[fieldName];\n\n if (oldValue !== newValue) {\n const oldFormatted =\n oldValue !== null && oldValue !== undefined\n ? formatFieldValue(fieldName, oldValue, true, displayMap)\n : null;\n const newFormatted =\n newValue !== null && newValue !== undefined\n ? formatFieldValue(fieldName, newValue, true, displayMap)\n : null;\n\n events.push({\n id: `${version.id}-${fieldName}`,\n timestamp,\n author,\n message: generateActionMessage(fieldName, oldValue, newValue),\n oldValue: oldFormatted || null,\n newValue: newFormatted || null,\n fieldName,\n });\n }\n }\n }\n }\n\n events.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());\n return events;\n}\n","<template>\n <div\n role=\"alert\"\n :class=\"[\n 'py-3 px-4 w-full rounded-lg border border-base-300 border-l-4 bg-base-200/60',\n variantBorderClass,\n ]\"\n >\n <!-- Mobile-first: stack vertically on small screens, row on desktop -->\n <div\n class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:gap-3\"\n >\n <div class=\"flex items-start gap-3 min-w-0\">\n <component :is=\"iconComponent\" v-if=\"icon\" class=\"shrink-0 w-5 h-5 opacity-90 mt-0.5\" />\n <span class=\"flex-1 min-w-0\">\n <slot />\n </span>\n </div>\n <div\n v-if=\"hasActions\"\n class=\"grid grid-cols-2 gap-2 sm:flex sm:flex-row sm:flex-wrap sm:ml-auto sm:shrink-0 sm:[&>*]:w-auto\"\n >\n <slot name=\"actions\" />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, useSlots, h } from 'vue';\n\ntype AlertVariant = 'warning' | 'success' | 'error' | 'info' | 'neutral';\ntype IconType = 'lock' | 'clock' | 'info' | 'archive';\n\ninterface Props {\n variant: AlertVariant;\n icon?: IconType;\n}\n\nconst props = defineProps<Props>();\n\nconst slots = useSlots();\n\nconst variantBorderClass = computed(() => {\n const map: Record<AlertVariant, string> = {\n warning: 'border-l-warning',\n success: 'border-l-success',\n error: 'border-l-error',\n info: 'border-l-info',\n neutral: 'border-l-base-content/30',\n };\n return map[props.variant];\n});\n\nconst hasActions = computed(() => {\n const actionsSlot = slots.actions;\n if (!actionsSlot) return false;\n const vnodes = actionsSlot();\n return !!vnodes?.length;\n});\n\nconst iconComponent = computed(() => {\n if (!props.icon) return null;\n const icons = {\n lock: () =>\n h(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n fill: 'none',\n viewBox: '0 0 24 24',\n 'stroke-width': '1.5',\n stroke: 'currentColor',\n class: 'w-5 h-5',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n d: 'M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z',\n }),\n ],\n ),\n clock: () =>\n h(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n fill: 'none',\n viewBox: '0 0 24 24',\n 'stroke-width': '1.5',\n stroke: 'currentColor',\n class: 'w-5 h-5',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n d: 'M12 6v6h4.5m4.5 0a9 9 0 11-18 0 9 9 0 0118 0z',\n }),\n ],\n ),\n info: () =>\n h(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n fill: 'none',\n viewBox: '0 0 24 24',\n 'stroke-width': '1.5',\n stroke: 'currentColor',\n class: 'w-5 h-5',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n d: 'M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z',\n }),\n ],\n ),\n archive: () =>\n h(\n 'svg',\n {\n xmlns: 'http://www.w3.org/2000/svg',\n fill: 'none',\n viewBox: '0 0 24 24',\n 'stroke-width': '1.5',\n stroke: 'currentColor',\n class: 'w-5 h-5',\n },\n [\n h('path', {\n 'stroke-linecap': 'round',\n 'stroke-linejoin': 'round',\n d: 'M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z',\n }),\n ],\n ),\n };\n return icons[props.icon] ?? null;\n});\n</script>\n","<template>\n <div class=\"flex flex-col\">\n <span class=\"text-xs font-semibold uppercase tracking-wide text-base-content/50 mb-0.5\">\n {{ label }}\n </span>\n <template v-if=\"hasContent\">\n <span\n v-if=\"copyable\"\n ref=\"valueRef\"\n role=\"button\"\n tabindex=\"0\"\n class=\"font-mono cursor-pointer select-all\"\n @click=\"handleCopy\"\n @keydown.enter=\"handleCopy\"\n @keydown.space.prevent=\"handleCopy\"\n >\n {{ copied ? 'Copied!' : undefined }}\n <template v-if=\"!copied\"><slot /></template>\n </span>\n <span v-else>\n <slot />\n </span>\n </template>\n <template v-else>\n <slot name=\"empty\">\n <span class=\"text-base-content/50 italic text-sm\">Not set</span>\n </slot>\n </template>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { Comment } from 'vue';\nimport { ref, computed, useSlots } from 'vue';\n\ninterface Props {\n label: string;\n copyable?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n copyable: false,\n});\n\nconst slots = useSlots();\nconst valueRef = ref<HTMLSpanElement | null>(null);\nconst copied = ref(false);\nlet copyTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\nconst hasContent = computed(() => {\n const defaultSlot = slots.default;\n if (!defaultSlot) return false;\n const vnodes = defaultSlot();\n if (!vnodes || !Array.isArray(vnodes)) return false;\n return vnodes.some((v) => v.type !== Comment);\n});\n\nasync function handleCopy() {\n if (!props.copyable) return;\n const text = valueRef.value?.textContent?.trim();\n if (!text) return;\n try {\n await navigator.clipboard.writeText(text);\n copied.value = true;\n if (copyTimeoutId) clearTimeout(copyTimeoutId);\n copyTimeoutId = setTimeout(() => {\n copied.value = false;\n copyTimeoutId = null;\n }, 1500);\n } catch {\n // ignore clipboard errors\n }\n}\n</script>\n","import { withMetadata } from '@dragonmastery/zinia-forms-core';\nimport { z } from 'zod';\n\n/** Schema for adding a comment/note on support ticket timeline. */\nconst TimelineNoteCreateSchema = z.object({\n body: z.string().min(1, 'Comment is required').trim(),\n is_internal: z.boolean().optional().default(false),\n});\n\nexport const timelineNoteCreateMetadata = withMetadata(\n TimelineNoteCreateSchema,\n 'timelineNoteCreateForm',\n {\n body: {\n label: 'Comment',\n placeholder: 'Add a comment...',\n inputType: 'textarea',\n },\n is_internal: {\n label: 'Internal Note',\n helpText: 'Internal notes are not visible to the customer',\n },\n },\n);\n\nexport type TimelineNoteCreateDto = z.infer<typeof TimelineNoteCreateSchema>;\n","<template>\n <div class=\"border-t border-base-200 pt-4 mt-4 w-full\">\n <template v-if=\"disabled\">\n <div class=\"bg-base-200/50 rounded-lg p-4 text-center\">\n <span class=\"text-base-content/50 text-sm inline-flex items-center gap-1.5\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"w-4 h-4\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z\"\n />\n </svg>\n This ticket has been locked. You can no longer add comments.\n </span>\n </div>\n </template>\n <ZiniaForm\n v-else\n @handle-submit=\"handleSubmit\"\n @success=\"handleSuccess\"\n @error=\"handleError\"\n title=\"\"\n subtitle=\"\"\n >\n <div ref=\"formWrapperRef\" class=\"grid grid-cols-1 gap-4 w-full\" @keydown=\"handleKeydown\">\n <p\n v-if=\"showTypeToggle\"\n class=\"col-span-full text-xs mb-1\"\n :class=\"form.values.is_internal ? 'text-warning' : 'text-info'\"\n >\n {{\n form.values.is_internal\n ? '🔒 Internal — not visible to customer'\n : 'Visible to customer'\n }}\n </p>\n <div class=\"col-span-full\">\n <zinia.BodyField class=\"textarea w-full\" />\n </div>\n <div class=\"col-span-full flex flex-wrap gap-2 justify-between items-center\">\n <div v-if=\"showTypeToggle\" class=\"join\">\n <button\n type=\"button\"\n :class=\"[\n 'btn btn-sm join-item',\n !form.values.is_internal ? 'btn-info btn-active' : '',\n ]\"\n :disabled=\"form.isSubmitting\"\n @click=\"form.setValue('is_internal', false)\"\n >\n Customer Note\n </button>\n <button\n type=\"button\"\n :class=\"[\n 'btn btn-sm join-item',\n form.values.is_internal ? 'btn-warning btn-active' : '',\n ]\"\n :disabled=\"form.isSubmitting\"\n @click=\"form.setValue('is_internal', true)\"\n >\n Internal Note\n </button>\n </div>\n <div v-else></div>\n <ZiniaSubmitButton submitText=\"Comment\" submittingText=\"Adding...\" />\n </div>\n </div>\n </ZiniaForm>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { useForm } from '@dragonmastery/zinia-forms-core';\nimport { ref, watch } from 'vue';\nimport { timelineNoteCreateMetadata } from './timelineNoteFormMetadata';\n\ninterface Props {\n showTypeToggle: boolean;\n disabled: boolean;\n /** When provided, noteType is controlled (e.g. synced with URL query param) */\n noteType?: 'customer' | 'internal';\n /** Called when form submits with validated data. Must return a Promise. */\n onSubmit: (payload: { content: string; noteType: 'customer' | 'internal' }) => Promise<void>;\n}\n\nconst props = defineProps<Props>();\nconst formWrapperRef = ref<HTMLElement | null>(null);\n\nconst emit = defineEmits<{\n 'update:noteType': [value: 'customer' | 'internal'];\n}>();\n\nconst { form, zinia, ZiniaForm, ZiniaSubmitButton, ZiniaFormErrorsSummary, refreshFormData } =\n useForm(timelineNoteCreateMetadata, {\n storeName: 'timeline-note-input',\n persistToLocalStorage: false,\n renderStyle: 'daisy_ui',\n fetchData: async () => ({\n body: '',\n is_internal: props.noteType === 'internal',\n }),\n });\n\n// Sync form's is_internal when controlled noteType prop changes (e.g. from URL)\nwatch(\n () => props.noteType,\n (noteType) => {\n if (noteType !== undefined) {\n form.setValue('is_internal', noteType === 'internal');\n }\n },\n { immediate: true },\n);\n\n// Emit when user toggles note type (for URL sync in StaffTimeline)\nwatch(\n () => form.values.is_internal,\n (isInternal) => {\n if (props.showTypeToggle) {\n emit('update:noteType', isInternal ? 'internal' : 'customer');\n }\n },\n);\n\nfunction handleKeydown(e: KeyboardEvent) {\n if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {\n e.preventDefault();\n const form = formWrapperRef.value?.closest('form');\n form?.requestSubmit();\n }\n}\n\nasync function handleSubmit(formData: { body: string; is_internal: boolean }) {\n await props.onSubmit({\n content: (formData.body ?? '').trim(),\n noteType: props.showTypeToggle && formData.is_internal ? 'internal' : 'customer',\n });\n}\n\nfunction handleSuccess() {\n refreshFormData();\n (document.activeElement as HTMLElement)?.blur();\n}\n\nfunction handleError(_error: Error | unknown) {\n // Form displays errors via ZiniaFormErrorsSummary\n}\n\nfunction reset() {\n refreshFormData();\n}\n\ndefineExpose({ reset });\n</script>\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"TimelineSystemEvent-B69B3eeL.js","names":["priorityConfig: Record<SupportTicketPriority, PriorityBadgeConfig>","typeConfig: Record<SupportTicketType, TypeBadgeConfig>","files: Attachment[]","queueItem: QueueItem","imageFiles: File[]","approvalConfig: Record<SupportTicketApproval, ApprovalBadgeConfig>","relative: string"],"sources":["../src/components/ConfirmDialog.vue","../src/components/ImageModal.vue","../src/slices/support_ticket/shared/SupportTicketPriorityBadge.vue","../src/slices/support_ticket/shared/SupportTicketTypeBadge.vue","../src/slices/support_ticket/utils/creditValueFormatter.ts","../src/slices/support_ticket/shared/InlineAttachments.vue","../src/slices/support_ticket/shared/SupportTicketApprovalBadge.vue","../src/slices/support_ticket/utils/formatTicketDate.ts","../src/slices/support_ticket/shared/TimelineItem.vue","../src/slices/support_ticket/shared/TimelineSystemEvent.vue"],"sourcesContent":["<template>\n <dialog\n ref=\"dialogRef\"\n :class=\"['modal', { 'modal-open': isOpen }]\"\n @click=\"handleBackdropClick\"\n >\n <div class=\"modal-box\" @click.stop>\n <h3 class=\"font-bold text-lg mb-4\">{{ title }}</h3>\n <div class=\"py-4\">\n <slot name=\"message\">\n <p>{{ message }}</p>\n </slot>\n </div>\n <div class=\"modal-action\">\n <button\n class=\"btn btn-outline\"\n @click.prevent=\"handleCancel\"\n :disabled=\"isProcessing\"\n type=\"button\"\n >\n {{ cancelText }}\n </button>\n <button\n class=\"btn\"\n :class=\"confirmButtonClass\"\n @click.prevent=\"handleConfirm\"\n :disabled=\"isProcessing\"\n type=\"button\"\n >\n {{ isProcessing ? processingText : confirmText }}\n </button>\n </div>\n </div>\n </dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from 'vue';\n\ninterface Props {\n modelValue: boolean;\n title?: string;\n message?: string;\n confirmText?: string;\n cancelText?: string;\n processingText?: string;\n confirmButtonClass?: string;\n isProcessing?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n title: 'Confirm',\n message: 'Are you sure?',\n confirmText: 'Confirm',\n cancelText: 'Cancel',\n processingText: 'Processing...',\n confirmButtonClass: 'btn-primary',\n isProcessing: false,\n});\n\nconst emit = defineEmits<{\n 'update:modelValue': [value: boolean];\n confirm: [];\n cancel: [];\n}>();\n\nconst dialogRef = ref<HTMLDialogElement | null>(null);\nconst isOpen = ref(props.modelValue);\n\nwatch(\n () => props.modelValue,\n (newValue) => {\n isOpen.value = newValue;\n if (newValue && dialogRef.value) {\n dialogRef.value.showModal();\n } else if (dialogRef.value) {\n dialogRef.value.close();\n }\n },\n { immediate: true },\n);\n\nwatch(isOpen, (newValue) => {\n if (newValue && dialogRef.value) {\n dialogRef.value.showModal();\n } else if (dialogRef.value) {\n dialogRef.value.close();\n }\n});\n\nconst handleConfirm = () => {\n emit('confirm');\n};\n\nconst handleCancel = () => {\n isOpen.value = false;\n emit('update:modelValue', false);\n emit('cancel');\n};\n\nconst handleBackdropClick = (event: MouseEvent) => {\n // Close when clicking the backdrop (modal itself)\n if (event.target === dialogRef.value) {\n handleCancel();\n }\n};\n</script>\n","<template>\n <dialog\n ref=\"dialogRef\"\n class=\"modal\"\n :class=\"{ 'modal-open': isOpen }\"\n @click=\"handleBackdropClick\"\n @keydown=\"handleKeydown\"\n >\n <div class=\"modal-box w-full max-w-full h-full max-h-full p-0 flex flex-col bg-base-100\">\n <!-- Header -->\n <div\n class=\"flex items-center justify-between p-3 sm:p-4 border-b border-base-300 flex-shrink-0 bg-base-100/95 backdrop-blur\"\n >\n <div class=\"flex-1 min-w-0 mr-2\">\n <h3 class=\"font-semibold text-sm sm:text-base truncate\">\n {{ imageName }}\n </h3>\n <p\n v-if=\"imageIndex !== null && totalImages !== null\"\n class=\"text-xs text-base-content/60 mt-0.5\"\n >\n {{ imageIndex + 1 }} / {{ totalImages }}\n </p>\n </div>\n <div class=\"flex items-center gap-1 sm:gap-2 flex-shrink-0\">\n <!-- Navigation buttons (if multiple images) -->\n <button\n v-if=\"showNavigation && imageIndex !== null && imageIndex > 0\"\n @click.prevent=\"goToPrevious\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n title=\"Previous image\"\n aria-label=\"Previous image\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <button\n v-if=\"showNavigation && imageIndex !== null && imageIndex < (totalImages || 0) - 1\"\n @click.prevent=\"goToNext\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n title=\"Next image\"\n aria-label=\"Next image\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n </button>\n <!-- Download button -->\n <button\n v-if=\"props.onDownload\"\n @click.prevent=\"handleDownload\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n title=\"Download\"\n aria-label=\"Download image\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n </button>\n <!-- Close button -->\n <button\n @click.prevent=\"handleClose\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n title=\"Close\"\n aria-label=\"Close modal\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n <!-- Image container -->\n <div\n ref=\"imageContainerRef\"\n class=\"flex-1 flex items-center justify-center overflow-hidden bg-base-200 relative\"\n >\n <!-- Loading state -->\n <div v-if=\"isLoading\" class=\"absolute inset-0 flex items-center justify-center\">\n <span class=\"loading loading-spinner loading-lg\"></span>\n </div>\n\n <!-- Error state -->\n <div\n v-if=\"hasError\"\n class=\"absolute inset-0 flex flex-col items-center justify-center p-4 text-center\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-12 w-12 text-error mb-2\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n <p class=\"text-sm text-base-content/70\">Failed to load image</p>\n </div>\n\n <!-- Image -->\n <img\n v-if=\"imageSrc && !hasError\"\n ref=\"imageRef\"\n :src=\"imageSrc\"\n :alt=\"imageName\"\n class=\"max-w-full max-h-full object-contain\"\n :class=\"{\n 'transition-transform duration-300 ease-out':\n !isDragging && !isPinching && !isResetting,\n }\"\n :style=\"imageStyle\"\n @load=\"handleImageLoad\"\n @error=\"handleImageError\"\n @dblclick=\"resetZoom\"\n @touchstart.stop=\"handleImageTouchStart\"\n @touchmove.stop=\"handleImageTouchMove\"\n @touchend.stop=\"handleImageTouchEnd\"\n />\n\n <!-- Zoom controls -->\n <div\n v-if=\"imageSrc && !hasError\"\n class=\"zoom-controls absolute bottom-4 left-1/2 transform -translate-x-1/2 flex gap-1 sm:gap-2 bg-base-100/90 backdrop-blur rounded-lg p-1.5 sm:p-2 shadow-lg z-10\"\n @touchstart.stop\n @touchmove.stop\n @touchend.stop\n @click.stop\n >\n <button\n @click.prevent=\"zoomOut\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n :disabled=\"scale <= minScale\"\n title=\"Zoom out\"\n aria-label=\"Zoom out\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7\"\n />\n </svg>\n </button>\n <button\n @click.prevent=\"resetZoom\"\n class=\"text-xs flex items-center px-2 text-base-content/70 hover:text-base-content hover:bg-base-200 rounded transition-colors cursor-pointer\"\n type=\"button\"\n title=\"Reset zoom to 100%\"\n aria-label=\"Reset zoom to 100%\"\n >\n {{ Math.round(scale * 100) }}%\n </button>\n <button\n @click.prevent=\"zoomIn\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n :disabled=\"scale >= maxScale\"\n title=\"Zoom in\"\n aria-label=\"Zoom in\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7\"\n />\n </svg>\n </button>\n <button\n @click.prevent=\"rotateImage\"\n class=\"btn btn-sm btn-circle btn-ghost\"\n type=\"button\"\n title=\"Rotate image\"\n aria-label=\"Rotate image\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n aria-hidden=\"true\"\n data-slot=\"icon\"\n fill=\"none\"\n class=\"size-6\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M18.363 5.634A8.997 9.002 29.494 0 0 7.5 4.206 8.997 9.002 29.494 0 0 3.306 14.33 8.997 9.002 29.494 0 0 11.996 21a8.997 9.002 29.494 0 0 8.694-6.673m-2.327-8.693L20.87 8.14m.017-4.994v5.015m0 0h-5.013\"\n ></path>\n </svg>\n </button>\n </div>\n </div>\n </div>\n <form method=\"dialog\" class=\"modal-backdrop\">\n <button type=\"button\" @click.prevent=\"handleClose\">close</button>\n </form>\n </dialog>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';\n\ninterface Props {\n isOpen: boolean;\n imageSrc?: string;\n imageName?: string;\n imageIndex?: number | null;\n totalImages?: number | null;\n onDownload?: () => void;\n onPrevious?: () => void;\n onNext?: () => void;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n imageSrc: '',\n imageName: '',\n imageIndex: null,\n totalImages: null,\n onDownload: undefined,\n onPrevious: undefined,\n onNext: undefined,\n});\n\nconst emit = defineEmits<{\n close: [];\n}>();\n\nconst dialogRef = ref<HTMLDialogElement | null>(null);\nconst imageRef = ref<HTMLImageElement | null>(null);\nconst imageContainerRef = ref<HTMLDivElement | null>(null);\nconst isOpen = ref(props.isOpen);\nconst isLoading = ref(false);\nconst hasError = ref(false);\n\n// Zoom and pan state\nconst scale = ref(1);\nconst translateX = ref(0);\nconst translateY = ref(0);\nconst rotation = ref(0); // Rotation in degrees (can go beyond 360 for smooth transitions)\nconst minScale = 0.5;\nconst maxScale = 5;\nconst zoomStep = 0.05; // Flat 5% increments (0.05 = 5%)\n\n// Animation state\nconst isDragging = ref(false);\nconst isPinching = ref(false);\nconst isResetting = ref(false); // Track when resetting to disable transition\n\n// Touch state for pinch zoom and pan\nconst touchState = ref<{\n type: 'pinch' | 'pan';\n initialDistance?: number;\n initialScale?: number;\n initialTranslateX?: number;\n initialTranslateY?: number;\n initialTouches?: Touch[];\n lastPanX?: number;\n lastPanY?: number;\n} | null>(null);\n\n// Mouse drag state for panning\nconst mouseDragState = ref<{\n isDragging: boolean;\n startX: number;\n startY: number;\n startTranslateX: number;\n startTranslateY: number;\n} | null>(null);\n\n// Mobile detection\nconst isMobile = ref(false);\n\nconst imageStyle = computed(() => ({\n transform: `scale(${scale.value}) translate(${translateX.value}px, ${translateY.value}px) rotate(${rotation.value}deg)`,\n cursor: mouseDragState.value?.isDragging ? 'grabbing' : scale.value > 1 ? 'grab' : 'default',\n touchAction: 'none' as const, // Prevent default touch behaviors\n userSelect: 'none' as const, // Prevent text selection during drag\n willChange: isDragging.value || isPinching.value ? 'transform' : 'auto', // Optimize for animations\n}));\n\nconst showNavigation = computed(() => {\n return (\n props.totalImages !== null && props.totalImages > 1 && (props.onPrevious || props.onNext)\n );\n});\n\n// Watch for modal open/close\nwatch(\n () => props.isOpen,\n (newValue) => {\n isOpen.value = newValue;\n if (newValue && dialogRef.value) {\n dialogRef.value.showModal();\n // Reset zoom and rotation when opening\n isResetting.value = true;\n scale.value = 1;\n translateX.value = 0;\n translateY.value = 0;\n rotation.value = 0;\n touchState.value = null;\n mouseDragState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n isLoading.value = true;\n hasError.value = false;\n // Re-enable transitions after modal opens\n nextTick(() => {\n setTimeout(() => {\n isResetting.value = false;\n }, 50);\n });\n\n // Add event listeners with passive: false only when modal is open\n nextTick(() => {\n if (imageContainerRef.value) {\n imageContainerRef.value.addEventListener('wheel', handleWheel, {\n passive: false,\n });\n imageContainerRef.value.addEventListener('mousedown', handleMouseDown);\n // Container-level touch handlers (for areas outside the image)\n imageContainerRef.value.addEventListener('touchstart', handleTouchStart, {\n passive: false,\n });\n imageContainerRef.value.addEventListener('touchmove', handleTouchMove, {\n passive: false,\n });\n imageContainerRef.value.addEventListener('touchend', handleTouchEnd, {\n passive: true,\n });\n imageContainerRef.value.addEventListener('touchcancel', handleTouchEnd, {\n passive: true,\n });\n }\n // Add global mouse listeners for drag\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('mouseup', handleMouseUp);\n });\n } else if (dialogRef.value) {\n dialogRef.value.close();\n\n // Remove event listeners when modal closes\n if (imageContainerRef.value) {\n imageContainerRef.value.removeEventListener('wheel', handleWheel);\n imageContainerRef.value.removeEventListener('mousedown', handleMouseDown);\n imageContainerRef.value.removeEventListener('touchstart', handleTouchStart);\n imageContainerRef.value.removeEventListener('touchmove', handleTouchMove);\n imageContainerRef.value.removeEventListener('touchend', handleTouchEnd);\n imageContainerRef.value.removeEventListener('touchcancel', handleTouchEnd);\n }\n // Remove global mouse listeners\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n // Clear states when modal closes\n touchState.value = null;\n mouseDragState.value = null;\n }\n },\n { immediate: true },\n);\n\n// Watch for image source changes\nwatch(\n () => props.imageSrc,\n () => {\n if (props.isOpen && props.imageSrc) {\n isLoading.value = true;\n hasError.value = false;\n // Reset zoom and rotation when image changes\n scale.value = 1;\n translateX.value = 0;\n translateY.value = 0;\n rotation.value = 0;\n }\n },\n);\n\n// Watch for image index changes (navigation)\nwatch(\n () => props.imageIndex,\n () => {\n if (props.isOpen) {\n resetZoom();\n isLoading.value = true;\n hasError.value = false;\n }\n },\n);\n\n// Mobile detection\nonMounted(() => {\n isMobile.value = window.innerWidth < 768;\n window.addEventListener('resize', () => {\n isMobile.value = window.innerWidth < 768;\n });\n});\n\n// Keyboard navigation\nconst handleKeydown = (event: KeyboardEvent) => {\n if (!props.isOpen) return;\n\n switch (event.key) {\n case 'Escape':\n handleClose();\n break;\n case 'ArrowLeft':\n if (showNavigation.value && props.imageIndex !== null && props.imageIndex > 0) {\n event.preventDefault();\n goToPrevious();\n }\n break;\n case 'ArrowRight':\n if (\n showNavigation.value &&\n props.imageIndex !== null &&\n props.imageIndex < (props.totalImages || 0) - 1\n ) {\n event.preventDefault();\n goToNext();\n }\n break;\n case '+':\n case '=':\n event.preventDefault();\n zoomIn();\n break;\n case '-':\n event.preventDefault();\n zoomOut();\n break;\n case '0':\n event.preventDefault();\n resetZoom();\n break;\n }\n};\n\n// Zoom functions - using flat 5% increments\nconst zoomIn = () => {\n if (scale.value < maxScale) {\n const newScale = scale.value + zoomStep;\n scale.value = Math.min(maxScale, newScale);\n }\n};\n\nconst zoomOut = () => {\n if (scale.value > minScale) {\n const newScale = scale.value - zoomStep;\n scale.value = Math.max(minScale, newScale);\n // Reset pan if zoomed out to fit\n if (scale.value <= 1) {\n translateX.value = 0;\n translateY.value = 0;\n }\n }\n};\n\nconst resetZoom = () => {\n // Disable transitions to prevent counter-clockwise animation\n isResetting.value = true;\n\n scale.value = 1;\n translateX.value = 0;\n translateY.value = 0;\n rotation.value = 0;\n\n touchState.value = null;\n mouseDragState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n\n // Re-enable transitions after reset\n nextTick(() => {\n setTimeout(() => {\n isResetting.value = false;\n }, 50);\n });\n};\n\nconst rotateImage = () => {\n // Rotate 90 degrees clockwise\n // Don't use modulo - let it accumulate so we can control reset direction\n rotation.value = rotation.value + 90;\n};\n\n// Mouse wheel zoom - fixed to zoom towards cursor with smooth updates\nconst handleWheel = (event: WheelEvent) => {\n if (!imageRef.value || !props.isOpen) return;\n\n // Always prevent default to avoid page scrolling\n event.preventDefault();\n\n // Calculate zoom delta based on wheel direction\n // Use deltaY for vertical scroll, deltaX for horizontal (trackpad support)\n // Reverse sign: scroll down (positive delta) should zoom out, scroll up (negative delta) should zoom in\n const delta = event.deltaY !== 0 ? event.deltaY : event.deltaX;\n const scrollDirection = delta > 0 ? -1 : 1; // Negative for scroll down (zoom out), positive for scroll up (zoom in)\n\n const oldScale = scale.value;\n // Apply flat 5% increment based on scroll direction\n const newScale =\n scrollDirection > 0\n ? oldScale + zoomStep // Zoom in: add 0.05\n : oldScale - zoomStep; // Zoom out: subtract 0.05\n\n const clampedScale = Math.max(minScale, Math.min(maxScale, newScale));\n\n if (Math.abs(clampedScale - oldScale) > 0.001) {\n // Get the image container bounds\n const containerRect = imageContainerRef.value?.getBoundingClientRect();\n if (!containerRect) return;\n\n // Get mouse position relative to container center\n const mouseX = event.clientX - containerRect.left - containerRect.width / 2;\n const mouseY = event.clientY - containerRect.top - containerRect.height / 2;\n\n // Calculate the point on the image (in image coordinates) that the mouse is over\n // This accounts for current translation and scale\n const imagePointX = (mouseX - translateX.value) / oldScale;\n const imagePointY = (mouseY - translateY.value) / oldScale;\n\n // Apply new scale\n scale.value = clampedScale;\n\n // Adjust translation so the same image point stays under the mouse\n // After zoom, we want: mouseX = imagePointX * clampedScale + translateX\n translateX.value = mouseX - imagePointX * clampedScale;\n translateY.value = mouseY - imagePointY * clampedScale;\n\n // Reset pan if zoomed out to fit\n if (scale.value <= 1) {\n translateX.value = 0;\n translateY.value = 0;\n }\n }\n};\n\n// Mouse drag for panning\nconst handleMouseDown = (event: MouseEvent) => {\n if (!imageRef.value || !props.isOpen || scale.value <= 1) return;\n\n // Only start drag on left mouse button\n if (event.button !== 0) return;\n\n event.preventDefault();\n\n isDragging.value = true;\n mouseDragState.value = {\n isDragging: true,\n startX: event.clientX,\n startY: event.clientY,\n startTranslateX: translateX.value,\n startTranslateY: translateY.value,\n };\n\n // Change cursor to grabbing\n if (imageRef.value) {\n imageRef.value.style.cursor = 'grabbing';\n }\n};\n\nconst handleMouseMove = (event: MouseEvent) => {\n if (!mouseDragState.value || !mouseDragState.value.isDragging || !props.isOpen) return;\n\n event.preventDefault();\n\n // Use requestAnimationFrame for smoother updates\n requestAnimationFrame(() => {\n if (!mouseDragState.value) return;\n\n const deltaX = event.clientX - mouseDragState.value.startX;\n const deltaY = event.clientY - mouseDragState.value.startY;\n\n // Pan relative to current scale\n translateX.value = mouseDragState.value.startTranslateX + deltaX / scale.value;\n translateY.value = mouseDragState.value.startTranslateY + deltaY / scale.value;\n\n // Clamp translation to reasonable bounds\n const maxTranslate = 300;\n translateX.value = Math.max(-maxTranslate, Math.min(maxTranslate, translateX.value));\n translateY.value = Math.max(-maxTranslate, Math.min(maxTranslate, translateY.value));\n });\n};\n\nconst handleMouseUp = () => {\n if (mouseDragState.value) {\n mouseDragState.value.isDragging = false;\n }\n mouseDragState.value = null;\n isDragging.value = false;\n\n // Reset cursor\n if (imageRef.value) {\n imageRef.value.style.cursor = scale.value > 1 ? 'grab' : 'default';\n }\n};\n\n// Touch gestures - improved implementation\nconst getTouchDistance = (touches: Touch[]): number => {\n if (touches.length < 2) return 0;\n const touch1 = touches[0];\n const touch2 = touches[1];\n if (!touch1 || !touch2) return 0;\n const dx = touch1.clientX - touch2.clientX;\n const dy = touch1.clientY - touch2.clientY;\n return Math.sqrt(dx * dx + dy * dy);\n};\n\nconst getTouchCenter = (touches: Touch[]): { x: number; y: number } | null => {\n if (touches.length === 0) return null;\n if (touches.length === 1) {\n const touch = touches[0];\n if (!touch) return null;\n return { x: touch.clientX, y: touch.clientY };\n }\n // For 2 touches, return the center point\n const touch1 = touches[0];\n const touch2 = touches[1];\n if (!touch1 || !touch2) return null;\n return {\n x: (touch1.clientX + touch2.clientX) / 2,\n y: (touch1.clientY + touch2.clientY) / 2,\n };\n};\n\n// Handle touch events on the image directly\nconst handleImageTouchStart = (event: TouchEvent) => {\n if (!imageRef.value || !props.isOpen) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n const touches = Array.from(event.touches);\n\n if (touches.length === 2) {\n // Pinch zoom gesture\n isPinching.value = true;\n const distance = getTouchDistance(touches);\n const center = getTouchCenter(touches);\n\n if (center) {\n touchState.value = {\n type: 'pinch',\n initialDistance: distance,\n initialScale: scale.value,\n initialTranslateX: translateX.value,\n initialTranslateY: translateY.value,\n initialTouches: touches,\n };\n }\n } else if (touches.length === 1 && scale.value > 1) {\n // Single touch pan (only when zoomed in)\n isDragging.value = true;\n const touch = touches[0];\n if (touch) {\n touchState.value = {\n type: 'pan',\n lastPanX: touch.clientX,\n lastPanY: touch.clientY,\n };\n }\n } else {\n // Clear any existing touch state\n touchState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n }\n};\n\nconst handleImageTouchMove = (event: TouchEvent) => {\n if (!imageRef.value || !props.isOpen || !touchState.value) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n handleTouchMove(event);\n};\n\nconst handleImageTouchEnd = (event: TouchEvent) => {\n if (!imageRef.value || !props.isOpen) return;\n\n event.preventDefault();\n event.stopPropagation();\n\n handleTouchEnd();\n};\n\n// Handle touch events on the container (for areas outside the image)\nconst handleTouchStart = (event: TouchEvent) => {\n if (!imageRef.value || !props.isOpen) return;\n\n // Don't handle touch if it's on the zoom controls\n const target = event.target as HTMLElement;\n if (target.closest('.zoom-controls')) {\n return;\n }\n\n // If touch is on the image, let handleImageTouchStart handle it\n if (target === imageRef.value || imageRef.value.contains(target)) {\n return;\n }\n\n event.preventDefault(); // Prevent scrolling\n\n const touches = Array.from(event.touches);\n\n if (touches.length === 2) {\n // Pinch zoom gesture\n isPinching.value = true;\n const distance = getTouchDistance(touches);\n const center = getTouchCenter(touches);\n\n if (center) {\n touchState.value = {\n type: 'pinch',\n initialDistance: distance,\n initialScale: scale.value,\n initialTranslateX: translateX.value,\n initialTranslateY: translateY.value,\n initialTouches: touches,\n };\n }\n } else if (touches.length === 1 && scale.value > 1) {\n // Single touch pan (only when zoomed in)\n isDragging.value = true;\n const touch = touches[0];\n if (touch) {\n touchState.value = {\n type: 'pan',\n lastPanX: touch.clientX,\n lastPanY: touch.clientY,\n };\n }\n } else {\n // Clear any existing touch state\n touchState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n }\n};\n\nconst handleTouchMove = (event: TouchEvent) => {\n if (!imageRef.value || !props.isOpen || !touchState.value) return;\n\n const touches = Array.from(event.touches);\n\n // Use requestAnimationFrame for smoother updates\n requestAnimationFrame(() => {\n if (!touchState.value) return;\n\n if (touchState.value.type === 'pinch' && touches.length === 2) {\n // Handle pinch zoom\n event.preventDefault();\n\n const distance = getTouchDistance(touches);\n if (touchState.value.initialDistance && touchState.value.initialScale !== undefined) {\n const scaleChange = distance / touchState.value.initialDistance;\n const newScale = Math.max(\n minScale,\n Math.min(maxScale, touchState.value.initialScale * scaleChange),\n );\n scale.value = newScale;\n\n // Optionally adjust translation based on pinch center\n // This keeps the pinch center point stable during zoom\n }\n } else if (touchState.value.type === 'pan' && touches.length === 1 && scale.value > 1) {\n // Handle single touch pan (only when zoomed in)\n event.preventDefault();\n\n const touch = touches[0];\n if (\n touch &&\n touchState.value.lastPanX !== undefined &&\n touchState.value.lastPanY !== undefined\n ) {\n const deltaX = touch.clientX - touchState.value.lastPanX;\n const deltaY = touch.clientY - touchState.value.lastPanY;\n\n // Pan relative to current scale\n translateX.value += deltaX / scale.value;\n translateY.value += deltaY / scale.value;\n\n // Clamp translation\n const maxTranslate = 200;\n translateX.value = Math.max(-maxTranslate, Math.min(maxTranslate, translateX.value));\n translateY.value = Math.max(-maxTranslate, Math.min(maxTranslate, translateY.value));\n\n touchState.value.lastPanX = touch.clientX;\n touchState.value.lastPanY = touch.clientY;\n }\n } else {\n // Touch count changed or invalid state - reset\n touchState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n }\n });\n};\n\nconst handleTouchEnd = () => {\n touchState.value = null;\n isDragging.value = false;\n isPinching.value = false;\n};\n\n// Image load handlers\nconst handleImageLoad = () => {\n isLoading.value = false;\n hasError.value = false;\n};\n\nconst handleImageError = () => {\n isLoading.value = false;\n hasError.value = true;\n};\n\n// Navigation\nconst goToPrevious = () => {\n if (props.onPrevious) {\n props.onPrevious();\n }\n};\n\nconst goToNext = () => {\n if (props.onNext) {\n props.onNext();\n }\n};\n\n// Actions\nconst handleDownload = () => {\n if (props.onDownload) {\n props.onDownload();\n }\n};\n\nconst handleClose = () => {\n isOpen.value = false;\n emit('close');\n};\n\nconst handleBackdropClick = (event: MouseEvent) => {\n if (event.target === dialogRef.value) {\n handleClose();\n }\n};\n\n// Cleanup\nonUnmounted(() => {\n window.removeEventListener('resize', () => {});\n // Clean up event listeners\n if (imageContainerRef.value) {\n imageContainerRef.value.removeEventListener('wheel', handleWheel);\n imageContainerRef.value.removeEventListener('mousedown', handleMouseDown);\n imageContainerRef.value.removeEventListener('touchstart', handleTouchStart);\n imageContainerRef.value.removeEventListener('touchmove', handleTouchMove);\n imageContainerRef.value.removeEventListener('touchend', handleTouchEnd);\n imageContainerRef.value.removeEventListener('touchcancel', handleTouchEnd);\n }\n // Remove global mouse listeners\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('mouseup', handleMouseUp);\n});\n</script>\n","<template>\n <div :class=\"badgeClasses\" :aria-label=\"ariaLabel\" role=\"status\">\n {{ displayText }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\n/**\n * SupportTicketPriorityBadge - A reusable Vue component for displaying support ticket priority\n * as color-coded badges with consistent DaisyUI styling and accessibility features.\n *\n * @example\n * <SupportTicketPriorityBadge :priority=\"'HIGH'\" size=\"md\" />\n * <SupportTicketPriorityBadge :priority=\"'CRITICAL'\" size=\"sm\" variant=\"outline\" />\n */\nimport type { SupportTicketPriority } from '@dragonmastery/dragoncore-shared';\nimport { computed } from 'vue';\n\n/**\n * Props for the SupportTicketPriorityBadge component\n */\ninterface Props {\n /** The support ticket priority to display */\n priority: SupportTicketPriority;\n /** Size of the badge - defaults to 'md' */\n size?: 'sm' | 'md' | 'lg';\n /** Visual variant of the badge - defaults to 'default' */\n variant?: 'default' | 'outline';\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'default',\n});\n\n/**\n * Configuration for each priority badge\n */\ninterface PriorityBadgeConfig {\n /** DaisyUI badge color class */\n color: string;\n /** Display text for the badge */\n text: string;\n /** Accessibility label for screen readers */\n ariaLabel: string;\n}\n\nconst priorityConfig: Record<SupportTicketPriority, PriorityBadgeConfig> = {\n LOW: {\n color: 'badge-neutral',\n text: 'Low',\n ariaLabel: 'Priority: Low priority',\n },\n MEDIUM: {\n color: 'badge-neutral',\n text: 'Medium',\n ariaLabel: 'Priority: Medium priority',\n },\n HIGH: {\n color: 'badge-neutral',\n text: 'High',\n ariaLabel: 'Priority: High priority',\n },\n CRITICAL: {\n color: 'badge-neutral',\n text: 'Critical',\n ariaLabel: 'Priority: Critical priority',\n },\n};\n\nconst getPriorityConfig = (priority: SupportTicketPriority): PriorityBadgeConfig => {\n const config = priorityConfig[priority];\n if (!config) {\n return {\n color: 'badge-neutral',\n text: priority || 'Unknown',\n ariaLabel: `Priority: ${priority || 'Unknown priority'}`,\n };\n }\n return config;\n};\n\nconst config = computed(() => getPriorityConfig(props.priority));\n\nconst badgeClasses = computed(() => {\n const baseClasses = ['badge', 'text-xs'];\n\n // Add color class\n baseClasses.push(config.value.color);\n\n // Add size class with responsive text sizing\n if (props.size === 'sm') {\n baseClasses.push('badge-sm', 'text-xs');\n } else if (props.size === 'lg') {\n baseClasses.push('badge-lg', 'text-sm');\n } else {\n // md size - responsive text\n baseClasses.push('text-xs', 'sm:text-sm');\n }\n\n // Add variant class\n if (props.variant === 'outline') {\n baseClasses.push('badge-outline');\n }\n\n return baseClasses.join(' ');\n});\n\nconst displayText = computed(() => config.value.text);\nconst ariaLabel = computed(() => config.value.ariaLabel);\n</script>\n","<template>\n <div :class=\"badgeClasses\" :aria-label=\"ariaLabel\" role=\"status\">\n {{ displayText }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\n/**\n * SupportTicketTypeBadge - A reusable Vue component for displaying support ticket type\n * as color-coded badges with consistent DaisyUI styling and accessibility features.\n *\n * @example\n * <SupportTicketTypeBadge :type=\"'BUG'\" size=\"md\" />\n * <SupportTicketTypeBadge :type=\"'FEATURE_REQUEST'\" size=\"sm\" variant=\"outline\" />\n */\nimport type { SupportTicketType } from '@dragonmastery/dragoncore-shared';\nimport { computed } from 'vue';\n\n/**\n * Props for the SupportTicketTypeBadge component\n */\ninterface Props {\n /** The support ticket type to display */\n type: SupportTicketType;\n /** Size of the badge - defaults to 'md' */\n size?: 'sm' | 'md' | 'lg';\n /** Visual variant of the badge - defaults to 'default' */\n variant?: 'default' | 'outline';\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'default',\n});\n\n/**\n * Configuration for each type badge\n */\ninterface TypeBadgeConfig {\n /** DaisyUI badge color class */\n color: string;\n /** Display text for the badge */\n text: string;\n /** Accessibility label for screen readers */\n ariaLabel: string;\n}\n\nconst typeConfig: Record<SupportTicketType, TypeBadgeConfig> = {\n BUG: {\n color: 'badge-neutral',\n text: 'Bug',\n ariaLabel: 'Type: Bug report requiring fix',\n },\n FEATURE_REQUEST: {\n color: 'badge-neutral',\n text: 'Feature',\n ariaLabel: 'Type: New feature request',\n },\n IMPROVEMENT: {\n color: 'badge-neutral',\n text: 'Improvement',\n ariaLabel: 'Type: Enhancement to existing feature',\n },\n OPERATIONAL: {\n color: 'badge-neutral',\n text: 'Ops',\n ariaLabel: 'Type: Operational/admin work',\n },\n};\n\nconst getTypeConfig = (type: SupportTicketType): TypeBadgeConfig => {\n const config = typeConfig[type];\n if (!config) {\n return {\n color: 'badge-neutral',\n text: type || 'Unknown',\n ariaLabel: `Type: ${type || 'Unknown type'}`,\n };\n }\n return config;\n};\n\nconst config = computed(() => getTypeConfig(props.type));\n\nconst badgeClasses = computed(() => {\n const baseClasses = ['badge', 'text-xs'];\n\n // Add color class\n baseClasses.push(config.value.color);\n\n // Add size class with responsive text sizing\n if (props.size === 'sm') {\n baseClasses.push('badge-sm', 'text-xs');\n } else if (props.size === 'lg') {\n baseClasses.push('badge-lg', 'text-sm');\n } else {\n // md size - responsive text\n baseClasses.push('text-xs', 'sm:text-sm');\n }\n\n // Add variant class\n if (props.variant === 'outline') {\n baseClasses.push('badge-outline');\n }\n\n return baseClasses.join(' ');\n});\n\nconst displayText = computed(() => config.value.text);\nconst ariaLabel = computed(() => config.value.ariaLabel);\n</script>\n","import type { SupportTicketApproval } from '@dragonmastery/dragoncore-shared';\n\n/**\n * Helper function to check if credit value is empty\n */\nfunction isCreditValueEmpty(value: string | null | undefined): boolean {\n return value === null || value === undefined || value.trim() === '';\n}\n\n/**\n * Core credit formatting logic\n */\nfunction formatCreditValueCore(creditValue: string | null | undefined): string {\n // Use same logic as backend formatter\n if (isCreditValueEmpty(creditValue)) {\n return 'TBD';\n }\n\n const trimmed = creditValue!.trim();\n const num = parseFloat(trimmed);\n\n // Explicit zero is valid - preserve it (same as backend)\n if (num === 0) {\n return '0';\n }\n\n // Remove trailing zeros for non-zero values (same as backend)\n // Examples: \"100.00\" -> \"100\", \"9.50\" -> \"9.5\", \"123.45\" -> \"123.45\"\n return trimmed.replace(/\\.?0+$/, '');\n}\n\n/**\n * Formats credit value for staff views (includes internal ticket logic)\n *\n * @param creditValue - The credit value from the database\n * @param approvalStatus - The approval status to determine display logic\n * @returns Formatted string for display\n *\n * @example\n * formatStaffCreditValue(\"5.50\", \"PENDING\") // \"5.5\"\n * formatStaffCreditValue(\"10\", \"INTERNAL\") // \"N/A\"\n */\nexport function formatStaffCreditValue(\n creditValue: string | null | undefined,\n approvalStatus?: SupportTicketApproval,\n): string {\n // Internal tickets don't use credits\n if (approvalStatus === 'INTERNAL') {\n return 'N/A';\n }\n\n return formatCreditValueCore(creditValue);\n}\n\n/**\n * Formats credit value for customer views (status-based logic)\n *\n * @param creditValue - The credit value from the database\n * @param status - The computed status from the customer query\n * @returns Formatted string for display\n *\n * @example\n * formatCustomerCreditValue(\"5.50\", \"PENDING\") // \"5.5\"\n * formatCustomerCreditValue(\"5.50\", \"FOLLOWUP\") // \"5.5\"\n */\nexport function formatCustomerCreditValue(creditValue: string | null | undefined): string {\n // Note: Internal tickets (status would be different) are handled by backend\n // Customers don't see internal tickets, so we don't need to check for INTERNAL here\n return formatCreditValueCore(creditValue);\n}\n","<template>\n <div class=\"mt-4 sm:mt-6\">\n <input ref=\"fileInput\" type=\"file\" multiple class=\"hidden\" @change=\"handleFileSelect\" />\n\n <!-- Drag & Drop and Paste: compact button-like on mobile, expanded on desktop -->\n <div v-if=\"canUpload\" class=\"grid grid-cols-2 gap-2 sm:gap-4 mb-4\">\n <!-- Drag & Drop Zone -->\n <div\n class=\"flex flex-col items-center justify-center gap-1 sm:gap-2 py-2.5 px-2 sm:py-6 sm:px-4 rounded-lg border-2 border-dashed transition-colors cursor-pointer touch-manipulation text-center\"\n :class=\"{\n 'border-primary bg-primary/5': isDragging,\n 'border-base-300 hover:border-primary/50 active:border-primary/50': !isDragging,\n }\"\n @dragover.prevent=\"isDragging = true\"\n @dragleave.prevent=\"isDragging = false\"\n @drop.prevent=\"handleFileDrop\"\n @click.prevent.stop=\"openFileSelector\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 sm:h-12 sm:w-12 text-primary shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12\"\n />\n </svg>\n <span class=\"text-xs sm:text-lg font-medium\">Browse</span>\n <span class=\"hidden sm:inline text-xs text-base-content/70\"\n >Drag and drop or click</span\n >\n <p v-if=\"!recordId\" class=\"hidden sm:block text-xs text-base-content/50\">\n Queued until ticket is created\n </p>\n </div>\n\n <!-- Paste zone - collapse handles visibility via CSS, so no unmount during animation -->\n <div\n tabindex=\"0\"\n role=\"textbox\"\n aria-label=\"Paste image here\"\n class=\"flex flex-col items-center justify-center gap-1 sm:gap-2 py-2.5 px-2 sm:py-6 sm:px-4 rounded-lg border-2 border-dashed border-base-300 cursor-text outline-none focus:border-primary focus:ring-2 focus:ring-primary/20 focus:bg-primary/5 touch-manipulation text-center\"\n @paste=\"handlePaste\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-5 w-5 sm:h-12 sm:w-12 text-primary shrink-0\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n <span class=\"text-xs sm:text-lg font-medium\">Paste</span>\n <span class=\"hidden sm:inline text-xs text-base-content/70\"\n >Tap to focus, then paste</span\n >\n </div>\n </div>\n\n <!-- Upload Queue -->\n <div v-if=\"uploadQueue.length > 0\" class=\"mb-4 space-y-2\">\n <div\n v-for=\"item in uploadQueue\"\n :key=\"item.id\"\n class=\"flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-3 p-2 sm:p-3 bg-base-200 rounded-lg\"\n >\n <div class=\"flex-1 min-w-0\">\n <div class=\"text-xs sm:text-sm font-medium truncate\">\n {{ item.file.name }}\n </div>\n <div\n v-if=\"item.status === 'uploading'\"\n class=\"w-full bg-base-300 rounded-full h-1.5 mt-1\"\n >\n <div\n class=\"bg-primary h-1.5 rounded-full transition-all\"\n :style=\"{ width: `${item.progress}%` }\"\n ></div>\n </div>\n <div v-if=\"item.status === 'error'\" class=\"text-xs text-error mt-1 break-words\">\n {{ item.errorMessage }}\n </div>\n </div>\n <div class=\"flex gap-2 sm:gap-1\">\n <button\n v-if=\"item.status === 'error'\"\n @click.prevent=\"retryUpload(item)\"\n class=\"btn btn-xs btn-ghost flex-1 sm:flex-none\"\n type=\"button\"\n >\n Retry\n </button>\n <button\n v-if=\"item.status !== 'uploading'\"\n @click.prevent=\"removeFromQueue(item.id)\"\n class=\"btn btn-xs btn-ghost text-error flex-1 sm:flex-none\"\n type=\"button\"\n >\n Remove\n </button>\n </div>\n </div>\n </div>\n\n <!-- Attachments List -->\n <div v-if=\"(attachments.length > 0 || attachmentsLoading) && recordId\" class=\"space-y-2\">\n <div v-if=\"attachmentsLoading\" class=\"flex justify-center py-4\">\n <span class=\"loading loading-spinner loading-sm\"></span>\n </div>\n <div\n v-for=\"file in attachments\"\n :key=\"file.id\"\n class=\"flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-3 p-2 sm:p-3 bg-base-100 border border-base-300 rounded-lg hover:bg-base-200 transition-colors\"\n :class=\"{ 'cursor-pointer': isImage(file.type) }\"\n @click.prevent=\"isImage(file.type) && viewImage(file)\"\n >\n <div class=\"flex items-center gap-2 sm:gap-3 flex-1 min-w-0 w-full sm:w-auto\">\n <div class=\"flex-shrink-0\">\n <!-- Image thumbnail preview -->\n <div\n v-if=\"isImage(file.type)\"\n class=\"w-10 h-10 sm:w-12 sm:h-12 rounded overflow-hidden bg-base-200 flex items-center justify-center\"\n >\n <img\n v-if=\"getImageUrlSync(file.id)\"\n :src=\"getImageUrlSync(file.id)\"\n :alt=\"file.name\"\n class=\"w-full h-full object-cover\"\n @error=\"handleImageError\"\n />\n <div v-else class=\"w-full h-full flex items-center justify-center\">\n <span class=\"loading loading-spinner loading-xs\"></span>\n </div>\n </div>\n <!-- File type badge for non-images -->\n <div v-else class=\"badge badge-sm\" :class=\"getFileTypeBadgeClass(file.type)\">\n {{ getFileTypeLabel(file.type) }}\n </div>\n </div>\n <div class=\"flex-1 min-w-0\">\n <div class=\"text-sm sm:text-base font-medium truncate\">\n {{ file.name }}\n </div>\n <div class=\"text-xs text-base-content/60\">\n {{ formatFileSize(file.size) }} •\n {{ formatDate(file.uploadedAt) }}\n </div>\n </div>\n </div>\n <div class=\"flex gap-1 justify-end sm:justify-start\" @click.stop>\n <button\n v-if=\"isImage(file.type)\"\n @click.prevent=\"viewImage(file)\"\n class=\"btn btn-sm btn-ghost btn-circle\"\n title=\"View Image\"\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z\"\n />\n </svg>\n </button>\n <button\n @click.prevent=\"downloadFile(file, $event)\"\n class=\"btn btn-sm btn-ghost btn-circle\"\n title=\"Download\"\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4\"\n />\n </svg>\n </button>\n <button\n v-if=\"canDelete\"\n @click.prevent=\"confirmDelete(file)\"\n class=\"btn btn-sm btn-ghost btn-circle text-error\"\n title=\"Delete\"\n type=\"button\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-4 w-4\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n </div>\n\n <div\n v-else-if=\"!attachmentsLoading && recordId\"\n class=\"text-center py-8 text-base-content/50\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n class=\"h-12 w-12 mx-auto mb-2 opacity-50\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M15.172 7l-6.586 6.586a2 2 0 102.828 2.828l6.414-6.586a4 4 0 00-5.656-5.656l-6.415 6.585a6 6 0 108.486 8.486L20.5 13\"\n />\n </svg>\n <p>No attachments</p>\n </div>\n\n <!-- Image View Modal -->\n <ImageModal\n :is-open=\"!!viewingImage\"\n :image-src=\"viewingImage ? getImageUrlSync(viewingImage.id) : ''\"\n :image-name=\"viewingImage?.name || ''\"\n :image-index=\"viewingImageIndex\"\n :total-images=\"imageAttachments.length\"\n :on-download=\"\n viewingImage\n ? () => {\n if (viewingImage) downloadFile(viewingImage, undefined);\n }\n : undefined\n \"\n :on-previous=\"canGoToPrevious ? goToPreviousImage : undefined\"\n :on-next=\"canGoToNext ? goToNextImage : undefined\"\n @close=\"closeImageModal\"\n />\n\n <!-- Delete Confirmation Modal -->\n <ConfirmDialog\n v-model=\"showDeleteModal\"\n title=\"Delete Attachment\"\n :message=\"deleteMessage\"\n confirm-text=\"Delete\"\n cancel-text=\"Cancel\"\n confirm-button-class=\"btn-error\"\n @confirm=\"deleteFile\"\n @cancel=\"closeDeleteModal\"\n />\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, onUnmounted, ref, watch } from 'vue';\nimport ConfirmDialog from '../../../components/ConfirmDialog.vue';\nimport ImageModal from '../../../components/ImageModal.vue';\nimport { useEnv } from '../../../composables/useEnv';\nimport { useMutation } from '../../../composables/useMutation';\nimport { useQuery } from '../../../composables/useQuery';\nimport { useUserSessionStore } from '../../../composables/useUserSessionStore';\n\ninterface Attachment {\n id: string;\n name: string;\n size: number;\n type: string;\n uploadedAt: Date;\n}\n\ninterface QueueItem {\n id: string;\n file: File;\n status: 'pending' | 'uploading' | 'success' | 'error';\n progress: number;\n errorMessage?: string;\n}\n\ninterface Props {\n recordId?: string; // Optional for create forms\n canUpload?: boolean;\n canDelete?: boolean;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n canUpload: true,\n canDelete: true,\n});\n\nconst emit = defineEmits<{\n uploaded: [];\n deleted: [];\n filesQueued: [files: File[]];\n 'update:attachmentsCount': [count: number];\n}>();\n\n// Expose methods for parent components (e.g., to get queued files after creation)\nconst uploadQueuedFiles = async (ticketId: string) => {\n // Upload all pending files with the new ticket ID\n const pendingItems = uploadQueue.value.filter((item) => item.status === 'pending');\n for (const item of pendingItems) {\n item.status = 'uploading';\n await uploadFile(item, ticketId);\n }\n};\n\ndefineExpose({\n getQueuedFiles: () =>\n uploadQueue.value.filter((item) => item.status === 'pending').map((item) => item.file),\n uploadQueuedFiles,\n});\n\nconst fileInput = ref<HTMLInputElement | null>(null);\nconst isDragging = ref(false);\nconst isUploading = ref(false);\nconst uploadQueue = ref<QueueItem[]>([]);\nconst fileToDelete = ref<Attachment | null>(null);\nconst showDeleteModal = ref(false);\nconst viewingImage = ref<Attachment | null>(null);\nconst imageUrls = ref<Map<string, string>>(new Map()); // Cache of image object URLs\n\nconst env = useEnv();\nconst userStore = useUserSessionStore();\n\n// Query attachments (only if recordId exists)\nconst {\n data: attachmentsData,\n loading: attachmentsLoading,\n refetch: refreshAttachments,\n} = useQuery(\n async (api) => {\n if (!props.recordId) return { files: [], folders: [] };\n return await api.attachments.listAttachments({\n record_id: props.recordId,\n record_type: 'support_ticket',\n filters: {\n limit: 100,\n include_folders: false,\n folder_id: null, // Only root level attachments for Jira-style\n },\n });\n },\n {\n enabled: computed(() => !!props.recordId),\n staleTime: 2 * 60 * 1000,\n },\n);\n\nconst attachments = computed<Attachment[]>(() => {\n if (!attachmentsData.value) return [];\n const files: Attachment[] = [];\n const fileItems = attachmentsData.value.files || [];\n for (const item of fileItems) {\n files.push({\n id: item.id,\n name: item.original_name,\n size: parseInt(item.file_size || '0'),\n type: item.content_type || '',\n uploadedAt: new Date(item.created_at),\n });\n }\n return files;\n});\n\n// Emit count for parent (e.g. collapse header)\nwatch(\n () => attachments.value.length,\n (count) => emit('update:attachmentsCount', count),\n { immediate: true },\n);\n\nconst deleteMessage = computed(() => {\n if (!fileToDelete.value)\n return 'Are you sure you want to delete this attachment? This action cannot be undone.';\n return `Are you sure you want to delete \"${fileToDelete.value.name}\"? This action cannot be undone.`;\n});\n\n// Filter to only image attachments for navigation\nconst imageAttachments = computed(() => {\n return attachments.value.filter((file) => isImage(file.type));\n});\n\n// Current viewing image index\nconst viewingImageIndex = computed(() => {\n if (!viewingImage.value) return null;\n const index = imageAttachments.value.findIndex((img) => img.id === viewingImage.value?.id);\n return index >= 0 ? index : null;\n});\n\nconst canGoToPrevious = computed(() => {\n return viewingImageIndex.value !== null && viewingImageIndex.value > 0;\n});\n\nconst canGoToNext = computed(() => {\n return (\n viewingImageIndex.value !== null &&\n viewingImageIndex.value < imageAttachments.value.length - 1\n );\n});\n\n// Mutations\nconst { mutate: deleteAttachment } = useMutation(\n (api, input: { id: string }) => api.attachments.deleteAttachment(input.id),\n { invalidate: /^attachments?:/ },\n);\n\n// File type helpers\nconst getFileTypeLabel = (mimeType: string): string => {\n if (mimeType.startsWith('image/')) return 'IMG';\n if (mimeType.startsWith('video/')) return 'VID';\n if (mimeType.startsWith('audio/')) return 'AUD';\n if (mimeType.includes('pdf')) return 'PDF';\n if (mimeType.includes('word') || mimeType.includes('document')) return 'DOC';\n if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'XLS';\n if (mimeType.includes('zip') || mimeType.includes('archive')) return 'ZIP';\n return 'FILE';\n};\n\nconst getFileTypeBadgeClass = (mimeType: string): string => {\n if (mimeType.startsWith('image/')) return 'badge-primary';\n if (mimeType.startsWith('video/')) return 'badge-secondary';\n if (mimeType.includes('pdf')) return 'badge-error';\n if (mimeType.includes('word') || mimeType.includes('document')) return 'badge-info';\n return 'badge-ghost';\n};\n\nconst formatFileSize = (bytes: number): string => {\n if (bytes === 0) return '0 B';\n const k = 1024;\n const sizes = ['B', 'KB', 'MB', 'GB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;\n};\n\nconst formatDate = (date: Date): string => {\n return new Intl.DateTimeFormat('en-US', {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n }).format(date);\n};\n\n// Image helpers\nconst isImage = (mimeType: string): boolean => {\n return mimeType.startsWith('image/');\n};\n\nconst getImageUrl = async (fileId: string): Promise<string> => {\n // Check if we already have a cached URL\n if (imageUrls.value.has(fileId)) {\n return imageUrls.value.get(fileId)!;\n }\n\n // Fetch image with bearer token\n try {\n const res = await fetch(\n `${env.restApiClient.apiUrl}/attachments/support_ticket/${fileId}`,\n {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${userStore.accessToken}`,\n },\n },\n );\n\n if (!res.ok) {\n throw new Error('Failed to load image');\n }\n\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n imageUrls.value.set(fileId, url);\n return url;\n } catch (error) {\n console.error('Failed to load image:', error);\n // Return empty string or placeholder\n return '';\n }\n};\n\n// Computed property to get image URLs for thumbnails\nconst getImageUrlSync = (fileId: string): string => {\n // For thumbnails, we'll use a loading state and fetch async\n // Return empty initially, will be updated when loaded\n return imageUrls.value.get(fileId) || '';\n};\n\n// Load image for thumbnail\nconst loadImageThumbnail = async (fileId: string) => {\n if (!imageUrls.value.has(fileId)) {\n await getImageUrl(fileId);\n }\n};\n\nconst handleImageError = (event: Event) => {\n const img = event.target as HTMLImageElement;\n img.style.display = 'none';\n // Could show a placeholder icon here\n};\n\n// Cleanup object URLs when component unmounts\nonUnmounted(() => {\n imageUrls.value.forEach((url) => {\n URL.revokeObjectURL(url);\n });\n imageUrls.value.clear();\n});\n\nconst viewImage = async (file: Attachment) => {\n viewingImage.value = file;\n // Load the image URL if not already loaded\n if (!imageUrls.value.has(file.id)) {\n await getImageUrl(file.id);\n }\n};\n\nconst goToPreviousImage = () => {\n if (!canGoToPrevious.value || viewingImageIndex.value === null) return;\n const previousImage = imageAttachments.value[viewingImageIndex.value - 1];\n if (previousImage) {\n viewImage(previousImage);\n }\n};\n\nconst goToNextImage = () => {\n if (!canGoToNext.value || viewingImageIndex.value === null) return;\n const nextImage = imageAttachments.value[viewingImageIndex.value + 1];\n if (nextImage) {\n viewImage(nextImage);\n }\n};\n\nconst closeImageModal = () => {\n viewingImage.value = null;\n};\n\n// File upload\nconst openFileSelector = (event?: Event) => {\n // Always prevent default and stop propagation\n if (event) {\n event.preventDefault();\n event.stopPropagation();\n }\n\n // Prevent if already uploading\n if (isUploading.value) {\n return;\n }\n\n // Allow file selection even without recordId (for create forms)\n // Files will be queued and uploaded after ticket creation\n fileInput.value?.click();\n};\n\nconst processFiles = (files: File[]) => {\n const fileArray = Array.from(files);\n fileArray.forEach((file) => {\n const queueItem: QueueItem = {\n id: `${Date.now()}-${Math.random()}`,\n file,\n status: 'pending',\n progress: 0,\n };\n uploadQueue.value.push(queueItem);\n });\n\n emit('filesQueued', fileArray);\n\n // If recordId exists, upload immediately. Otherwise, queue for later\n if (props.recordId) {\n processUploadQueue();\n }\n};\n\nconst handleFileSelect = (event: Event) => {\n const target = event.target as HTMLInputElement;\n const files = target.files;\n if (!files) return;\n\n processFiles(Array.from(files));\n target.value = ''; // Reset input\n};\n\nconst handleFileDrop = (event: DragEvent) => {\n isDragging.value = false;\n\n const files = event.dataTransfer?.files;\n if (!files || files.length === 0) return;\n\n processFiles(Array.from(files));\n};\n\nconst handlePaste = (event: ClipboardEvent) => {\n if (!props.canUpload || isUploading.value) return;\n\n const items = event.clipboardData?.items;\n if (!items) return;\n\n const imageFiles: File[] = [];\n for (const item of items) {\n if (item.type.startsWith('image/')) {\n const file = item.getAsFile();\n if (file) {\n imageFiles.push(file);\n }\n }\n }\n\n if (imageFiles.length > 0) {\n event.preventDefault();\n event.stopPropagation();\n processFiles(imageFiles);\n }\n};\n\nconst processUploadQueue = async (ticketId?: string) => {\n const nextItem = uploadQueue.value.find((item) => item.status === 'pending');\n if (!nextItem) {\n isUploading.value = false;\n return;\n }\n\n isUploading.value = true;\n nextItem.status = 'uploading';\n\n await uploadFile(nextItem, ticketId);\n\n // Process next item\n if (uploadQueue.value.some((item) => item.status === 'pending')) {\n processUploadQueue(ticketId);\n } else {\n isUploading.value = false;\n }\n};\n\nconst uploadFile = async (item: QueueItem, ticketId?: string) => {\n const recordId = ticketId || props.recordId;\n if (!recordId) {\n item.status = 'error';\n item.errorMessage = 'Ticket ID is required for upload';\n return;\n }\n\n const progressInterval = setInterval(() => {\n item.progress += Math.random() * 10;\n item.progress = Math.min(99, Math.round(item.progress * 100) / 100);\n }, 200);\n\n try {\n const formData = new FormData();\n formData.append('file', item.file);\n formData.append('file_name', item.file.name);\n formData.append('record_id', recordId);\n formData.append('record_type', 'support_ticket');\n\n const response = await fetch(\n `${env.restApiClient.apiUrl}/attachments/support_ticket/${recordId}`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${userStore.accessToken}`,\n },\n body: formData,\n },\n );\n\n if (!response.ok) {\n throw new Error(`Upload failed: ${response.statusText}`);\n }\n\n const result = await response.json();\n if (result && result.id) {\n clearInterval(progressInterval);\n item.progress = 100;\n item.status = 'success';\n setTimeout(() => {\n removeFromQueue(item.id);\n refreshAttachments();\n emit('uploaded');\n }, 500);\n } else {\n throw new Error('Invalid response');\n }\n } catch (error) {\n clearInterval(progressInterval);\n item.status = 'error';\n item.errorMessage = error instanceof Error ? error.message : 'Upload failed';\n }\n};\n\nconst removeFromQueue = (id: string) => {\n const index = uploadQueue.value.findIndex((item) => item.id === id);\n if (index !== -1) {\n uploadQueue.value.splice(index, 1);\n }\n};\n\nconst retryUpload = (item: QueueItem) => {\n item.status = 'pending';\n item.progress = 0;\n item.errorMessage = undefined;\n if (props.recordId) {\n processUploadQueue();\n }\n};\n\n// File download\nconst downloadFile = async (file: Attachment, event?: Event) => {\n if (event) {\n event.preventDefault();\n event.stopPropagation();\n }\n try {\n const res = await fetch(\n `${env.restApiClient.apiUrl}/attachments/support_ticket/${file.id}`,\n {\n method: 'GET',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${userStore.accessToken}`,\n },\n },\n );\n if (!res.ok) {\n throw new Error('Failed to download file');\n }\n const blob = await res.blob();\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = file.name;\n document.body.appendChild(a);\n a.click();\n a.remove();\n URL.revokeObjectURL(url);\n } catch (error) {\n console.error('Download failed:', error);\n }\n};\n\n// File delete\nconst confirmDelete = (file: Attachment) => {\n fileToDelete.value = file;\n showDeleteModal.value = true;\n};\n\nconst closeDeleteModal = () => {\n showDeleteModal.value = false;\n fileToDelete.value = null;\n};\n\nconst deleteFile = async () => {\n if (!fileToDelete.value) return;\n\n try {\n await deleteAttachment({ id: fileToDelete.value.id });\n refreshAttachments();\n emit('deleted');\n closeDeleteModal();\n } catch (error) {\n console.error('Delete failed:', error);\n }\n};\n\n// Watch for recordId changes\nwatch(\n () => props.recordId,\n (newRecordId, oldRecordId) => {\n if (newRecordId && newRecordId !== oldRecordId) {\n refreshAttachments();\n // If we have queued files and now have a recordId, upload them\n if (uploadQueue.value.some((item) => item.status === 'pending')) {\n processUploadQueue(newRecordId);\n }\n }\n },\n);\n\n// Load image thumbnails when attachments are loaded\nwatch(\n () => attachments.value,\n (newAttachments) => {\n newAttachments.forEach((file) => {\n if (isImage(file.type) && !imageUrls.value.has(file.id)) {\n loadImageThumbnail(file.id);\n }\n });\n },\n { immediate: true },\n);\n</script>\n","<template>\n <div :class=\"badgeClasses\" :aria-label=\"ariaLabel\" role=\"status\">\n {{ displayText }}\n </div>\n</template>\n\n<script setup lang=\"ts\">\n/**\n * SupportTicketApprovalBadge - A reusable Vue component for displaying support ticket approval status\n * as color-coded badges with consistent DaisyUI styling and accessibility features.\n *\n * @example\n * <SupportTicketApprovalBadge :approvalStatus=\"'PENDING'\" size=\"md\" />\n * <SupportTicketApprovalBadge :approvalStatus=\"'APPROVED'\" size=\"sm\" variant=\"outline\" />\n */\nimport type { SupportTicketApproval } from '@dragonmastery/dragoncore-shared';\nimport { computed } from 'vue';\n\n/**\n * Props for the SupportTicketApprovalBadge component\n */\ninterface Props {\n /** The support ticket approval status to display */\n approvalStatus: SupportTicketApproval;\n /** Size of the badge - defaults to 'md' */\n size?: 'sm' | 'md' | 'lg';\n /** Visual variant of the badge - defaults to 'default' */\n variant?: 'default' | 'outline';\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n size: 'md',\n variant: 'default',\n});\n\n/**\n * Configuration for each approval status badge\n */\ninterface ApprovalBadgeConfig {\n /** DaisyUI badge color class */\n color: string;\n /** Display text for the badge */\n text: string;\n /** Accessibility label for screen readers */\n ariaLabel: string;\n}\n\nconst approvalConfig: Record<SupportTicketApproval, ApprovalBadgeConfig> = {\n PENDING: {\n color: 'badge-warning',\n text: 'Pending',\n ariaLabel: 'Approval: Awaiting staff decision',\n },\n APPROVED: {\n color: 'badge-success',\n text: 'Approved',\n ariaLabel: 'Approval: Approved by staff',\n },\n REJECTED: {\n color: 'badge-error',\n text: 'Rejected',\n ariaLabel: 'Approval: Rejected by staff',\n },\n INTERNAL: {\n color: 'badge-info',\n text: 'Internal',\n ariaLabel: 'Approval: Internal staff ticket',\n },\n};\n\nconst getApprovalConfig = (approvalStatus: SupportTicketApproval): ApprovalBadgeConfig => {\n const config = approvalConfig[approvalStatus];\n if (!config) {\n return {\n color: 'badge-neutral',\n text: approvalStatus || 'Unknown',\n ariaLabel: `Approval: ${approvalStatus || 'Unknown status'}`,\n };\n }\n return config;\n};\n\nconst config = computed(() => getApprovalConfig(props.approvalStatus));\n\nconst badgeClasses = computed(() => {\n const baseClasses = ['badge', 'text-xs'];\n\n // Add color class\n baseClasses.push(config.value.color);\n\n // Add size class with responsive text sizing\n if (props.size === 'sm') {\n baseClasses.push('badge-sm', 'text-xs');\n } else if (props.size === 'lg') {\n baseClasses.push('badge-lg', 'text-sm');\n } else {\n // md size - responsive text\n baseClasses.push('text-xs', 'sm:text-sm');\n }\n\n // Add variant class\n if (props.variant === 'outline') {\n baseClasses.push('badge-outline');\n }\n\n return baseClasses.join(' ');\n});\n\nconst displayText = computed(() => config.value.text);\nconst ariaLabel = computed(() => config.value.ariaLabel);\n</script>\n","/**\n * Takes an ISO date string and returns:\n * - formatted: \"Jan 15, 2025\"\n * - relative: \"3 days ago\" / \"2 hours ago\" / \"just now\"\n */\nexport function formatTicketDate(isoString: string): { formatted: string; relative: string } {\n const date = new Date(isoString);\n if (isNaN(date.getTime())) {\n return { formatted: isoString, relative: '' };\n }\n\n const formatted = date.toLocaleDateString(undefined, {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffSec = Math.floor(diffMs / 1000);\n const diffMin = Math.floor(diffSec / 60);\n const diffHour = Math.floor(diffMin / 60);\n const diffDay = Math.floor(diffHour / 24);\n\n let relative: string;\n if (Math.abs(diffSec) < 60) {\n relative = 'just now';\n } else if (diffSec < 0) {\n const sec = Math.abs(diffSec);\n const min = Math.floor(sec / 60);\n const hr = Math.floor(min / 60);\n const day = Math.floor(hr / 24);\n if (min < 60) relative = `in ${min} minute${min === 1 ? '' : 's'}`;\n else if (hr < 24) relative = `in ${hr} hour${hr === 1 ? '' : 's'}`;\n else relative = `in ${day} day${day === 1 ? '' : 's'}`;\n } else if (diffMin < 60) {\n relative = diffMin === 1 ? '1 minute ago' : `${diffMin} minutes ago`;\n } else if (diffHour < 24) {\n relative = diffHour === 1 ? '1 hour ago' : `${diffHour} hours ago`;\n } else if (diffDay < 30) {\n relative = diffDay === 1 ? '1 day ago' : `${diffDay} days ago`;\n } else {\n const diffWeeks = Math.floor(diffDay / 7);\n const diffMonths = Math.floor(diffDay / 30);\n if (diffWeeks < 4) {\n relative = diffWeeks === 1 ? '1 week ago' : `${diffWeeks} weeks ago`;\n } else if (diffMonths < 12) {\n relative = diffMonths === 1 ? '1 month ago' : `${diffMonths} months ago`;\n } else {\n const diffYears = Math.floor(diffMonths / 12);\n relative = diffYears === 1 ? '1 year ago' : `${diffYears} years ago`;\n }\n }\n\n return { formatted, relative };\n}\n","<template>\n <div\n :class=\"[\n 'card card-bordered p-4 w-full',\n variant === 'internal'\n ? 'bg-warning/10 border-warning/30'\n : 'bg-base-100',\n ]\"\n >\n <div class=\"flex items-center gap-2 mb-3 flex-wrap\">\n <span\n v-if=\"variant === 'internal'\"\n class=\"badge badge-warning badge-sm gap-1 shrink-0\"\n aria-label=\"Internal note\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"w-3.5 h-3.5\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M16.5 10.5V6.75a4.5 4.5 0 10-9 0v3.75m-.75 11.25h10.5a2.25 2.25 0 002.25-2.25v-6.75a2.25 2.25 0 00-2.25-2.25H6.75a2.25 2.25 0 00-2.25 2.25v6.75a2.25 2.25 0 002.25 2.25z\"\n />\n </svg>\n Internal\n </span>\n <span\n v-else\n class=\"w-6 h-6 rounded-full bg-primary text-primary-content text-xs flex items-center justify-center shrink-0\"\n aria-hidden=\"true\"\n >\n {{ authorInitial }}\n </span>\n <span class=\"font-semibold text-sm\">{{ authorName }}</span>\n <span class=\"text-base-content/50 text-sm\">· {{ relativeTime }}</span>\n </div>\n <div class=\"text-sm text-base-content break-words whitespace-pre-wrap\">\n <slot />\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { formatTicketDate } from '../utils/formatTicketDate';\n\ninterface Props {\n authorName: string;\n createdAt: string;\n variant: 'customer' | 'internal';\n}\n\nconst props = defineProps<Props>();\n\nconst authorInitial = computed(() => {\n const name = props.authorName?.trim() || '?';\n return name.charAt(0).toUpperCase();\n});\n\nconst relativeTime = computed(() => {\n return props.createdAt ? formatTicketDate(props.createdAt).relative : '';\n});\n</script>\n","<template>\n <div class=\"py-2 flex items-start gap-2 text-sm\">\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"w-4 h-4 text-base-content/30 shrink-0 mt-0.5\"\n aria-hidden=\"true\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124a6.57 6.57 0 01.22-.128c.332-.183.582-.495.644-.869l.214-1.281z\"\n />\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M15 12a3 3 0 11-6 0 3 3 0 016 0z\"\n />\n </svg>\n <div class=\"flex-1 min-w-0\">\n <span class=\"font-medium text-base-content/70\">{{ author }}</span>\n <span class=\"text-base-content/50 ml-1\">\n <template v-if=\"oldValue && newValue\">\n {{ message }}\n <span class=\"line-through text-base-content/40 ml-1\">{{ oldValue }}</span>\n <span class=\"mx-1\">→</span>\n <span>{{ newValue }}</span>\n </template>\n <template v-else-if=\"newValue\">\n {{ message }}\n <span class=\"ml-1\">{{ newValue }}</span>\n </template>\n <template v-else>\n {{ message }}\n </template>\n </span>\n <span class=\"text-base-content/30 text-xs\"> · {{ relativeTime }}</span>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed } from 'vue';\nimport { formatTicketDate } from '../utils/formatTicketDate';\n\ninterface Props {\n author: string;\n message: string;\n timestamp: string;\n oldValue?: string | null;\n newValue?: string | null;\n}\n\nconst props = defineProps<Props>();\n\nconst relativeTime = computed(() => {\n return props.timestamp ? formatTicketDate(props.timestamp).relative : '';\n});\n</script>\n"],"mappings":"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"}
|