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