@webority/mobile-support 0.0.12 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/lib/commonjs/SupportProvider.js +1 -335
  2. package/lib/commonjs/client.js +1 -164
  3. package/lib/commonjs/clipboard.js +1 -10
  4. package/lib/commonjs/components/Attachments.js +1 -246
  5. package/lib/commonjs/components/NewSupportTicket.js +1 -210
  6. package/lib/commonjs/components/PhotoTray.js +1 -141
  7. package/lib/commonjs/components/SupportContact.js +1 -134
  8. package/lib/commonjs/components/SupportTicketDetail.js +1 -348
  9. package/lib/commonjs/components/SupportTicketList.js +1 -279
  10. package/lib/commonjs/device.js +1 -29
  11. package/lib/commonjs/files.js +1 -37
  12. package/lib/commonjs/format.js +1 -29
  13. package/lib/commonjs/index.js +1 -97
  14. package/lib/commonjs/photos.js +1 -67
  15. package/lib/commonjs/report/ReportIssueButton.js +1 -57
  16. package/lib/commonjs/report/captureAndReport.js +1 -34
  17. package/lib/commonjs/report/index.js +1 -20
  18. package/lib/commonjs/share/index.js +1 -44
  19. package/lib/commonjs/types.js +1 -2
  20. package/lib/commonjs/validation.js +1 -45
  21. package/lib/module/SupportProvider.js +1 -323
  22. package/lib/module/client.js +1 -156
  23. package/lib/module/clipboard.js +1 -5
  24. package/lib/module/components/Attachments.js +1 -240
  25. package/lib/module/components/NewSupportTicket.js +1 -205
  26. package/lib/module/components/PhotoTray.js +1 -136
  27. package/lib/module/components/SupportContact.js +1 -129
  28. package/lib/module/components/SupportTicketDetail.js +1 -343
  29. package/lib/module/components/SupportTicketList.js +1 -273
  30. package/lib/module/device.js +1 -24
  31. package/lib/module/files.js +1 -31
  32. package/lib/module/format.js +1 -23
  33. package/lib/module/index.js +1 -10
  34. package/lib/module/photos.js +1 -61
  35. package/lib/module/report/ReportIssueButton.js +1 -52
  36. package/lib/module/report/captureAndReport.js +1 -29
  37. package/lib/module/report/index.js +1 -12
  38. package/lib/module/share/index.js +1 -37
  39. package/lib/module/types.js +0 -2
  40. package/lib/module/validation.js +1 -38
  41. package/lib/typescript/commonjs/SupportProvider.d.ts +4 -0
  42. package/lib/typescript/commonjs/client.d.ts +0 -1
  43. package/lib/typescript/commonjs/format.d.ts +6 -4
  44. package/lib/typescript/module/SupportProvider.d.ts +4 -0
  45. package/lib/typescript/module/client.d.ts +0 -1
  46. package/lib/typescript/module/format.d.ts +6 -4
  47. package/package.json +8 -6
@@ -1,129 +1 @@
1
- "use strict";
2
-
3
- import { Button, Card, Spinner, Text, toast, useTheme } from '@webority/mobile-ui';
4
- import { useMemo } from 'react';
5
- import { Linking, StyleSheet, View } from 'react-native';
6
- import { copyText } from "../clipboard.js";
7
- import { useSupportConfig } from "../SupportProvider.js";
8
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
9
- /** How to reach the support team: email with copy and compose, phone, and hours. */
10
- export const SupportContact = ({
11
- testID = 'support-contact'
12
- }) => {
13
- const theme = useTheme();
14
- const styles = useMemo(() => buildStyles(theme), [theme]);
15
- const {
16
- config,
17
- loading,
18
- error
19
- } = useSupportConfig();
20
- if (!config) {
21
- return /*#__PURE__*/_jsx(Card, {
22
- variant: "outlined",
23
- testID: testID,
24
- children: loading ? /*#__PURE__*/_jsx(Spinner, {
25
- size: "sm",
26
- accessibilityLabel: "Loading contact details"
27
- }) : /*#__PURE__*/_jsx(Text, {
28
- variant: "bodySmall",
29
- color: "error",
30
- children: error?.message ?? 'Contact details are not available right now.'
31
- })
32
- });
33
- }
34
- const email = config.supportEmail;
35
- const phone = config.supportPhone?.trim();
36
- const hours = config.supportHours?.trim();
37
- const copyEmail = async () => {
38
- try {
39
- await copyText(email);
40
- toast.success('Email address copied.');
41
- } catch {
42
- toast.error('Could not copy the email address.');
43
- }
44
- };
45
- const open = (url, failure) => {
46
- Linking.openURL(url).catch(() => toast.error(failure));
47
- };
48
- return /*#__PURE__*/_jsxs(Card, {
49
- variant: "outlined",
50
- bodyStyle: styles.body,
51
- testID: testID,
52
- children: [/*#__PURE__*/_jsx(Text, {
53
- variant: "title",
54
- accessibilityRole: "header",
55
- children: `Contact ${config.supportName || config.productName}`
56
- }), /*#__PURE__*/_jsxs(View, {
57
- style: styles.row,
58
- children: [/*#__PURE__*/_jsx(Text, {
59
- variant: "label",
60
- color: "secondary",
61
- children: "Email"
62
- }), /*#__PURE__*/_jsx(Text, {
63
- variant: "body",
64
- selectable: true,
65
- testID: "support-contact-email",
66
- children: email || '-'
67
- })]
68
- }), email ? /*#__PURE__*/_jsxs(View, {
69
- style: styles.actions,
70
- children: [/*#__PURE__*/_jsx(Button, {
71
- title: "Copy",
72
- size: "sm",
73
- variant: "outline",
74
- tone: "neutral",
75
- leftIcon: "copy",
76
- accessibilityLabel: `Copy ${email}`,
77
- onPress: () => void copyEmail(),
78
- testID: "support-contact-copy"
79
- }), /*#__PURE__*/_jsx(Button, {
80
- title: "Send email",
81
- size: "sm",
82
- variant: "outline",
83
- leftIcon: "mail",
84
- accessibilityLabel: `Send an email to ${email}`,
85
- onPress: () => open(`mailto:${email}`, 'No email app is set up on this device.'),
86
- testID: "support-contact-mail"
87
- })]
88
- }) : null, phone ? /*#__PURE__*/_jsxs(View, {
89
- style: styles.row,
90
- children: [/*#__PURE__*/_jsx(Text, {
91
- variant: "label",
92
- color: "secondary",
93
- children: "Phone"
94
- }), /*#__PURE__*/_jsx(Button, {
95
- title: phone,
96
- size: "sm",
97
- variant: "link",
98
- leftIcon: "call",
99
- accessibilityLabel: `Call ${phone}`,
100
- onPress: () => open(`tel:${phone.replace(/[^\d+]/g, '')}`, 'This device cannot make calls.'),
101
- testID: "support-contact-phone"
102
- })]
103
- }) : null, hours ? /*#__PURE__*/_jsxs(View, {
104
- style: styles.row,
105
- children: [/*#__PURE__*/_jsx(Text, {
106
- variant: "label",
107
- color: "secondary",
108
- children: "Hours"
109
- }), /*#__PURE__*/_jsx(Text, {
110
- variant: "bodySmall",
111
- children: hours
112
- })]
113
- }) : null]
114
- });
115
- };
116
- const buildStyles = theme => StyleSheet.create({
117
- body: {
118
- gap: theme.spacing.sm
119
- },
120
- row: {
121
- gap: theme.spacing.xxs,
122
- alignItems: 'flex-start'
123
- },
124
- actions: {
125
- flexDirection: 'row',
126
- gap: theme.spacing.sm
127
- }
128
- });
129
- //# sourceMappingURL=SupportContact.js.map
1
+ import{Button as t,Card as e,Spinner as a,Text as o,toast as r,useTheme as i}from"@webority/mobile-ui";import{useMemo as l}from"react";import{Linking as s,StyleSheet as n,View as c}from"react-native";import{copyText as p}from"../clipboard.js";import{useSupportConfig as d}from"../SupportProvider.js";import{jsx as m,jsxs as u}from"react/jsx-runtime";export const SupportContact=({testID:n="support-contact"})=>{const y=i(),b=l(()=>buildStyles(y),[y]),{config:h,loading:v,error:f}=d();if(!h)return m(e,{variant:"outlined",testID:n,children:v?m(a,{size:"sm",accessibilityLabel:"Loading contact details"}):m(o,{variant:"bodySmall",color:"error",children:f?.message??"Contact details are not available right now."})});const g=h.supportEmail,I=h.supportPhone?.trim(),w=h.supportHours?.trim(),open=(t,e)=>{s.openURL(t).catch(()=>r.error(e))};return u(e,{variant:"outlined",bodyStyle:b.body,testID:n,children:[m(o,{variant:"title",accessibilityRole:"header",children:`Contact ${h.supportName||h.productName}`}),u(c,{style:b.row,children:[m(o,{variant:"label",color:"secondary",children:"Email"}),m(o,{variant:"body",selectable:!0,testID:"support-contact-email",children:g||"-"})]}),g?u(c,{style:b.actions,children:[m(t,{title:"Copy",size:"sm",variant:"outline",tone:"neutral",leftIcon:"copy",accessibilityLabel:`Copy ${g}`,onPress:()=>{(async()=>{try{await p(g),r.success("Email address copied.")}catch{r.error("Could not copy the email address.")}})()},testID:"support-contact-copy"}),m(t,{title:"Send email",size:"sm",variant:"outline",leftIcon:"mail",accessibilityLabel:`Send an email to ${g}`,onPress:()=>open(`mailto:${g}`,"No email app is set up on this device."),testID:"support-contact-mail"})]}):null,I?u(c,{style:b.row,children:[m(o,{variant:"label",color:"secondary",children:"Phone"}),m(t,{title:I,size:"sm",variant:"link",leftIcon:"call",accessibilityLabel:`Call ${I}`,onPress:()=>open(`tel:${I.replace(/[^\d+]/g,"")}`,"This device cannot make calls."),testID:"support-contact-phone"})]}):null,w?u(c,{style:b.row,children:[m(o,{variant:"label",color:"secondary",children:"Hours"}),m(o,{variant:"bodySmall",children:w})]}):null]})};const buildStyles=t=>n.create({body:{gap:t.spacing.sm},row:{gap:t.spacing.xxs,alignItems:"flex-start"},actions:{flexDirection:"row",gap:t.spacing.sm}});
@@ -1,343 +1 @@
1
- "use strict";
2
-
3
- import { Button, Card, Input, Spinner, StatusBadge, Text, useTheme } from '@webority/mobile-ui';
4
- import { useMemo, useState } from 'react';
5
- import { ScrollView, StyleSheet, View } from 'react-native';
6
- import { formatWhen } from "../format.js";
7
- import { useSupportConfig, useSupportTicket } from "../SupportProvider.js";
8
- import { validateBody, validateFiles } from "../validation.js";
9
- import { AttachmentList, ImageViewer } from "./Attachments.js";
10
- import { PhotoTray } from "./PhotoTray.js";
11
- import { STATUS_TONES } from "./SupportTicketList.js";
12
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
- const MessageBubble = ({
14
- message,
15
- styles,
16
- onOpenImage
17
- }) => {
18
- if (message.authorKind === 'System') {
19
- return /*#__PURE__*/_jsx(View, {
20
- style: styles.system,
21
- testID: `support-message-${message.id}`,
22
- children: /*#__PURE__*/_jsx(Text, {
23
- variant: "caption",
24
- color: "tertiary",
25
- align: "center",
26
- children: `${message.body}, ${formatWhen(message.createdDateTimeUtc)}`
27
- })
28
- });
29
- }
30
- const mine = message.authorKind === 'Customer';
31
- return /*#__PURE__*/_jsx(View, {
32
- style: [styles.bubbleRow, mine ? styles.mineRow : styles.theirsRow],
33
- testID: `support-message-${message.id}`,
34
- children: /*#__PURE__*/_jsxs(View, {
35
- style: [styles.bubble, mine ? styles.mine : styles.theirs],
36
- children: [/*#__PURE__*/_jsx(Text, {
37
- variant: "label",
38
- color: mine ? 'primary' : 'secondary',
39
- children: message.authorName ?? '-'
40
- }), /*#__PURE__*/_jsx(Text, {
41
- variant: "body",
42
- selectable: true,
43
- children: message.body
44
- }), /*#__PURE__*/_jsx(AttachmentList, {
45
- attachments: message.attachments,
46
- onOpenImage: onOpenImage
47
- }), /*#__PURE__*/_jsx(Text, {
48
- variant: "caption",
49
- color: "tertiary",
50
- children: formatWhen(message.createdDateTimeUtc)
51
- })]
52
- })
53
- });
54
- };
55
- const SATISFACTION_TEXT = {
56
- Satisfied: 'You said we handled this well.',
57
- Unsatisfied: 'You said we did not handle this well.'
58
- };
59
- export const SupportTicketDetail = ({
60
- ticketId,
61
- testID = 'support-ticket-detail'
62
- }) => {
63
- const theme = useTheme();
64
- const styles = useMemo(() => buildStyles(theme), [theme]);
65
- const {
66
- config
67
- } = useSupportConfig();
68
- const {
69
- ticket,
70
- loading,
71
- error,
72
- actionError,
73
- busy,
74
- reload,
75
- reply,
76
- reopen,
77
- rate
78
- } = useSupportTicket(ticketId);
79
- const [viewing, setViewing] = useState(null);
80
- const [draft, setDraft] = useState('');
81
- const [photos, setPhotos] = useState([]);
82
- const [replyError, setReplyError] = useState(null);
83
- const [choice, setChoice] = useState(null);
84
- const [comment, setComment] = useState('');
85
- if (!ticket) {
86
- return /*#__PURE__*/_jsx(View, {
87
- style: styles.center,
88
- testID: testID,
89
- children: loading ? /*#__PURE__*/_jsx(Spinner, {
90
- accessibilityLabel: "Loading the ticket"
91
- }) : /*#__PURE__*/_jsxs(_Fragment, {
92
- children: [/*#__PURE__*/_jsx(Text, {
93
- variant: "body",
94
- color: "error",
95
- align: "center",
96
- children: error?.message ?? 'That ticket could not be found.'
97
- }), /*#__PURE__*/_jsx(Button, {
98
- title: "Try again",
99
- variant: "ghost",
100
- onPress: () => void reload()
101
- })]
102
- })
103
- });
104
- }
105
- const sendReply = async () => {
106
- const body = draft.trim();
107
- // Without the config the server still enforces its limits; only emptiness is checked here.
108
- const problem = config ? validateBody(body, config) ?? validateFiles(photos, config) : body ? undefined : 'Write a message.';
109
- if (problem) {
110
- setReplyError(problem);
111
- return;
112
- }
113
- setReplyError(null);
114
- if (await reply({
115
- body,
116
- files: photos
117
- })) {
118
- setDraft('');
119
- setPhotos([]);
120
- }
121
- };
122
- const sendRating = async () => {
123
- if (!choice) {
124
- return;
125
- }
126
- if (await rate({
127
- satisfaction: choice,
128
- comment
129
- })) {
130
- setChoice(null);
131
- setComment('');
132
- }
133
- };
134
- const visible = ticket.messages.filter(m => !m.isInternal);
135
- return /*#__PURE__*/_jsxs(View, {
136
- style: styles.root,
137
- testID: testID,
138
- children: [/*#__PURE__*/_jsxs(ScrollView, {
139
- contentContainerStyle: styles.content,
140
- keyboardShouldPersistTaps: "handled",
141
- children: [/*#__PURE__*/_jsxs(View, {
142
- style: styles.head,
143
- children: [/*#__PURE__*/_jsx(Text, {
144
- variant: "h3",
145
- accessibilityRole: "header",
146
- children: ticket.subject
147
- }), /*#__PURE__*/_jsx(Text, {
148
- variant: "bodySmall",
149
- color: "secondary",
150
- children: `${ticket.number}, ${ticket.category}`
151
- }), /*#__PURE__*/_jsxs(View, {
152
- style: styles.headRow,
153
- children: [/*#__PURE__*/_jsx(StatusBadge, {
154
- label: ticket.statusLabel,
155
- tone: STATUS_TONES[ticket.status] ?? 'neutral',
156
- accessibilityLabel: `Status: ${ticket.statusLabel}`
157
- }), /*#__PURE__*/_jsx(Text, {
158
- variant: "caption",
159
- color: "tertiary",
160
- children: `Opened ${formatWhen(ticket.createdDateTimeUtc)}`
161
- })]
162
- })]
163
- }), ticket.canReopen && /*#__PURE__*/_jsxs(Card, {
164
- variant: "outlined",
165
- bodyStyle: styles.section,
166
- children: [/*#__PURE__*/_jsx(Text, {
167
- variant: "bodySmall",
168
- children: `This ticket is ${ticket.statusLabel}. Reopen it if you still need help.`
169
- }), /*#__PURE__*/_jsx(Button, {
170
- title: "Reopen ticket",
171
- variant: "outline",
172
- size: "sm",
173
- loading: busy,
174
- disabled: busy,
175
- onPress: () => void reopen(),
176
- testID: "support-reopen"
177
- })]
178
- }), ticket.canRate && /*#__PURE__*/_jsxs(Card, {
179
- variant: "outlined",
180
- bodyStyle: styles.section,
181
- testID: "support-rating",
182
- children: [/*#__PURE__*/_jsx(Text, {
183
- variant: "label",
184
- children: "How did we do?"
185
- }), ticket.satisfaction && /*#__PURE__*/_jsx(Text, {
186
- variant: "bodySmall",
187
- color: "secondary",
188
- children: SATISFACTION_TEXT[ticket.satisfaction]
189
- }), /*#__PURE__*/_jsxs(View, {
190
- style: styles.ratingRow,
191
- children: [/*#__PURE__*/_jsx(Button, {
192
- title: "Satisfied",
193
- size: "sm",
194
- leftIcon: "like",
195
- variant: choice === 'Satisfied' ? 'solid' : 'outline',
196
- tone: "success",
197
- accessibilityState: {
198
- selected: choice === 'Satisfied'
199
- },
200
- onPress: () => setChoice('Satisfied'),
201
- testID: "support-rate-satisfied"
202
- }), /*#__PURE__*/_jsx(Button, {
203
- title: "Not satisfied",
204
- size: "sm",
205
- leftIcon: "dislike",
206
- variant: choice === 'Unsatisfied' ? 'solid' : 'outline',
207
- tone: "error",
208
- accessibilityState: {
209
- selected: choice === 'Unsatisfied'
210
- },
211
- onPress: () => setChoice('Unsatisfied'),
212
- testID: "support-rate-unsatisfied"
213
- })]
214
- }), choice && /*#__PURE__*/_jsxs(_Fragment, {
215
- children: [/*#__PURE__*/_jsx(Input, {
216
- label: "Anything to add? (optional)",
217
- value: comment,
218
- onChangeText: setComment,
219
- multiline: true,
220
- maxLength: config?.satisfactionCommentMaxLength,
221
- testID: "support-rating-comment"
222
- }), /*#__PURE__*/_jsx(Button, {
223
- title: "Send rating",
224
- size: "sm",
225
- loading: busy,
226
- disabled: busy,
227
- onPress: () => void sendRating(),
228
- testID: "support-rating-send"
229
- })]
230
- })]
231
- }), /*#__PURE__*/_jsx(View, {
232
- style: styles.thread,
233
- accessibilityLabel: "Conversation",
234
- children: visible.map(m => /*#__PURE__*/_jsx(MessageBubble, {
235
- message: m,
236
- styles: styles,
237
- onOpenImage: (uri, name) => setViewing({
238
- uri,
239
- name
240
- })
241
- }, m.id))
242
- }), /*#__PURE__*/_jsxs(View, {
243
- style: styles.section,
244
- children: [/*#__PURE__*/_jsx(Input, {
245
- label: "Your reply",
246
- placeholder: "Write a reply to the support team",
247
- value: draft,
248
- onChangeText: setDraft,
249
- multiline: true,
250
- maxLength: config?.bodyMaxLength,
251
- error: replyError ?? undefined,
252
- testID: "support-reply-input"
253
- }), config && /*#__PURE__*/_jsx(PhotoTray, {
254
- photos: photos,
255
- onChange: setPhotos,
256
- remaining: config.maxAttachmentsPerMessage - photos.length,
257
- maxBytes: config.maxAttachmentBytes,
258
- disabled: busy
259
- }), ticket.canReopen && /*#__PURE__*/_jsx(Text, {
260
- variant: "caption",
261
- color: "secondary",
262
- children: "Replying reopens this ticket."
263
- }), actionError && /*#__PURE__*/_jsx(Text, {
264
- variant: "bodySmall",
265
- color: "error",
266
- accessibilityLiveRegion: "polite",
267
- children: actionError.message
268
- }), /*#__PURE__*/_jsx(Button, {
269
- title: "Send reply",
270
- leftIcon: "send",
271
- loading: busy,
272
- disabled: busy,
273
- onPress: () => void sendReply(),
274
- testID: "support-reply-send"
275
- })]
276
- })]
277
- }), /*#__PURE__*/_jsx(ImageViewer, {
278
- image: viewing,
279
- onClose: () => setViewing(null)
280
- })]
281
- });
282
- };
283
- const buildStyles = theme => StyleSheet.create({
284
- root: {
285
- flex: 1
286
- },
287
- content: {
288
- padding: theme.spacing.md,
289
- gap: theme.spacing.md
290
- },
291
- center: {
292
- flex: 1,
293
- alignItems: 'center',
294
- justifyContent: 'center',
295
- padding: theme.spacing.xl,
296
- gap: theme.spacing.sm
297
- },
298
- head: {
299
- gap: theme.spacing.xxs
300
- },
301
- headRow: {
302
- flexDirection: 'row',
303
- alignItems: 'center',
304
- gap: theme.spacing.sm,
305
- marginTop: theme.spacing.xs
306
- },
307
- section: {
308
- gap: theme.spacing.sm
309
- },
310
- ratingRow: {
311
- flexDirection: 'row',
312
- gap: theme.spacing.sm
313
- },
314
- thread: {
315
- gap: theme.spacing.sm
316
- },
317
- system: {
318
- paddingHorizontal: theme.spacing.lg,
319
- paddingVertical: theme.spacing.xxs
320
- },
321
- bubbleRow: {
322
- flexDirection: 'row'
323
- },
324
- mineRow: {
325
- justifyContent: 'flex-end'
326
- },
327
- theirsRow: {
328
- justifyContent: 'flex-start'
329
- },
330
- bubble: {
331
- maxWidth: '85%',
332
- borderRadius: theme.radius.lg,
333
- padding: theme.spacing.sm,
334
- gap: theme.spacing.xxs
335
- },
336
- mine: {
337
- backgroundColor: theme.colors.primaryMuted
338
- },
339
- theirs: {
340
- backgroundColor: theme.colors.background.secondary
341
- }
342
- });
343
- //# sourceMappingURL=SupportTicketDetail.js.map
1
+ import{Button as e,Card as t,Input as i,Spinner as a,StatusBadge as n,Text as s,useTheme as r}from"@webority/mobile-ui";import{useMemo as o,useState as l}from"react";import{ScrollView as c,StyleSheet as d,View as p}from"react-native";import{formatWhen as m}from"../format.js";import{useSupportConfig as g,useSupportTicket as h,useSupportTimeZone as u}from"../SupportProvider.js";import{validateBody as y,validateFiles as b}from"../validation.js";import{AttachmentList as f,ImageViewer as x}from"./Attachments.js";import{PhotoTray as v}from"./PhotoTray.js";import{STATUS_TONES as I}from"./SupportTicketList.js";import{jsx as w,jsxs as S,Fragment as D}from"react/jsx-runtime";const MessageBubble=({message:e,styles:t,timeZone:i,onOpenImage:a})=>{if("System"===e.authorKind)return w(p,{style:t.system,testID:`support-message-${e.id}`,children:w(s,{variant:"caption",color:"tertiary",align:"center",children:`${e.body}, ${m(e.createdDateTimeUtc,i)}`})});const n="Customer"===e.authorKind;return w(p,{style:[t.bubbleRow,n?t.mineRow:t.theirsRow],testID:`support-message-${e.id}`,children:S(p,{style:[t.bubble,n?t.mine:t.theirs],children:[w(s,{variant:"label",color:n?"primary":"secondary",children:e.authorName??"-"}),w(s,{variant:"body",selectable:!0,children:e.body}),w(f,{attachments:e.attachments,onOpenImage:a}),w(s,{variant:"caption",color:"tertiary",children:m(e.createdDateTimeUtc,i)})]})})},R={Satisfied:"You said we handled this well.",Unsatisfied:"You said we did not handle this well."};export const SupportTicketDetail=({ticketId:d,testID:f="support-ticket-detail"})=>{const k=r(),j=o(()=>buildStyles(k),[k]),{config:C}=g(),L=u(),{ticket:T,loading:P,error:$,actionError:U,busy:z,reload:A,reply:M,reopen:O,rate:W}=h(d),[Y,B]=l(null),[H,K]=l(""),[N,Z]=l([]),[E,F]=l(null),[V,q]=l(null),[G,J]=l("");if(!T)return w(p,{style:j.center,testID:f,children:P?w(a,{accessibilityLabel:"Loading the ticket"}):S(D,{children:[w(s,{variant:"body",color:"error",align:"center",children:$?.message??"That ticket could not be found."}),w(e,{title:"Try again",variant:"ghost",onPress:()=>{A()}})]})});const Q=T.messages.filter(e=>!e.isInternal);return S(p,{style:j.root,testID:f,children:[S(c,{contentContainerStyle:j.content,keyboardShouldPersistTaps:"handled",children:[S(p,{style:j.head,children:[w(s,{variant:"h3",accessibilityRole:"header",children:T.subject}),w(s,{variant:"bodySmall",color:"secondary",children:`${T.number}, ${T.category}`}),S(p,{style:j.headRow,children:[w(n,{label:T.statusLabel,tone:I[T.status]??"neutral",accessibilityLabel:`Status: ${T.statusLabel}`}),w(s,{variant:"caption",color:"tertiary",children:`Opened ${m(T.createdDateTimeUtc,L)}`})]})]}),T.canReopen&&S(t,{variant:"outlined",bodyStyle:j.section,children:[w(s,{variant:"bodySmall",children:`This ticket is ${T.statusLabel}. Reopen it if you still need help.`}),w(e,{title:"Reopen ticket",variant:"outline",size:"sm",loading:z,disabled:z,onPress:()=>{O()},testID:"support-reopen"})]}),T.canRate&&S(t,{variant:"outlined",bodyStyle:j.section,testID:"support-rating",children:[w(s,{variant:"label",children:"How did we do?"}),T.satisfaction&&w(s,{variant:"bodySmall",color:"secondary",children:R[T.satisfaction]}),S(p,{style:j.ratingRow,children:[w(e,{title:"Satisfied",size:"sm",leftIcon:"like",variant:"Satisfied"===V?"solid":"outline",tone:"success",accessibilityState:{selected:"Satisfied"===V},onPress:()=>q("Satisfied"),testID:"support-rate-satisfied"}),w(e,{title:"Not satisfied",size:"sm",leftIcon:"dislike",variant:"Unsatisfied"===V?"solid":"outline",tone:"error",accessibilityState:{selected:"Unsatisfied"===V},onPress:()=>q("Unsatisfied"),testID:"support-rate-unsatisfied"})]}),V&&S(D,{children:[w(i,{label:"Anything to add? (optional)",value:G,onChangeText:J,multiline:!0,maxLength:C?.satisfactionCommentMaxLength,testID:"support-rating-comment"}),w(e,{title:"Send rating",size:"sm",loading:z,disabled:z,onPress:()=>{(async()=>{V&&await W({satisfaction:V,comment:G})&&(q(null),J(""))})()},testID:"support-rating-send"})]})]}),w(p,{style:j.thread,accessibilityLabel:"Conversation",children:Q.map(e=>w(MessageBubble,{message:e,styles:j,timeZone:L,onOpenImage:(e,t)=>B({uri:e,name:t})},e.id))}),S(p,{style:j.section,children:[w(i,{label:"Your reply",placeholder:"Write a reply to the support team",value:H,onChangeText:K,multiline:!0,maxLength:C?.bodyMaxLength,error:E??void 0,testID:"support-reply-input"}),C&&w(v,{photos:N,onChange:Z,remaining:C.maxAttachmentsPerMessage-N.length,maxBytes:C.maxAttachmentBytes,disabled:z}),T.canReopen&&w(s,{variant:"caption",color:"secondary",children:"Replying reopens this ticket."}),U&&w(s,{variant:"bodySmall",color:"error",accessibilityLiveRegion:"polite",children:U.message}),w(e,{title:"Send reply",leftIcon:"send",loading:z,disabled:z,onPress:()=>{(async()=>{const e=H.trim(),t=C?y(e,C)??b(N,C):e?void 0:"Write a message.";t?F(t):(F(null),await M({body:e,files:N})&&(K(""),Z([])))})()},testID:"support-reply-send"})]})]}),w(x,{image:Y,onClose:()=>B(null)})]})};const buildStyles=e=>d.create({root:{flex:1},content:{padding:e.spacing.md,gap:e.spacing.md},center:{flex:1,alignItems:"center",justifyContent:"center",padding:e.spacing.xl,gap:e.spacing.sm},head:{gap:e.spacing.xxs},headRow:{flexDirection:"row",alignItems:"center",gap:e.spacing.sm,marginTop:e.spacing.xs},section:{gap:e.spacing.sm},ratingRow:{flexDirection:"row",gap:e.spacing.sm},thread:{gap:e.spacing.sm},system:{paddingHorizontal:e.spacing.lg,paddingVertical:e.spacing.xxs},bubbleRow:{flexDirection:"row"},mineRow:{justifyContent:"flex-end"},theirsRow:{justifyContent:"flex-start"},bubble:{maxWidth:"85%",borderRadius:e.radius.lg,padding:e.spacing.sm,gap:e.spacing.xxs},mine:{backgroundColor:e.colors.primaryMuted},theirs:{backgroundColor:e.colors.background.secondary}});