@webority/support-react 0.68.3 → 0.70.1

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/README.md CHANGED
@@ -40,6 +40,13 @@ export function App() {
40
40
  `SupportProvider` also takes `timeZone`, the IANA zone every ticket and message time is shown
41
41
  in. It defaults to `Asia/Kolkata`; times read `06-Oct-2026, 1:10 PM`.
42
42
 
43
+ `SupportProvider` also takes `renderTime`, a function that draws every ticket and message time from its ISO string instead of the built-in date and time. When it is set, the "Times are shown in" note is left out, because the product's own rendering decides the zone.
44
+
45
+ ```jsx
46
+ <SupportProvider client={client} renderTime={(iso) => <ScreenTime value={iso} />}>
47
+ </SupportProvider>
48
+ ```
49
+
43
50
  `createSupportClient(http, basePath = "/api/support")` accepts any axios-like instance with
44
51
  `get(url, config)` and `post(url, data, config)` that resolve to `{ data }`. The second argument
45
52
  is the base path as a string, or as `{ base }` the way `createChatClient(http, { base })` takes
@@ -1 +1 @@
1
- import{createContext as e,useCallback as t,useContext as n,useEffect as r,useId as i,useMemo as a,useRef as o,useState as s}from"react";import{AppActionIcon as c,AppAlert as l,AppAvatar as u,AppBadge as d,AppButton as f,AppCard as p,AppCopyButton as m,AppDataTable as h,AppFileAttachment as g,AppIcon as _,AppInfoList as v,AppInput as y,AppLink as b,AppModal as x,AppSearch as S,AppSelect as C,AppSkeleton as w,AppSpinner as T,AppStatusBadge as E,AppTableEmptyState as D,AppTabs as O,AppTextarea as k,useFloatingStack as A}from"@webority/ui-react";import{toBlob as j}from"html-to-image";import{createPortal as ee}from"react-dom";import{Fragment as te,jsx as M,jsxs as N}from"react/jsx-runtime";var ne=`Something went wrong. Please try again.`,P=class extends Error{constructor(e,t,n){super(t),this.name=`SupportApiError`,this.code=e,this.status=n}};async function F(e){if(e&&typeof Blob<`u`&&e instanceof Blob)try{return JSON.parse(await e.text())}catch{return null}if(typeof e==`string`)try{return JSON.parse(e)}catch{return null}return e??null}async function I(e){if(e instanceof P)return e;let t=e?.response;if(!t)return new P(`network`,ne);let n=await F(t.data);return new P(typeof n?.code==`string`&&n.code?n.code:`unknown`,typeof n?.message==`string`&&n.message?n.message:ne,t.status)}async function L(e){try{return(await e()).data}catch(e){throw await I(e)}}function R(e,t,n){n!=null&&String(n).trim()!==``&&e.append(t,String(n))}function z(e,t,n,r){(n??[]).forEach((n,i)=>{let a=n?.name||`${r}-${i+1}.png`;e.append(t,n,a)})}function B(e,t=`/api/support`){let n=(typeof t==`string`?t:t?.base??`/api/support`).replace(/\/+$/,``),r=e=>`${n}/tickets/${encodeURIComponent(e)}`;return{getConfig:()=>L(()=>e.get(`${n}/settings`)),listTickets:({status:t,search:r,page:i,pageSize:a}={})=>{let o=new URLSearchParams;R(o,`status`,t),R(o,`search`,r?.trim()),R(o,`page`,i),R(o,`pageSize`,a);let s=o.toString();return L(()=>e.get(`${n}/tickets${s?`?${s}`:``}`))},getTicketCounts:()=>L(()=>e.get(`${n}/tickets/counts`)),getTicket:t=>L(()=>e.get(r(t))),createTicket:({category:t,subject:r,body:i,pageUrl:a,userAgent:o,appVersion:s,files:c,screenshots:l})=>{let u=new FormData;return u.append(`category`,t??``),u.append(`subject`,r??``),u.append(`body`,i??``),R(u,`pageUrl`,a),R(u,`userAgent`,o),R(u,`appVersion`,s),z(u,`files`,c,`file`),z(u,`screenshots`,l,`screenshot`),L(()=>e.post(`${n}/tickets`,u))},reply:(t,{body:n,files:i,screenshots:a})=>{let o=new FormData;return o.append(`body`,n??``),z(o,`files`,i,`file`),z(o,`screenshots`,a,`screenshot`),L(()=>e.post(`${r(t)}/replies`,o))},reopen:t=>L(()=>e.post(`${r(t)}/reopen`)),rate:(t,{satisfaction:n,comment:i})=>{let a={satisfaction:n};return i!=null&&i.trim()!==``&&(a.comment=i.trim()),L(()=>e.post(`${r(t)}/rating`,a))},fetchAttachment:t=>L(()=>e.get(`${n}/attachments/${encodeURIComponent(t)}`,{responseType:`blob`})),confirmAction:t=>L(()=>e.post(`${n}/actions/confirm`,{token:t}))}}var V=e(null);function H(){let e=n(V);if(!e)throw Error(`Support components and hooks must be used inside <SupportProvider>.`);return e}function U(){return H().client}function W(){return H().timeZone}function G(e,n,i=!0){let[a,c]=s({data:null,loading:i,error:null}),[l,u]=s(0),d=o(e);d.current=e;let f=o(n);r(()=>{let e=f.current!==n;if(f.current=n,!i){c({data:null,loading:!1,error:null});return}let t=!0;return c(t=>({data:e?null:t.data,loading:!0,error:null})),d.current(l>0).then(e=>t&&c({data:e,loading:!1,error:null}),e=>t&&c(t=>({...t,loading:!1,error:e}))),()=>{t=!1}},[n,i,l]);let p=t(()=>u(e=>e+1),[]),m=t(e=>c(t=>({...t,data:e})),[]);return{...a,reload:p,setData:m}}function K(){let{loadConfig:e}=H(),{data:t,loading:n,error:r,reload:i}=G(e,`config`);return{config:t,loading:n,error:r,reload:i}}function re(e={}){let t=U(),{status:n,search:r,page:i,pageSize:a}=e,{data:o,loading:s,error:c,reload:l}=G(()=>t.listTickets({status:n,search:r,page:i,pageSize:a}),JSON.stringify([`tickets`,n??null,r??null,i??null,a??null]));return{tickets:o?.items??[],totalCount:o?.totalCount??0,totalPages:o?.totalPages??0,loading:s,error:c,reload:l}}function ie(){let e=U(),{data:t,loading:n,error:r,reload:i}=G(()=>e.getTicketCounts(),`ticket-counts`);return{counts:t,loading:n,error:r,reload:i}}function q(e){let n=U(),{data:r,loading:i,error:a,reload:o,setData:s}=G(()=>n.getTicket(e),e??``,!!e),c=t(async e=>{let t=await e();return s(t),t},[s]);return{ticket:r,loading:i,error:a,reload:o,reply:t(t=>c(()=>n.reply(e,t)),[c,n,e]),reopen:t(()=>c(()=>n.reopen(e)),[c,n,e]),rate:t(t=>c(()=>n.rate(e,t)),[c,n,e])}}var J=`Asia/Kolkata`,ae=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],oe=new Map;function se(e){let t=oe.get(e);return t||(t=new Intl.DateTimeFormat(`en-IN`,{day:`2-digit`,month:`numeric`,year:`numeric`,hour:`numeric`,minute:`2-digit`,hour12:!0,timeZone:e}),oe.set(e,t)),t}function Y(e,t=J){if(!e)return`-`;let n=new Date(e);if(Number.isNaN(n.getTime()))return`-`;let r=Object.fromEntries(se(t).formatToParts(n).map(e=>[e.type,e.value]));return`${r.day}-${ae[Number(r.month)-1]}-${r.year}, ${r.hour}:${r.minute} ${r.dayPeriod.toUpperCase()}`}function ce(e=J){return new Intl.DateTimeFormat(`en-IN`,{timeZone:e,timeZoneName:`short`}).formatToParts(new Date).find(e=>e.type===`timeZoneName`)?.value??e}function le(e){if(!Number.isFinite(e)||e<0)return`-`;if(e<1024)return`${e} B`;if(e<1024*1024)return`${Math.round(e/1024)} KB`;let t=e/(1024*1024);return`${Number.isInteger(t)?t:t.toFixed(1)} MB`}function ue(e){return(e?.allowedContentTypes??[]).join(`,`)}function de(e,t){if(t.length===0)return!0;let n=(e||``).toLowerCase();return t.some(e=>{let t=e.toLowerCase();return t.endsWith(`/*`)?n.startsWith(t.slice(0,-1)):n===t})}function X(e,t){let n=e??[],r=t?.maxAttachmentsPerMessage??0,i=t?.maxAttachmentBytes??0,a=t?.allowedContentTypes??[];if(r>0&&n.length>r)return r===1?`You can attach one file.`:`You can attach up to ${r} files.`;for(let e of n){let t=e.name||`This file`;if(!de(e.type,a))return`${t} is a file type we cannot accept.`;if(i>0&&e.size>i)return`${t} is larger than ${le(i)}.`}return null}function fe(e){let t=Array.from(e.clipboardData?.items??[]),n=Date.now();return t.filter(e=>e.kind===`file`&&e.type.startsWith(`image/`)).map(e=>e.getAsFile()).filter(Boolean).map((e,t)=>{let r=(e.type.split(`/`)[1]||`png`).replace(`jpeg`,`jpg`);return new File([e],`pasted-image-${n}-${t+1}.${r}`,{type:e.type})})}function pe({id:e,label:t=`Attachments`,files:n,onChange:r,config:i,error:a}){let o=i?.maxAttachmentBytes??0,s=o>0?Math.max(.1,Math.round(o/(1024*1024)*10)/10):0;return M(g,{id:e,label:t,optional:!0,multiple:!0,value:n,onChange:r,accept:ue(i),maxSizeMb:s,validate:!1,error:a||void 0})}function me(e,t){return t&&e.includes(t)?t:``}function he({id:e,config:t,defaultCategory:n,initialValues:i,onValuesChange:a,extraFiles:o=[],onSubmit:c,onBusyChange:u,children:d}){let f=t?.categories??[],[p,m]=s(()=>i?.category??me(f,n)),[h,g]=s(i?.subject??``),[_,v]=s(i?.body??``),[b,x]=s(i?.files??[]),[S,w]=s({}),[T,E]=s(``);r(()=>{a?.({category:p,subject:h,body:_,files:b})},[p,h,_,b,a]);let D=e=>{x(e),w(n=>({...n,files:X([...o,...e],t)??void 0}))},O=e=>{let t=fe(e);t.length>0&&D([...b,...t])};function A(){let e={};h.trim()||(e.subject=`Give your ticket a subject.`),_.trim()||(e.body=`Tell us what happened.`);let n=X([...o,...b],t);return n&&(e.files=n),w(e),Object.keys(e).length===0}async function j(e){if(e.preventDefault(),E(``),A()){u?.(!0);try{await c({category:p,subject:h.trim(),body:_.trim(),files:b})}catch(e){E(e?.message||`Your ticket did not send. Please try again.`)}finally{u?.(!1)}}}return N(`form`,{id:e,onSubmit:j,noValidate:!0,className:`d-flex flex-column gap-3`,children:[T&&M(l,{variant:`danger`,children:T}),M(C,{id:`${e}-category`,label:`Category`,value:p,onChange:m,options:f.map(e=>({value:e,label:e})),placeholder:`Choose a category`,hint:`Not sure? Leave it empty and we will sort it.`,error:S.category}),M(y,{id:`${e}-subject`,label:`Subject`,required:!0,maxLength:t?.subjectMaxLength,showCount:!0,placeholder:`A short summary`,value:h,onChange:e=>g(e.target.value),error:S.subject}),M(k,{id:`${e}-body`,label:`Message`,required:!0,rows:6,maxLength:t?.bodyMaxLength,showCount:!0,placeholder:`What happened, and what did you expect to happen?`,value:_,onChange:e=>v(e.target.value),onPaste:O,hint:`You can paste an image into the message.`,error:S.body}),d,M(pe,{id:`${e}-files`,files:b,onChange:D,config:t,error:S.files})]})}var ge=`supportCapture`,_e={zIndex:`var(--wui-z-fixed)`,bottom:0,insetInlineEnd:0},ve={zIndex:`var(--wui-z-fixed)`},ye={maxWidth:`20rem`},be=1500,xe=e=>e?.dataset?.[ge]!==`exclude`,Se=()=>new Promise(e=>requestAnimationFrame(()=>e()));async function Ce(){let e=await j(document.body,{filter:xe,pixelRatio:Math.min(window.devicePixelRatio||1,2)});if(!e)throw Error(`The page capture came back empty.`);return new File([e],`screenshot.png`,{type:`image/png`})}function we({file:e,failed:t,disabled:n,hintId:i,controlRef:a,onAdd:o,onRemove:c}){let[u,d]=s(null);return r(()=>{if(!e)return;let t=URL.createObjectURL(e);return d(t),()=>URL.revokeObjectURL(t)},[e]),N(`div`,{className:`d-flex flex-column gap-2`,children:[M(`span`,{className:`wui-text-sm fw-semibold`,children:`Screenshot`}),e&&u&&M(`img`,{src:u,alt:`Screenshot of the page you were on`,className:`d-block w-100 border rounded`,style:ye}),M(`div`,{ref:a,className:`d-flex flex-wrap gap-2`,children:e?N(te,{children:[M(f,{variant:`outline`,size:`sm`,leftIcon:`refresh-cw`,onClick:o,disabled:n,"aria-describedby":i,children:`Retake`}),M(f,{variant:`ghost`,size:`sm`,leftIcon:`x`,onClick:c,disabled:n,children:`Remove screenshot`})]}):M(f,{variant:`outline`,size:`sm`,leftIcon:`camera`,onClick:o,disabled:n,"aria-describedby":i,children:`Add a screenshot`})}),M(`span`,{id:i,className:`wui-text-sm text-body-secondary`,children:`Only what is on your screen now. You can remove it before sending.`}),t&&M(l,{variant:`info`,children:`We could not take a screenshot of this page. You can attach an image instead, or send the report without one.`})]})}function Te({label:e=`Report an issue`,floating:t=!1,compact:n=!1,defaultCategory:a=`Bug report`,appVersion:c,getTicketHref:u,onOpenTicket:d,onCreated:p,className:m,style:h}){let g=U(),{config:v,error:y}=K(),b=`support-report-${i().replace(/[^a-zA-Z0-9-]/g,``)}`,[S,C]=s(!1),[w,T]=s(!1),[E,D]=s(null),[O,k]=s(!1),[j,ne]=s(!1),[P,F]=s(null),[I,L]=s(null),R=A(`end`,t&&n),z=o(null),B=o(null),V=o(!1);r(()=>{!S||!V.current||(V.current=!1,B.current?.querySelector(`button`)?.focus())},[S]);function H(){F(null),D(null),k(!1),L(null),C(!0)}async function W(){T(!0),k(!1),await new Promise(e=>{let t=setTimeout(e,be);z.current=()=>{clearTimeout(t),e()},C(!1)}),z.current=null,await Se(),await Se();try{D(await Ce())}catch{D(null),k(!0)}finally{V.current=!0,T(!1),C(!0)}}let G=()=>{j||(C(!1),D(null),k(!1),F(null),L(null))};async function re(e){let t=await g.createTicket({...e,pageUrl:window.location.href,userAgent:navigator.userAgent,appVersion:c,screenshots:E?[E]:[]});F(t),D(null),p?.(t)}let ie=!P&&(!!E||!!I&&(I.subject.trim()!==``||I.body.trim()!==``||I.files.length>0)),q=P&&u?u(P.id):void 0,J=P?N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:G,children:`Close`}),P&&(q||d)?M(f,{href:q,onClick:q?void 0:()=>{C(!1),F(null),d(P)},children:`View ticket`}):null]}):N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:G,disabled:j,children:`Cancel`}),M(f,{type:`submit`,form:b,loading:j,disabled:!v,children:`Submit`})]}),ae=n?M(f,{variant:`secondary`,className:`wui-floating-btn wui-floating-btn--secondary`,disabled:w,onClick:H,"aria-haspopup":`dialog`,"aria-label":e,tooltip:e,tooltipPlacement:`start`,children:M(_,{name:`bug`,size:22})}):M(f,{leftIcon:`bug`,variant:t?`primary`:`outline`,disabled:w,onClick:H,"aria-haspopup":`dialog`,children:e});return N(te,{children:[t&&n?R&&ee(M(`span`,{className:[`wui-floating-stack-item wui-floating-stack-item--secondary`,m].filter(Boolean).join(` `),style:h,children:ae}),R):M(`span`,{"data-support-capture":`exclude`,className:[t?`position-fixed m-3`:`d-inline-block`,m].filter(Boolean).join(` `),style:t?{..._e,...h}:h,children:ae}),w&&N(`div`,{"data-support-capture":`exclude`,role:`status`,className:`position-fixed top-0 start-50 translate-middle-x mt-3 d-flex align-items-center gap-2 px-3 py-2 bg-body border rounded-pill shadow-sm wui-text-sm`,style:ve,children:[M(`span`,{className:`spinner-border spinner-border-sm`,"aria-hidden":`true`}),`Taking a screenshot`]}),M(x,{open:S,size:`lg`,title:`Report an issue`,subtitle:`Tell us what went wrong. You can add a screenshot of this page if it helps.`,headerIcon:`bug`,confirmDiscard:`Discard this report?`,dirty:ie&&!j,onClose:G,onExited:()=>z.current?.(),footer:J,"data-support-capture":`exclude`,children:P?M(l,{variant:`success`,role:`status`,title:`Ticket ${P.number} is with the support team`,children:`We reply by email. You can follow the ticket on your support page.`}):v?M(he,{id:b,config:v,defaultCategory:a,initialValues:I,onValuesChange:L,extraFiles:E?[E]:[],onSubmit:re,onBusyChange:ne,children:M(we,{file:E,failed:O,disabled:j||w,hintId:`${b}-screenshot-hint`,controlRef:B,onAdd:W,onRemove:()=>D(null)})}):M(l,{variant:y?`danger`:`info`,children:y?y.message||`We could not load the support settings.`:`Loading the form.`})})]})}function Ee({open:e,onClose:t,onCreated:n,config:r,defaultCategory:a,appVersion:o}){let c=U(),l=`support-new-${i().replace(/[^a-zA-Z0-9-]/g,``)}`,[u,d]=s(!1),[p,m]=s(null),h=!!p&&(p.subject.trim()!==``||p.body.trim()!==``||p.files.length>0);async function g(e){n(await c.createTicket({...e,userAgent:typeof navigator>`u`?void 0:navigator.userAgent,appVersion:o}))}return M(x,{open:e,size:`lg`,title:`New ticket`,subtitle:`Tell us what you need. We reply by email and on this page.`,headerIcon:`message-square`,confirmDiscard:`Discard this ticket?`,dirty:h&&!u,onClose:()=>{u||t()},footer:N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:t,disabled:u,children:`Cancel`}),M(f,{type:`submit`,form:l,loading:u,children:`Send ticket`})]}),children:e&&M(he,{id:l,config:r,defaultCategory:a,onSubmit:g,onBusyChange:d,onValuesChange:m})})}var De={Open:`warning`,InProgress:`info`,WaitingOnCustomer:`warning`,Resolved:`success`,Closed:`neutral`};function Oe(e){return De[e]??`neutral`}var ke={width:140,maxWidth:`100%`},Ae={height:84};function je({attachment:e}){let t=U(),n=(e.contentType||``).startsWith(`image/`),[i,a]=s(null),[o,c]=s(!1);r(()=>{let n=!0,r=null;return a(null),c(!1),t.fetchAttachment(e.id).then(e=>{r=URL.createObjectURL(e),n?a(r):URL.revokeObjectURL(r)},()=>n&&c(!0)),()=>{n=!1,r&&URL.revokeObjectURL(r)}},[t,e.id]);let l=`Open ${e.fileName}`,u=N(`span`,{className:`d-block text-truncate px-2 py-1 wui-text-xs text-body-secondary`,children:[e.fileName,`, `,le(e.sizeBytes)]});return o?N(`span`,{className:`d-inline-flex flex-column border rounded overflow-hidden`,style:ke,children:[N(`span`,{className:`d-flex align-items-center justify-content-center gap-1 p-2 wui-text-xs text-body-secondary`,children:[M(_,{name:`x-circle`,size:14}),`Could not load this file.`]}),u]}):N(`a`,{className:`d-inline-flex flex-column border rounded overflow-hidden text-decoration-none`,style:ke,href:i??void 0,target:`_blank`,rel:`noopener noreferrer`,"aria-label":l,"aria-busy":!i||void 0,children:[M(`span`,{className:`d-flex align-items-center justify-content-center bg-body-tertiary`,style:Ae,children:i?n?M(`img`,{src:i,alt:e.fileName,className:`d-block w-100 h-100 object-fit-cover`}):M(_,{name:`file-text`,size:28}):M(T,{size:`sm`,label:`Loading ${e.fileName}`})}),u]})}var Me={whiteSpace:`pre-wrap`,overflowWrap:`anywhere`};function Ne({messages:e}){let t=W();return M(`ol`,{className:`list-unstyled d-flex flex-column gap-3 mb-0`,"aria-label":`Conversation`,children:e.map(e=>{let n=M(`time`,{dateTime:e.createdDateTimeUtc,className:`wui-text-xs text-body-secondary text-nowrap`,children:Y(e.createdDateTimeUtc,t)});if(e.authorKind===`System`)return N(`li`,{className:`text-center wui-text-xs text-body-secondary`,"data-author":`system`,children:[M(`span`,{className:`d-block`,children:e.body}),n]},e.id);let r=e.authorKind===`Customer`,i=e.authorName||(r?`You`:`Support team`);return N(`li`,{className:`d-flex align-items-start gap-2${r?` flex-row-reverse`:``}`,"data-author":r?`customer`:`agent`,children:[M(u,{name:i,size:32,tone:r?`neutral`:`primary`}),N(p,{className:`flex-grow-1 border`,padding:`sm`,variant:r?`default`:`accent`,style:{minWidth:0},children:[N(`div`,{className:`d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2`,children:[N(`span`,{className:`d-inline-flex flex-wrap align-items-center gap-2 fw-semibold wui-text-sm text-break`,children:[i,!r&&M(d,{tone:`primary`,children:`Support`})]}),n]}),M(`div`,{className:`wui-text-sm`,style:Me,children:e.body}),e.attachments?.length>0&&M(`div`,{className:`d-flex flex-wrap gap-2 mt-2`,children:e.attachments.map(e=>M(je,{attachment:e},e.id))})]})]},e.id)})})}function Pe({satisfaction:e,comment:t,canRate:n=!1,commentMaxLength:r,onRate:a}){let o=i(),[c,u]=s(``),[d,m]=s(null),[h,g]=s(``);if(e)return N(l,{variant:e===`Satisfied`?`success`:`info`,role:`status`,children:[e===`Satisfied`?`You told us this solved your problem.`:`You told us this did not solve your problem.`,t?N(`span`,{className:`d-block mt-1`,children:[`Your comment: `,t]}):null]});if(!n)return null;async function _(e){g(``),m(e);try{await a({satisfaction:e,comment:c})}catch(e){g(e?.message||`Your rating did not send. Please try again.`)}finally{m(null)}}return M(p,{padding:`sm`,className:`border`,title:`Did this solve your problem?`,children:N(`div`,{className:`d-flex flex-column gap-2`,children:[h&&M(l,{variant:`danger`,children:h}),M(k,{id:`${o}-comment`,label:`Anything to add?`,rows:2,maxLength:r,value:c,onChange:e=>u(e.target.value),hint:`Optional. Your comment goes to the support team.`}),N(`div`,{className:`d-flex flex-wrap gap-2`,children:[M(f,{variant:`outline`,leftIcon:`check-circle`,loading:d===`Satisfied`,disabled:d!==null,onClick:()=>_(`Satisfied`),children:`Yes, it is solved`}),M(f,{variant:`outline`,leftIcon:`x-circle`,loading:d===`Unsatisfied`,disabled:d!==null,onClick:()=>_(`Unsatisfied`),children:`No, it is not`})]})]})})}function Fe({onSend:e,config:t,reopens:n=!1}){let r=i(),[a,o]=s(``),[c,u]=s([]),[d,p]=s(``),[m,h]=s(``),[g,_]=s(!1),v=e=>{u(e),p(X(e,t)??``)},y=e=>{let t=fe(e);t.length>0&&v([...c,...t])};async function b(n){n.preventDefault();let r=a.trim();if(!r){h(`Write a message before you send it.`);return}let i=X(c,t);if(i){p(i);return}h(``),_(!0);try{await e({body:r,files:c}),o(``),u([]),p(``)}catch(e){h(e?.message||`Your reply did not send. Please try again.`)}finally{_(!1)}}return N(`form`,{onSubmit:b,noValidate:!0,className:`d-flex flex-column gap-2`,"aria-label":`Reply to this ticket`,children:[m&&M(l,{variant:`danger`,children:m}),M(k,{id:`${r}-body`,label:`Your reply`,rows:4,autoGrow:!0,maxLength:t?.bodyMaxLength,value:a,onChange:e=>o(e.target.value),onPaste:y,hint:n?`Sending a reply reopens this ticket. You can paste an image into the message.`:`You can paste an image into the message.`,required:!0}),M(pe,{id:`${r}-files`,files:c,onChange:v,config:t,error:d}),M(`div`,{className:`d-flex justify-content-end`,children:M(f,{type:`submit`,leftIcon:`send`,loading:g,children:`Send reply`})})]})}function Z({label:e,children:t}){return N(`div`,{className:`d-flex flex-column`,children:[M(`span`,{className:`wui-text-xs fw-semibold text-body-secondary`,children:e}),M(`span`,{className:`wui-text-sm`,children:t})]})}function Ie({ticketId:e,onBack:t,onChanged:n}){let{config:r}=K(),i=W(),{ticket:a,loading:o,error:c,reload:u,reply:d,reopen:p,rate:m}=q(e),[h,g]=s(!1),[_,v]=s(``),y=async e=>{let t=await e();return n?.(),t};async function b(){v(``),g(!0);try{await y(p)}catch(e){v(e?.message||`The ticket did not reopen. Please try again.`)}finally{g(!1)}}let x=M(f,{variant:`ghost`,size:`sm`,leftIcon:`arrow-left`,onClick:t,className:`align-self-start`,children:`Back to tickets`});if(!a)return N(`section`,{className:`d-flex flex-column gap-3`,"aria-busy":o||void 0,children:[x,c?N(l,{variant:`danger`,children:[c.message||`We could not load this ticket.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:u,children:`Try again`})]}):M(w,{height:160})]});let S=a.status===`Resolved`||a.status===`Closed`;return N(`section`,{className:`d-flex flex-column gap-3`,"aria-labelledby":`support-ticket-${a.id}`,children:[x,N(`header`,{className:`d-flex flex-column gap-2`,children:[N(`div`,{className:`d-flex flex-wrap align-items-center gap-2`,children:[M(`h2`,{id:`support-ticket-${a.id}`,className:`h5 mb-0 text-break`,children:a.subject}),a.statusLabel&&M(E,{label:a.statusLabel,tone:Oe(a.status)})]}),N(`div`,{className:`row row-cols-2 row-cols-md-4 g-3`,children:[M(Z,{label:`Ticket`,children:a.number}),M(Z,{label:`Category`,children:a.category||`-`}),M(Z,{label:`Opened`,children:M(`time`,{dateTime:a.createdDateTimeUtc,children:Y(a.createdDateTimeUtc,i)})}),M(Z,{label:`Last update`,children:M(`time`,{dateTime:a.updatedDateTimeUtc,children:Y(a.updatedDateTimeUtc,i)})})]}),N(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:[`Times are shown in `,ce(i),`.`]})]}),_&&M(l,{variant:`danger`,children:_}),S&&M(l,{variant:`success`,role:`status`,children:`If you still need help, reopen the ticket or send a reply.`}),a.status===`WaitingOnCustomer`&&M(l,{variant:`warning`,role:`status`,children:`We need your answer to carry on. Reply below and the ticket goes back to our team.`}),!S&&M(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:`Our team marks this resolved once your question is answered. Replying reopens it.`}),a.canReopen&&M(f,{variant:`secondary`,leftIcon:`history`,loading:h,onClick:b,className:`align-self-start`,children:`Reopen ticket`}),M(Pe,{satisfaction:a.satisfaction,comment:a.satisfactionComment,canRate:a.canRate,commentMaxLength:r?.satisfactionCommentMaxLength,onRate:e=>y(()=>m(e))}),M(Ne,{messages:a.messages??[]}),M(Fe,{config:r,reopens:S,onSend:e=>y(()=>d(e))})]})}var Q=`ticket`;function Le(){return typeof window>`u`?null:new URLSearchParams(window.location.search).get(Q)}function Re(e){if(typeof window>`u`)return e?`?${Q}=${encodeURIComponent(e)}`:`?`;let t=new URL(window.location.href);return e?t.searchParams.set(Q,e):t.searchParams.delete(Q),`${t.pathname}${t.search}${t.hash}`}function ze(e){if(typeof window>`u`)return;let t=Re(e);t!==`${window.location.pathname}${window.location.search}${window.location.hash}`&&window.history.replaceState(window.history.state,``,t)}var Be=[{key:`all`,label:`All`,countKey:`total`},{key:`Open`,label:`Open`,countKey:`open`},{key:`InProgress`,label:`In progress`,countKey:`inProgress`},{key:`WaitingOnCustomer`,label:`Waiting on you`,countKey:`waitingOnCustomer`},{key:`Resolved`,label:`Resolved`,countKey:`resolved`},{key:`Closed`,label:`Closed`,countKey:`closed`}];function Ve({tickets:e,loading:t,error:n,onRetry:r,onOpen:i,status:a,onStatusChange:o,counts:s,search:u,onSearchChange:d,pagination:p}){let m=W(),g=(e,t)=>{e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||(e.preventDefault(),i(t))};return N(`div`,{className:`d-flex flex-column gap-3`,children:[n&&N(l,{variant:`danger`,children:[n.message||`We could not load your tickets.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:r,children:`Try again`})]}),N(`div`,{className:`d-flex flex-column flex-md-row gap-2 align-items-md-center justify-content-between`,children:[M(O,{tabs:Be.map(({key:e,label:t,countKey:n})=>({key:e,label:t,count:s?.[n]})),value:a,onChange:o,ariaLabel:`Filter tickets by status`,className:`flex-grow-1`}),M(S,{value:u,onChange:d,placeholder:`Search tickets`,ariaLabel:`Search tickets`})]}),M(h,{columns:[{key:`subject`,label:`Subject`,render:e=>N(`div`,{className:`d-flex flex-column`,style:{minWidth:0},children:[M(b,{href:Re(e.id),animation:`none`,className:`fw-semibold text-break`,onClick:t=>g(t,e.id),children:e.subject}),N(`span`,{className:`wui-text-xs text-body-secondary`,children:[e.number,`, `,e.category,e.awaitingCustomer?`. Waiting for your reply`:``]})]})},{key:`status`,label:`Status`,render:e=>e.statusLabel?M(E,{label:e.statusLabel,tone:Oe(e.status)}):`-`},{key:`messageCount`,label:`Messages`,cellClassName:`wui-text-sm text-body-secondary`,render:e=>e.messageCount},{key:`updatedDateTimeUtc`,label:`Last update`,cellClassName:`wui-text-sm text-body-secondary text-nowrap`,render:e=>M(`time`,{dateTime:e.updatedDateTimeUtc,children:Y(e.updatedDateTimeUtc,m)})}],rows:e,loading:t,skeletonRows:4,ariaLabel:`Support tickets`,rowActionsLabel:`Actions`,rowActions:e=>M(c,{icon:`eye`,label:`Open ticket ${e.subject}`,href:Re(e.id),onClick:t=>g(t,e.id)}),stickyActions:!0,stackOnMobile:!0,sourceCount:s?.total??0,pagination:p,empty:M(D,{icon:`message-square`,title:`No tickets yet`,text:`When you raise a ticket, it shows here with its status.`}),emptyFiltered:M(D,{icon:`search`,title:`No tickets match`,text:`Try another search or status.`})})]})}function He(e,t){let[n,i]=s(e);return r(()=>{let n=setTimeout(()=>i(e),t);return()=>clearTimeout(n)},[e,t]),n}var Ue=300,We=10;function Ge({initialTicketId:e,defaultCategory:t,appVersion:n}){let{config:i,error:a,reload:o}=K(),[c,u]=s(`all`),[d,p]=s(``),m=He(d.trim(),Ue),[h,g]=s(We),_=`${c}|${m}|${h}`,[v,y]=s({key:_,page:1}),b=v.key===_?v.page:1,x=e=>y({key:_,page:e}),S=re({status:c===`all`?void 0:c,search:m||void 0,page:b,pageSize:h}),{counts:C,reload:w}=ie(),[T,E]=s(()=>e||Le()),[D,O]=s(!1),k=S.totalPages;r(()=>{k>0&&b>k&&y({key:_,page:k})},[k,b,_]);let A=()=>{S.reload(),w()},j=e=>{E(e),ze(e)};if(T)return M(Ie,{ticketId:T,onBack:()=>j(null),onChanged:A},T);let ee={page:b,totalPages:Math.max(1,S.totalPages),pageSize:h,total:S.totalCount,setPage:x,setPageSize:g};return N(`section`,{className:`d-flex flex-column gap-3`,style:{minWidth:0},"aria-labelledby":`support-center-title`,children:[N(`div`,{className:`d-flex flex-wrap align-items-center justify-content-between gap-2`,children:[M(`h2`,{id:`support-center-title`,className:`h5 mb-0`,children:`Your tickets`}),M(f,{leftIcon:`plus`,onClick:()=>O(!0),disabled:!i,children:`New ticket`})]}),a&&N(l,{variant:`warning`,children:[a.message||`We could not load the support settings.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:o,children:`Try again`})]}),M(Ve,{tickets:S.tickets,loading:S.loading,error:S.error,onRetry:A,onOpen:j,status:c,onStatusChange:u,counts:C,search:d,onSearchChange:p,pagination:ee}),i&&M(Ee,{open:D,onClose:()=>O(!1),config:i,defaultCategory:t,appVersion:n,onCreated:e=>{O(!1),A(),j(e.id)}})]})}var $=e=>typeof e==`string`&&e.trim()!==``;function Ke({className:e}){let{config:t}=K();if(!t)return null;let n=[];return $(t.supportEmail)&&n.push({key:`email`,icon:`mail`,label:`Email`,value:N(`span`,{className:`d-inline-flex flex-wrap align-items-center gap-2`,children:[M(`a`,{href:`mailto:${t.supportEmail}`,className:`text-break`,children:t.supportEmail}),M(m,{text:t.supportEmail,size:`sm`,label:`Copy email`,copiedLabel:`Email copied`})]})}),$(t.supportPhone)&&n.push({key:`phone`,icon:`phone`,label:`Phone`,value:t.supportPhone,href:`tel:${t.supportPhone.replace(/[^\d+]/g,``)}`}),$(t.supportHours)&&n.push({key:`hours`,icon:`clock`,label:`Hours`,value:t.supportHours}),n.length===0?null:N(`section`,{className:e,"aria-label":$(t.supportName)?`Contact ${t.supportName}`:`Contact support`,children:[$(t.supportName)&&M(`p`,{className:`fw-semibold mb-2`,children:t.supportName}),M(v,{items:n})]})}function qe({client:e,timeZone:n=J,children:r}){let i=o(null),s=t((t=!1)=>{let n=i.current;if(t||!n||n.client!==e){let t=e.getConfig(),n={client:e,pending:t};return i.current=n,t.catch(()=>{i.current===n&&(i.current=null)}),t}return n.pending},[e]),c=a(()=>({client:e,loadConfig:s,timeZone:n}),[e,s,n]);return M(V.Provider,{value:c,children:r})}export{Te as ReportIssue,P as SupportApiError,Ge as SupportCenter,Ke as SupportContact,qe as SupportProvider,B as createSupportClient,U as useSupportClient,K as useSupportConfig,q as useSupportTicket,ie as useSupportTicketCounts,re as useSupportTickets};
1
+ import{createContext as e,useCallback as t,useContext as n,useEffect as r,useId as i,useMemo as a,useRef as o,useState as s}from"react";import{AppActionIcon as c,AppAlert as l,AppAvatar as u,AppBadge as d,AppButton as f,AppCard as p,AppCopyButton as m,AppDataTable as h,AppFileAttachment as g,AppIcon as _,AppInfoList as v,AppInput as y,AppLink as b,AppModal as x,AppSearch as S,AppSelect as C,AppSkeleton as w,AppSpinner as T,AppStatusBadge as E,AppTableEmptyState as D,AppTabs as O,AppTextarea as k,useFloatingStack as A}from"@webority/ui-react";import{toBlob as j}from"html-to-image";import{createPortal as ee}from"react-dom";import{Fragment as te,jsx as M,jsxs as N}from"react/jsx-runtime";var P=`Something went wrong. Please try again.`,F=class extends Error{constructor(e,t,n){super(t),this.name=`SupportApiError`,this.code=e,this.status=n}};async function I(e){if(e&&typeof Blob<`u`&&e instanceof Blob)try{return JSON.parse(await e.text())}catch{return null}if(typeof e==`string`)try{return JSON.parse(e)}catch{return null}return e??null}async function L(e){if(e instanceof F)return e;let t=e?.response;if(!t)return new F(`network`,P);let n=await I(t.data);return new F(typeof n?.code==`string`&&n.code?n.code:`unknown`,typeof n?.message==`string`&&n.message?n.message:P,t.status)}async function R(e){try{return(await e()).data}catch(e){throw await L(e)}}function z(e,t,n){n!=null&&String(n).trim()!==``&&e.append(t,String(n))}function B(e,t,n,r){(n??[]).forEach((n,i)=>{let a=n?.name||`${r}-${i+1}.png`;e.append(t,n,a)})}function ne(e,t=`/api/support`){let n=(typeof t==`string`?t:t?.base??`/api/support`).replace(/\/+$/,``),r=e=>`${n}/tickets/${encodeURIComponent(e)}`;return{getConfig:()=>R(()=>e.get(`${n}/settings`)),listTickets:({status:t,search:r,page:i,pageSize:a}={})=>{let o=new URLSearchParams;z(o,`status`,t),z(o,`search`,r?.trim()),z(o,`page`,i),z(o,`pageSize`,a);let s=o.toString();return R(()=>e.get(`${n}/tickets${s?`?${s}`:``}`))},getTicketCounts:()=>R(()=>e.get(`${n}/tickets/counts`)),getTicket:t=>R(()=>e.get(r(t))),createTicket:({category:t,subject:r,body:i,pageUrl:a,userAgent:o,appVersion:s,files:c,screenshots:l})=>{let u=new FormData;return u.append(`category`,t??``),u.append(`subject`,r??``),u.append(`body`,i??``),z(u,`pageUrl`,a),z(u,`userAgent`,o),z(u,`appVersion`,s),B(u,`files`,c,`file`),B(u,`screenshots`,l,`screenshot`),R(()=>e.post(`${n}/tickets`,u))},reply:(t,{body:n,files:i,screenshots:a})=>{let o=new FormData;return o.append(`body`,n??``),B(o,`files`,i,`file`),B(o,`screenshots`,a,`screenshot`),R(()=>e.post(`${r(t)}/replies`,o))},reopen:t=>R(()=>e.post(`${r(t)}/reopen`)),rate:(t,{satisfaction:n,comment:i})=>{let a={satisfaction:n};return i!=null&&i.trim()!==``&&(a.comment=i.trim()),R(()=>e.post(`${r(t)}/rating`,a))},fetchAttachment:t=>R(()=>e.get(`${n}/attachments/${encodeURIComponent(t)}`,{responseType:`blob`})),confirmAction:t=>R(()=>e.post(`${n}/actions/confirm`,{token:t}))}}var V=e(null);function H(){let e=n(V);if(!e)throw Error(`Support components and hooks must be used inside <SupportProvider>.`);return e}function U(){return H().client}function re(){return H().timeZone}function W(){return H().renderTime??null}function G(e,n,i=!0){let[a,c]=s({data:null,loading:i,error:null}),[l,u]=s(0),d=o(e);d.current=e;let f=o(n);r(()=>{let e=f.current!==n;if(f.current=n,!i){c({data:null,loading:!1,error:null});return}let t=!0;return c(t=>({data:e?null:t.data,loading:!0,error:null})),d.current(l>0).then(e=>t&&c({data:e,loading:!1,error:null}),e=>t&&c(t=>({...t,loading:!1,error:e}))),()=>{t=!1}},[n,i,l]);let p=t(()=>u(e=>e+1),[]),m=t(e=>c(t=>({...t,data:e})),[]);return{...a,reload:p,setData:m}}function K(){let{loadConfig:e}=H(),{data:t,loading:n,error:r,reload:i}=G(e,`config`);return{config:t,loading:n,error:r,reload:i}}function ie(e={}){let t=U(),{status:n,search:r,page:i,pageSize:a}=e,{data:o,loading:s,error:c,reload:l}=G(()=>t.listTickets({status:n,search:r,page:i,pageSize:a}),JSON.stringify([`tickets`,n??null,r??null,i??null,a??null]));return{tickets:o?.items??[],totalCount:o?.totalCount??0,totalPages:o?.totalPages??0,loading:s,error:c,reload:l}}function q(){let e=U(),{data:t,loading:n,error:r,reload:i}=G(()=>e.getTicketCounts(),`ticket-counts`);return{counts:t,loading:n,error:r,reload:i}}function ae(e){let n=U(),{data:r,loading:i,error:a,reload:o,setData:s}=G(()=>n.getTicket(e),e??``,!!e),c=t(async e=>{let t=await e();return s(t),t},[s]);return{ticket:r,loading:i,error:a,reload:o,reply:t(t=>c(()=>n.reply(e,t)),[c,n,e]),reopen:t(()=>c(()=>n.reopen(e)),[c,n,e]),rate:t(t=>c(()=>n.rate(e,t)),[c,n,e])}}var J=`Asia/Kolkata`,oe=[`Jan`,`Feb`,`Mar`,`Apr`,`May`,`Jun`,`Jul`,`Aug`,`Sep`,`Oct`,`Nov`,`Dec`],se=new Map;function ce(e){let t=se.get(e);return t||(t=new Intl.DateTimeFormat(`en-IN`,{day:`2-digit`,month:`numeric`,year:`numeric`,hour:`numeric`,minute:`2-digit`,hour12:!0,timeZone:e}),se.set(e,t)),t}function le(e,t=J){if(!e)return`-`;let n=new Date(e);if(Number.isNaN(n.getTime()))return`-`;let r=Object.fromEntries(ce(t).formatToParts(n).map(e=>[e.type,e.value]));return`${r.day}-${oe[Number(r.month)-1]}-${r.year}, ${r.hour}:${r.minute} ${r.dayPeriod.toUpperCase()}`}function ue(e=J){return new Intl.DateTimeFormat(`en-IN`,{timeZone:e,timeZoneName:`short`}).formatToParts(new Date).find(e=>e.type===`timeZoneName`)?.value??e}function de(e){if(!Number.isFinite(e)||e<0)return`-`;if(e<1024)return`${e} B`;if(e<1024*1024)return`${Math.round(e/1024)} KB`;let t=e/(1024*1024);return`${Number.isInteger(t)?t:t.toFixed(1)} MB`}function fe(e){return(e?.allowedContentTypes??[]).join(`,`)}function pe(e,t){if(t.length===0)return!0;let n=(e||``).toLowerCase();return t.some(e=>{let t=e.toLowerCase();return t.endsWith(`/*`)?n.startsWith(t.slice(0,-1)):n===t})}function Y(e,t){let n=e??[],r=t?.maxAttachmentsPerMessage??0,i=t?.maxAttachmentBytes??0,a=t?.allowedContentTypes??[];if(r>0&&n.length>r)return r===1?`You can attach one file.`:`You can attach up to ${r} files.`;for(let e of n){let t=e.name||`This file`;if(!pe(e.type,a))return`${t} is a file type we cannot accept.`;if(i>0&&e.size>i)return`${t} is larger than ${de(i)}.`}return null}function me(e){let t=Array.from(e.clipboardData?.items??[]),n=Date.now();return t.filter(e=>e.kind===`file`&&e.type.startsWith(`image/`)).map(e=>e.getAsFile()).filter(Boolean).map((e,t)=>{let r=(e.type.split(`/`)[1]||`png`).replace(`jpeg`,`jpg`);return new File([e],`pasted-image-${n}-${t+1}.${r}`,{type:e.type})})}function he({id:e,label:t=`Attachments`,files:n,onChange:r,config:i,error:a}){let o=i?.maxAttachmentBytes??0,s=o>0?Math.max(.1,Math.round(o/(1024*1024)*10)/10):0;return M(g,{id:e,label:t,optional:!0,multiple:!0,value:n,onChange:r,accept:fe(i),maxSizeMb:s,validate:!1,error:a||void 0})}function ge(e,t){return t&&e.includes(t)?t:``}function _e({id:e,config:t,defaultCategory:n,initialValues:i,onValuesChange:a,extraFiles:o=[],onSubmit:c,onBusyChange:u,children:d}){let f=t?.categories??[],[p,m]=s(()=>i?.category??ge(f,n)),[h,g]=s(i?.subject??``),[_,v]=s(i?.body??``),[b,x]=s(i?.files??[]),[S,w]=s({}),[T,E]=s(``);r(()=>{a?.({category:p,subject:h,body:_,files:b})},[p,h,_,b,a]);let D=e=>{x(e),w(n=>({...n,files:Y([...o,...e],t)??void 0}))},O=e=>{let t=me(e);t.length>0&&D([...b,...t])};function A(){let e={};h.trim()||(e.subject=`Give your ticket a subject.`),_.trim()||(e.body=`Tell us what happened.`);let n=Y([...o,...b],t);return n&&(e.files=n),w(e),Object.keys(e).length===0}async function j(e){if(e.preventDefault(),E(``),A()){u?.(!0);try{await c({category:p,subject:h.trim(),body:_.trim(),files:b})}catch(e){E(e?.message||`Your ticket did not send. Please try again.`)}finally{u?.(!1)}}}return N(`form`,{id:e,onSubmit:j,noValidate:!0,className:`d-flex flex-column gap-3`,children:[T&&M(l,{variant:`danger`,children:T}),M(C,{id:`${e}-category`,label:`Category`,value:p,onChange:m,options:f.map(e=>({value:e,label:e})),placeholder:`Choose a category`,hint:`Not sure? Leave it empty and we will sort it.`,error:S.category}),M(y,{id:`${e}-subject`,label:`Subject`,required:!0,maxLength:t?.subjectMaxLength,showCount:!0,placeholder:`A short summary`,value:h,onChange:e=>g(e.target.value),error:S.subject}),M(k,{id:`${e}-body`,label:`Message`,required:!0,rows:6,maxLength:t?.bodyMaxLength,showCount:!0,placeholder:`What happened, and what did you expect to happen?`,value:_,onChange:e=>v(e.target.value),onPaste:O,hint:`You can paste an image into the message.`,error:S.body}),d,M(he,{id:`${e}-files`,files:b,onChange:D,config:t,error:S.files})]})}var ve=`supportCapture`,ye={zIndex:`var(--wui-z-fixed)`,bottom:0,insetInlineEnd:0},be={zIndex:`var(--wui-z-fixed)`},xe={maxWidth:`20rem`},Se=1500,Ce=e=>e?.dataset?.[ve]!==`exclude`,we=()=>new Promise(e=>requestAnimationFrame(()=>e()));async function Te(){let e=await j(document.body,{filter:Ce,pixelRatio:Math.min(window.devicePixelRatio||1,2)});if(!e)throw Error(`The page capture came back empty.`);return new File([e],`screenshot.png`,{type:`image/png`})}function Ee({file:e,failed:t,disabled:n,hintId:i,controlRef:a,onAdd:o,onRemove:c}){let[u,d]=s(null);return r(()=>{if(!e)return;let t=URL.createObjectURL(e);return d(t),()=>URL.revokeObjectURL(t)},[e]),N(`div`,{className:`d-flex flex-column gap-2`,children:[M(`span`,{className:`wui-text-sm fw-semibold`,children:`Screenshot`}),e&&u&&M(`img`,{src:u,alt:`Screenshot of the page you were on`,className:`d-block w-100 border rounded`,style:xe}),M(`div`,{ref:a,className:`d-flex flex-wrap gap-2`,children:e?N(te,{children:[M(f,{variant:`outline`,size:`sm`,leftIcon:`refresh-cw`,onClick:o,disabled:n,"aria-describedby":i,children:`Retake`}),M(f,{variant:`ghost`,size:`sm`,leftIcon:`x`,onClick:c,disabled:n,children:`Remove screenshot`})]}):M(f,{variant:`outline`,size:`sm`,leftIcon:`camera`,onClick:o,disabled:n,"aria-describedby":i,children:`Add a screenshot`})}),M(`span`,{id:i,className:`wui-text-sm text-body-secondary`,children:`Only what is on your screen now. You can remove it before sending.`}),t&&M(l,{variant:`info`,children:`We could not take a screenshot of this page. You can attach an image instead, or send the report without one.`})]})}function De({label:e=`Report an issue`,floating:t=!1,compact:n=!1,defaultCategory:a=`Bug report`,appVersion:c,getTicketHref:u,onOpenTicket:d,onCreated:p,className:m,style:h}){let g=U(),{config:v,error:y}=K(),b=`support-report-${i().replace(/[^a-zA-Z0-9-]/g,``)}`,[S,C]=s(!1),[w,T]=s(!1),[E,D]=s(null),[O,k]=s(!1),[j,P]=s(!1),[F,I]=s(null),[L,R]=s(null),z=A(`end`,t&&n),B=o(null),ne=o(null),V=o(!1);r(()=>{!S||!V.current||(V.current=!1,ne.current?.querySelector(`button`)?.focus())},[S]);function H(){I(null),D(null),k(!1),R(null),C(!0)}async function re(){T(!0),k(!1),await new Promise(e=>{let t=setTimeout(e,Se);B.current=()=>{clearTimeout(t),e()},C(!1)}),B.current=null,await we(),await we();try{D(await Te())}catch{D(null),k(!0)}finally{V.current=!0,T(!1),C(!0)}}let W=()=>{j||(C(!1),D(null),k(!1),I(null),R(null))};async function G(e){let t=await g.createTicket({...e,pageUrl:window.location.href,userAgent:navigator.userAgent,appVersion:c,screenshots:E?[E]:[]});I(t),D(null),p?.(t)}let ie=!F&&(!!E||!!L&&(L.subject.trim()!==``||L.body.trim()!==``||L.files.length>0)),q=F&&u?u(F.id):void 0,ae=F?N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:W,children:`Close`}),F&&(q||d)?M(f,{href:q,onClick:q?void 0:()=>{C(!1),I(null),d(F)},children:`View ticket`}):null]}):N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:W,disabled:j,children:`Cancel`}),M(f,{type:`submit`,form:b,loading:j,disabled:!v,children:`Submit`})]}),J=n?M(f,{variant:`secondary`,className:`wui-floating-btn wui-floating-btn--secondary`,disabled:w,onClick:H,"aria-haspopup":`dialog`,"aria-label":e,tooltip:e,tooltipPlacement:`start`,children:M(_,{name:`bug`,size:22})}):M(f,{leftIcon:`bug`,variant:t?`primary`:`outline`,disabled:w,onClick:H,"aria-haspopup":`dialog`,children:e});return N(te,{children:[t&&n?z&&ee(M(`span`,{className:[`wui-floating-stack-item wui-floating-stack-item--secondary`,m].filter(Boolean).join(` `),style:h,children:J}),z):M(`span`,{"data-support-capture":`exclude`,className:[t?`position-fixed m-3`:`d-inline-block`,m].filter(Boolean).join(` `),style:t?{...ye,...h}:h,children:J}),w&&N(`div`,{"data-support-capture":`exclude`,role:`status`,className:`position-fixed top-0 start-50 translate-middle-x mt-3 d-flex align-items-center gap-2 px-3 py-2 bg-body border rounded-pill shadow-sm wui-text-sm`,style:be,children:[M(`span`,{className:`spinner-border spinner-border-sm`,"aria-hidden":`true`}),`Taking a screenshot`]}),M(x,{open:S,size:`lg`,title:`Report an issue`,subtitle:`Tell us what went wrong. You can add a screenshot of this page if it helps.`,headerIcon:`bug`,confirmDiscard:`Discard this report?`,dirty:ie&&!j,onClose:W,onExited:()=>B.current?.(),footer:ae,"data-support-capture":`exclude`,children:F?M(l,{variant:`success`,role:`status`,title:`Ticket ${F.number} is with the support team`,children:`We reply by email. You can follow the ticket on your support page.`}):v?M(_e,{id:b,config:v,defaultCategory:a,initialValues:L,onValuesChange:R,extraFiles:E?[E]:[],onSubmit:G,onBusyChange:P,children:M(Ee,{file:E,failed:O,disabled:j||w,hintId:`${b}-screenshot-hint`,controlRef:ne,onAdd:re,onRemove:()=>D(null)})}):M(l,{variant:y?`danger`:`info`,children:y?y.message||`We could not load the support settings.`:`Loading the form.`})})]})}function Oe({open:e,onClose:t,onCreated:n,config:r,defaultCategory:a,appVersion:o}){let c=U(),l=`support-new-${i().replace(/[^a-zA-Z0-9-]/g,``)}`,[u,d]=s(!1),[p,m]=s(null),h=!!p&&(p.subject.trim()!==``||p.body.trim()!==``||p.files.length>0);async function g(e){n(await c.createTicket({...e,userAgent:typeof navigator>`u`?void 0:navigator.userAgent,appVersion:o}))}return M(x,{open:e,size:`lg`,title:`New ticket`,subtitle:`Tell us what you need. We reply by email and on this page.`,headerIcon:`message-square`,confirmDiscard:`Discard this ticket?`,dirty:h&&!u,onClose:()=>{u||t()},footer:N(`div`,{className:`d-flex justify-content-end gap-2 w-100`,children:[M(f,{variant:`secondary`,onClick:t,disabled:u,children:`Cancel`}),M(f,{type:`submit`,form:l,loading:u,children:`Send ticket`})]}),children:e&&M(_e,{id:l,config:r,defaultCategory:a,onSubmit:g,onBusyChange:d,onValuesChange:m})})}var ke={Open:`warning`,InProgress:`info`,WaitingOnCustomer:`warning`,Resolved:`success`,Closed:`neutral`};function Ae(e){return ke[e]??`neutral`}var je={width:140,maxWidth:`100%`},Me={height:84};function Ne({attachment:e}){let t=U(),n=(e.contentType||``).startsWith(`image/`),[i,a]=s(null),[o,c]=s(!1);r(()=>{let n=!0,r=null;return a(null),c(!1),t.fetchAttachment(e.id).then(e=>{r=URL.createObjectURL(e),n?a(r):URL.revokeObjectURL(r)},()=>n&&c(!0)),()=>{n=!1,r&&URL.revokeObjectURL(r)}},[t,e.id]);let l=`Open ${e.fileName}`,u=N(`span`,{className:`d-block text-truncate px-2 py-1 wui-text-xs text-body-secondary`,children:[e.fileName,`, `,de(e.sizeBytes)]});return o?N(`span`,{className:`d-inline-flex flex-column border rounded overflow-hidden`,style:je,children:[N(`span`,{className:`d-flex align-items-center justify-content-center gap-1 p-2 wui-text-xs text-body-secondary`,children:[M(_,{name:`x-circle`,size:14}),`Could not load this file.`]}),u]}):N(`a`,{className:`d-inline-flex flex-column border rounded overflow-hidden text-decoration-none`,style:je,href:i??void 0,target:`_blank`,rel:`noopener noreferrer`,"aria-label":l,"aria-busy":!i||void 0,children:[M(`span`,{className:`d-flex align-items-center justify-content-center bg-body-tertiary`,style:Me,children:i?n?M(`img`,{src:i,alt:e.fileName,className:`d-block w-100 h-100 object-fit-cover`}):M(_,{name:`file-text`,size:28}):M(T,{size:`sm`,label:`Loading ${e.fileName}`})}),u]})}function X({iso:e,className:t}){let n=W(),r=re();return n?n(e):M(`time`,{dateTime:e,className:t,children:le(e,r)})}var Pe={whiteSpace:`pre-wrap`,overflowWrap:`anywhere`};function Fe({messages:e}){return M(`ol`,{className:`list-unstyled d-flex flex-column gap-3 mb-0`,"aria-label":`Conversation`,children:e.map(e=>{let t=M(X,{iso:e.createdDateTimeUtc,className:`wui-text-xs text-body-secondary text-nowrap`});if(e.authorKind===`System`)return N(`li`,{className:`text-center wui-text-xs text-body-secondary`,"data-author":`system`,children:[M(`span`,{className:`d-block`,children:e.body}),t]},e.id);let n=e.authorKind===`Customer`,r=e.authorName||(n?`You`:`Support team`);return N(`li`,{className:`d-flex align-items-start gap-2${n?` flex-row-reverse`:``}`,"data-author":n?`customer`:`agent`,children:[M(u,{name:r,size:32,tone:n?`neutral`:`primary`}),N(p,{className:`flex-grow-1 border`,padding:`sm`,variant:n?`default`:`accent`,style:{minWidth:0},children:[N(`div`,{className:`d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2`,children:[N(`span`,{className:`d-inline-flex flex-wrap align-items-center gap-2 fw-semibold wui-text-sm text-break`,children:[r,!n&&M(d,{tone:`primary`,children:`Support`})]}),t]}),M(`div`,{className:`wui-text-sm`,style:Pe,children:e.body}),e.attachments?.length>0&&M(`div`,{className:`d-flex flex-wrap gap-2 mt-2`,children:e.attachments.map(e=>M(Ne,{attachment:e},e.id))})]})]},e.id)})})}function Ie({satisfaction:e,comment:t,canRate:n=!1,commentMaxLength:r,onRate:a}){let o=i(),[c,u]=s(``),[d,m]=s(null),[h,g]=s(``);if(e)return N(l,{variant:e===`Satisfied`?`success`:`info`,role:`status`,children:[e===`Satisfied`?`You told us this solved your problem.`:`You told us this did not solve your problem.`,t?N(`span`,{className:`d-block mt-1`,children:[`Your comment: `,t]}):null]});if(!n)return null;async function _(e){g(``),m(e);try{await a({satisfaction:e,comment:c})}catch(e){g(e?.message||`Your rating did not send. Please try again.`)}finally{m(null)}}return M(p,{padding:`sm`,className:`border`,title:`Did this solve your problem?`,children:N(`div`,{className:`d-flex flex-column gap-2`,children:[h&&M(l,{variant:`danger`,children:h}),M(k,{id:`${o}-comment`,label:`Anything to add?`,rows:2,maxLength:r,value:c,onChange:e=>u(e.target.value),hint:`Optional. Your comment goes to the support team.`}),N(`div`,{className:`d-flex flex-wrap gap-2`,children:[M(f,{variant:`outline`,leftIcon:`check-circle`,loading:d===`Satisfied`,disabled:d!==null,onClick:()=>_(`Satisfied`),children:`Yes, it is solved`}),M(f,{variant:`outline`,leftIcon:`x-circle`,loading:d===`Unsatisfied`,disabled:d!==null,onClick:()=>_(`Unsatisfied`),children:`No, it is not`})]})]})})}function Le({onSend:e,config:t,reopens:n=!1}){let r=i(),[a,o]=s(``),[c,u]=s([]),[d,p]=s(``),[m,h]=s(``),[g,_]=s(!1),v=e=>{u(e),p(Y(e,t)??``)},y=e=>{let t=me(e);t.length>0&&v([...c,...t])};async function b(n){n.preventDefault();let r=a.trim();if(!r){h(`Write a message before you send it.`);return}let i=Y(c,t);if(i){p(i);return}h(``),_(!0);try{await e({body:r,files:c}),o(``),u([]),p(``)}catch(e){h(e?.message||`Your reply did not send. Please try again.`)}finally{_(!1)}}return N(`form`,{onSubmit:b,noValidate:!0,className:`d-flex flex-column gap-2`,"aria-label":`Reply to this ticket`,children:[m&&M(l,{variant:`danger`,children:m}),M(k,{id:`${r}-body`,label:`Your reply`,rows:4,autoGrow:!0,maxLength:t?.bodyMaxLength,value:a,onChange:e=>o(e.target.value),onPaste:y,hint:n?`Sending a reply reopens this ticket. You can paste an image into the message.`:`You can paste an image into the message.`,required:!0}),M(he,{id:`${r}-files`,files:c,onChange:v,config:t,error:d}),M(`div`,{className:`d-flex justify-content-end`,children:M(f,{type:`submit`,leftIcon:`send`,loading:g,children:`Send reply`})})]})}function Z({label:e,children:t}){return N(`div`,{className:`d-flex flex-column`,children:[M(`span`,{className:`wui-text-xs fw-semibold text-body-secondary`,children:e}),M(`span`,{className:`wui-text-sm`,children:t})]})}function Re({ticketId:e,onBack:t,onChanged:n}){let{config:r}=K(),i=re(),a=W(),{ticket:o,loading:c,error:u,reload:d,reply:p,reopen:m,rate:h}=ae(e),[g,_]=s(!1),[v,y]=s(``),b=async e=>{let t=await e();return n?.(),t};async function x(){y(``),_(!0);try{await b(m)}catch(e){y(e?.message||`The ticket did not reopen. Please try again.`)}finally{_(!1)}}let S=M(f,{variant:`ghost`,size:`sm`,leftIcon:`arrow-left`,onClick:t,className:`align-self-start`,children:`Back to tickets`});if(!o)return N(`section`,{className:`d-flex flex-column gap-3`,"aria-busy":c||void 0,children:[S,u?N(l,{variant:`danger`,children:[u.message||`We could not load this ticket.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:d,children:`Try again`})]}):M(w,{height:160})]});let C=o.status===`Resolved`||o.status===`Closed`;return N(`section`,{className:`d-flex flex-column gap-3`,"aria-labelledby":`support-ticket-${o.id}`,children:[S,N(`header`,{className:`d-flex flex-column gap-2`,children:[N(`div`,{className:`d-flex flex-wrap align-items-center gap-2`,children:[M(`h2`,{id:`support-ticket-${o.id}`,className:`h5 mb-0 text-break`,children:o.subject}),o.statusLabel&&M(E,{label:o.statusLabel,tone:Ae(o.status)})]}),N(`div`,{className:`row row-cols-2 row-cols-md-4 g-3`,children:[M(Z,{label:`Ticket`,children:o.number}),M(Z,{label:`Category`,children:o.category||`-`}),M(Z,{label:`Opened`,children:M(X,{iso:o.createdDateTimeUtc})}),M(Z,{label:`Last update`,children:M(X,{iso:o.updatedDateTimeUtc})})]}),a===null&&N(`p`,{className:`wui-text-xs text-body-secondary mb-0`,children:[`Times are shown in `,ue(i),`.`]})]}),v&&M(l,{variant:`danger`,children:v}),C&&M(l,{variant:`success`,role:`status`,children:`If you still need help, reopen the ticket or send a reply.`}),o.status===`WaitingOnCustomer`&&M(l,{variant:`warning`,role:`status`,children:`We need your answer to carry on. Reply below and the ticket goes back to our team.`}),!C&&M(`p`,{className:`wui-text-sm text-body-secondary mb-0`,children:`Our team marks this resolved once your question is answered. Replying reopens it.`}),o.canReopen&&M(f,{variant:`secondary`,leftIcon:`history`,loading:g,onClick:x,className:`align-self-start`,children:`Reopen ticket`}),M(Ie,{satisfaction:o.satisfaction,comment:o.satisfactionComment,canRate:o.canRate,commentMaxLength:r?.satisfactionCommentMaxLength,onRate:e=>b(()=>h(e))}),M(Fe,{messages:o.messages??[]}),M(Le,{config:r,reopens:C,onSend:e=>b(()=>p(e))})]})}var Q=`ticket`;function ze(){return typeof window>`u`?null:new URLSearchParams(window.location.search).get(Q)}function Be(e){if(typeof window>`u`)return e?`?${Q}=${encodeURIComponent(e)}`:`?`;let t=new URL(window.location.href);return e?t.searchParams.set(Q,e):t.searchParams.delete(Q),`${t.pathname}${t.search}${t.hash}`}function Ve(e){if(typeof window>`u`)return;let t=Be(e);t!==`${window.location.pathname}${window.location.search}${window.location.hash}`&&window.history.replaceState(window.history.state,``,t)}var He=[{key:`all`,label:`All`,countKey:`total`},{key:`Open`,label:`Open`,countKey:`open`},{key:`InProgress`,label:`In progress`,countKey:`inProgress`},{key:`WaitingOnCustomer`,label:`Waiting on you`,countKey:`waitingOnCustomer`},{key:`Resolved`,label:`Resolved`,countKey:`resolved`},{key:`Closed`,label:`Closed`,countKey:`closed`}];function Ue({tickets:e,loading:t,error:n,onRetry:r,onOpen:i,status:a,onStatusChange:o,counts:s,search:u,onSearchChange:d,pagination:p}){let m=(e,t)=>{e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||(e.preventDefault(),i(t))};return N(`div`,{className:`d-flex flex-column gap-3`,children:[n&&N(l,{variant:`danger`,children:[n.message||`We could not load your tickets.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:r,children:`Try again`})]}),N(`div`,{className:`d-flex flex-column flex-md-row gap-2 align-items-md-center justify-content-between`,children:[M(O,{tabs:He.map(({key:e,label:t,countKey:n})=>({key:e,label:t,count:s?.[n]})),value:a,onChange:o,ariaLabel:`Filter tickets by status`,className:`flex-grow-1`}),M(S,{value:u,onChange:d,placeholder:`Search tickets`,ariaLabel:`Search tickets`})]}),M(h,{columns:[{key:`subject`,label:`Subject`,render:e=>N(`div`,{className:`d-flex flex-column`,style:{minWidth:0},children:[M(b,{href:Be(e.id),animation:`none`,className:`fw-semibold text-break`,onClick:t=>m(t,e.id),children:e.subject}),N(`span`,{className:`wui-text-xs text-body-secondary`,children:[e.number,`, `,e.category,e.awaitingCustomer?`. Waiting for your reply`:``]})]})},{key:`status`,label:`Status`,render:e=>e.statusLabel?M(E,{label:e.statusLabel,tone:Ae(e.status)}):`-`},{key:`messageCount`,label:`Messages`,cellClassName:`wui-text-sm text-body-secondary`,render:e=>e.messageCount},{key:`updatedDateTimeUtc`,label:`Last update`,cellClassName:`wui-text-sm text-body-secondary text-nowrap`,render:e=>M(X,{iso:e.updatedDateTimeUtc})}],rows:e,loading:t,skeletonRows:4,ariaLabel:`Support tickets`,rowActionsLabel:`Actions`,rowActions:e=>M(c,{icon:`eye`,label:`Open ticket ${e.subject}`,href:Be(e.id),onClick:t=>m(t,e.id)}),stickyActions:!0,stackOnMobile:!0,sourceCount:s?.total??0,pagination:p,empty:M(D,{icon:`message-square`,title:`No tickets yet`,text:`When you raise a ticket, it shows here with its status.`}),emptyFiltered:M(D,{icon:`search`,title:`No tickets match`,text:`Try another search or status.`})})]})}function We(e,t){let[n,i]=s(e);return r(()=>{let n=setTimeout(()=>i(e),t);return()=>clearTimeout(n)},[e,t]),n}var Ge=300,Ke=10;function qe({initialTicketId:e,defaultCategory:t,appVersion:n}){let{config:i,error:a,reload:o}=K(),[c,u]=s(`all`),[d,p]=s(``),m=We(d.trim(),Ge),[h,g]=s(Ke),_=`${c}|${m}|${h}`,[v,y]=s({key:_,page:1}),b=v.key===_?v.page:1,x=e=>y({key:_,page:e}),S=ie({status:c===`all`?void 0:c,search:m||void 0,page:b,pageSize:h}),{counts:C,reload:w}=q(),[T,E]=s(()=>e||ze()),[D,O]=s(!1),k=S.totalPages;r(()=>{k>0&&b>k&&y({key:_,page:k})},[k,b,_]);let A=()=>{S.reload(),w()},j=e=>{E(e),Ve(e)};if(T)return M(Re,{ticketId:T,onBack:()=>j(null),onChanged:A},T);let ee={page:b,totalPages:Math.max(1,S.totalPages),pageSize:h,total:S.totalCount,setPage:x,setPageSize:g};return N(`section`,{className:`d-flex flex-column gap-3`,style:{minWidth:0},"aria-labelledby":`support-center-title`,children:[N(`div`,{className:`d-flex flex-wrap align-items-center justify-content-between gap-2`,children:[M(`h2`,{id:`support-center-title`,className:`h5 mb-0`,children:`Your tickets`}),M(f,{leftIcon:`plus`,onClick:()=>O(!0),disabled:!i,children:`New ticket`})]}),a&&N(l,{variant:`warning`,children:[a.message||`We could not load the support settings.`,` `,M(f,{variant:`ghost`,size:`sm`,onClick:o,children:`Try again`})]}),M(Ue,{tickets:S.tickets,loading:S.loading,error:S.error,onRetry:A,onOpen:j,status:c,onStatusChange:u,counts:C,search:d,onSearchChange:p,pagination:ee}),i&&M(Oe,{open:D,onClose:()=>O(!1),config:i,defaultCategory:t,appVersion:n,onCreated:e=>{O(!1),A(),j(e.id)}})]})}var $=e=>typeof e==`string`&&e.trim()!==``;function Je({className:e}){let{config:t}=K();if(!t)return null;let n=[];return $(t.supportEmail)&&n.push({key:`email`,icon:`mail`,label:`Email`,value:N(`span`,{className:`d-inline-flex flex-wrap align-items-center gap-2`,children:[M(`a`,{href:`mailto:${t.supportEmail}`,className:`text-break`,children:t.supportEmail}),M(m,{text:t.supportEmail,size:`sm`,label:`Copy email`,copiedLabel:`Email copied`})]})}),$(t.supportPhone)&&n.push({key:`phone`,icon:`phone`,label:`Phone`,value:t.supportPhone,href:`tel:${t.supportPhone.replace(/[^\d+]/g,``)}`}),$(t.supportHours)&&n.push({key:`hours`,icon:`clock`,label:`Hours`,value:t.supportHours}),n.length===0?null:N(`section`,{className:e,"aria-label":$(t.supportName)?`Contact ${t.supportName}`:`Contact support`,children:[$(t.supportName)&&M(`p`,{className:`fw-semibold mb-2`,children:t.supportName}),M(v,{items:n})]})}function Ye({client:e,timeZone:n=J,renderTime:r,children:i}){let s=o(null),c=t((t=!1)=>{let n=s.current;if(t||!n||n.client!==e){let t=e.getConfig(),n={client:e,pending:t};return s.current=n,t.catch(()=>{s.current===n&&(s.current=null)}),t}return n.pending},[e]),l=a(()=>({client:e,loadConfig:c,timeZone:n,renderTime:r}),[e,c,n,r]);return M(V.Provider,{value:l,children:i})}export{De as ReportIssue,F as SupportApiError,qe as SupportCenter,Je as SupportContact,Ye as SupportProvider,ne as createSupportClient,U as useSupportClient,K as useSupportConfig,ae as useSupportTicket,q as useSupportTicketCounts,ie as useSupportTickets};
@@ -4,10 +4,12 @@
4
4
  * @param {object} props
5
5
  * @param {import('./client.js').SupportClient} props.client From `createSupportClient(http)`.
6
6
  * @param {string} [props.timeZone="Asia/Kolkata"] IANA zone every ticket and message time is shown in.
7
+ * @param {(iso: string) => import('react').ReactNode} [props.renderTime] Draws every ticket and message time instead of the built-in date and time. When set, the ticket's "Times are shown in" note is left out, because the product's own rendering decides the zone.
7
8
  * @param {import('react').ReactNode} props.children
8
9
  */
9
- export default function SupportProvider({ client, timeZone, children }: {
10
+ export default function SupportProvider({ client, timeZone, renderTime, children }: {
10
11
  client: import('./client.js').SupportClient;
11
12
  timeZone?: string;
13
+ renderTime?: (iso: string) => import('react').ReactNode;
12
14
  children: import('react').ReactNode;
13
15
  }): import("react").JSX.Element;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A ticket or message time: the product's renderTime when the provider has one, otherwise the built-in date and time.
3
+ *
4
+ * @param {object} props
5
+ * @param {string} props.iso
6
+ * @param {string} [props.className]
7
+ */
8
+ export default function SupportTime({ iso, className }: {
9
+ iso: string;
10
+ className?: string;
11
+ }): string | number | bigint | boolean | import("react").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | Iterable<import("react").ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal>;
@@ -1,8 +1,9 @@
1
- /** @type {import('react').Context<{ client: import('./client.js').SupportClient, loadConfig: (fresh?: boolean) => Promise<any>, timeZone: string } | null>} */
1
+ /** @type {import('react').Context<{ client: import('./client.js').SupportClient, loadConfig: (fresh?: boolean) => Promise<any>, timeZone: string, renderTime?: (iso: string) => import('react').ReactNode } | null>} */
2
2
  export declare const SupportContext: import('react').Context<{
3
3
  client: import('./client.js').SupportClient;
4
4
  loadConfig: (fresh?: boolean) => Promise<any>;
5
5
  timeZone: string;
6
+ renderTime?: (iso: string) => import('react').ReactNode;
6
7
  } | null>;
7
8
  /**
8
9
  * The support client the nearest SupportProvider holds.
@@ -16,6 +17,12 @@ export declare function useSupportClient(): import('./client.js').SupportClient;
16
17
  * @returns {string}
17
18
  */
18
19
  export declare function useSupportTimeZone(): string;
20
+ /**
21
+ * The product's own time renderer the nearest SupportProvider holds, or null for the built-in one.
22
+ *
23
+ * @returns {((iso: string) => import('react').ReactNode)|null}
24
+ */
25
+ export declare function useSupportRenderTime(): ((iso: string) => import('react').ReactNode) | null;
19
26
  /**
20
27
  * What the customer may send and how to reach support. Loaded once per provider and shared.
21
28
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/support-react",
3
- "version": "0.68.3",
3
+ "version": "0.70.1",
4
4
  "description": "Webority customer support for React portals: ticket centre, report-an-issue with an optional page screenshot, and support contact details, built on @webority/ui-react.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -30,7 +30,7 @@
30
30
  "react": "^19.0.0",
31
31
  "react-bootstrap": "^2.10.0",
32
32
  "react-dom": "^19.0.0",
33
- "@webority/ui-react": "0.68.3"
33
+ "@webority/ui-react": "0.70.1"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@testing-library/dom": "10.4.2",
@@ -48,7 +48,7 @@
48
48
  "typescript": "^7.0.2",
49
49
  "vite": "^8.0.12",
50
50
  "vitest": "5.0.1",
51
- "@webority/ui-react": "0.68.3"
51
+ "@webority/ui-react": "0.70.1"
52
52
  },
53
53
  "dependencies": {
54
54
  "html-to-image": "^1.11.13"