@webority/support-react 0.13.1 → 0.13.4

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.
@@ -11,8 +11,11 @@
11
11
  * @param {(id: string) => string} [props.getTicketHref] Where the new ticket can be opened; shows a View ticket link.
12
12
  * @param {(ticket: any) => void} [props.onOpenTicket] Called by the View ticket button when there is no href.
13
13
  * @param {(ticket: any) => void} [props.onCreated] Called once the ticket is raised.
14
+ * @param {string} [props.className] Added to the button's wrapper, both floating and inline, to place it.
15
+ * @param {import('react').CSSProperties} [props.style] Merged over the wrapper's own style. With
16
+ * `floating`, an inset such as `{ bottom: "5rem" }` moves the button clear of other fixed controls.
14
17
  */
15
- export default function ReportIssue({ label, floating, defaultCategory, appVersion, getTicketHref, onOpenTicket, onCreated, }: {
18
+ export default function ReportIssue({ label, floating, defaultCategory, appVersion, getTicketHref, onOpenTicket, onCreated, className, style, }: {
16
19
  label?: string;
17
20
  floating?: boolean;
18
21
  defaultCategory?: string;
@@ -20,4 +23,6 @@ export default function ReportIssue({ label, floating, defaultCategory, appVersi
20
23
  getTicketHref?: (id: string) => string;
21
24
  onOpenTicket?: (ticket: any) => void;
22
25
  onCreated?: (ticket: any) => void;
26
+ className?: string;
27
+ style?: import('react').CSSProperties;
23
28
  }): import("react").JSX.Element;
@@ -2,6 +2,7 @@
2
2
  * The customer's support page: their tickets, one ticket's conversation, and a New ticket
3
3
  * dialog. Opens the ticket named by `?ticket=<id>` in the address (support emails link there),
4
4
  * or `initialTicketId` when given, and keeps that parameter in step as tickets open and close.
5
+ * The list is filtered, searched and paged on the server; the status tabs show the server's counts.
5
6
  *
6
7
  * @param {object} props
7
8
  * @param {string} [props.initialTicketId] Open this ticket first. Wins over the address.
@@ -44,9 +44,43 @@ export type RatingInput = {
44
44
  satisfaction: "Satisfied" | "Unsatisfied";
45
45
  comment?: string;
46
46
  };
47
+ export type TicketStatus = "Open" | "InProgress" | "Resolved" | "Closed";
48
+ export type TicketListQuery = {
49
+ /**
50
+ * Only tickets in this status; every status when left out.
51
+ */
52
+ status?: TicketStatus;
53
+ /**
54
+ * Matches the subject or the ticket number.
55
+ */
56
+ search?: string;
57
+ /**
58
+ * 1-based. The API defaults to 1.
59
+ */
60
+ page?: number;
61
+ /**
62
+ * The API defaults to 20 and allows up to 100.
63
+ */
64
+ pageSize?: number;
65
+ };
66
+ export type TicketPage = {
67
+ items: any[];
68
+ page: number;
69
+ pageSize: number;
70
+ totalCount: number;
71
+ totalPages: number;
72
+ };
73
+ export type TicketCounts = {
74
+ open: number;
75
+ inProgress: number;
76
+ resolved: number;
77
+ closed: number;
78
+ total: number;
79
+ };
47
80
  export type SupportClient = {
48
81
  getConfig: () => Promise<any>;
49
- listTickets: () => Promise<any[]>;
82
+ listTickets: (query?: TicketListQuery) => Promise<TicketPage>;
83
+ getTicketCounts: () => Promise<TicketCounts>;
50
84
  getTicket: (id: string) => Promise<any>;
51
85
  createTicket: (input: NewTicketInput) => Promise<any>;
52
86
  reply: (id: string, input: ReplyInput) => Promise<any>;
@@ -81,10 +115,37 @@ export type SupportClient = {
81
115
  * @property {"Satisfied"|"Unsatisfied"} satisfaction
82
116
  * @property {string} [comment]
83
117
  */
118
+ /**
119
+ * @typedef {"Open"|"InProgress"|"Resolved"|"Closed"} TicketStatus
120
+ */
121
+ /**
122
+ * @typedef {object} TicketListQuery
123
+ * @property {TicketStatus} [status] Only tickets in this status; every status when left out.
124
+ * @property {string} [search] Matches the subject or the ticket number.
125
+ * @property {number} [page] 1-based. The API defaults to 1.
126
+ * @property {number} [pageSize] The API defaults to 20 and allows up to 100.
127
+ */
128
+ /**
129
+ * @typedef {object} TicketPage One page of the customer's tickets, most recently updated first.
130
+ * @property {any[]} items
131
+ * @property {number} page
132
+ * @property {number} pageSize
133
+ * @property {number} totalCount
134
+ * @property {number} totalPages
135
+ */
136
+ /**
137
+ * @typedef {object} TicketCounts How many of the customer's tickets are in each status.
138
+ * @property {number} open
139
+ * @property {number} inProgress
140
+ * @property {number} resolved
141
+ * @property {number} closed
142
+ * @property {number} total
143
+ */
84
144
  /**
85
145
  * @typedef {object} SupportClient
86
146
  * @property {() => Promise<any>} getConfig
87
- * @property {() => Promise<any[]>} listTickets
147
+ * @property {(query?: TicketListQuery) => Promise<TicketPage>} listTickets
148
+ * @property {() => Promise<TicketCounts>} getTicketCounts
88
149
  * @property {(id: string) => Promise<any>} getTicket
89
150
  * @property {(input: NewTicketInput) => Promise<any>} createTicket
90
151
  * @property {(id: string, input: ReplyInput) => Promise<any>} reply
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * Picked, dropped or pasted files for a message, limited to what the support config allows.
3
+ * The caller checks every file against the config and passes the one message through `error`,
4
+ * so the field's own check stays off and a rejected pick never shows two messages.
5
+ * A limit the config leaves open (no types listed, a size of 0) is passed as open, so the
6
+ * field accepts any file and its hint names only the limits that exist.
3
7
  *
4
8
  * @param {object} props
5
9
  * @param {string} [props.id]
@@ -1,17 +1,39 @@
1
1
  /**
2
- * The customer's tickets with status tabs and a search box.
2
+ * One page of the customer's tickets with status tabs and a search box. Filtering, search and
3
+ * paging all happen on the server; this only shows what it is given.
3
4
  *
4
5
  * @param {object} props
5
- * @param {any[]} props.tickets
6
+ * @param {any[]} props.tickets The current page.
6
7
  * @param {boolean} props.loading
7
8
  * @param {any} [props.error]
8
9
  * @param {() => void} props.onRetry
9
10
  * @param {(id: string) => void} props.onOpen
11
+ * @param {string} props.status A `STATUS_FILTERS` key.
12
+ * @param {(status: string) => void} props.onStatusChange
13
+ * @param {import('../client.js').TicketCounts|null} [props.counts] Labels the tabs once loaded.
14
+ * @param {string} props.search What is in the search box.
15
+ * @param {(search: string) => void} props.onSearchChange
16
+ * @param {{ page: number, totalPages: number, pageSize: number, total: number, firstRow: number, lastRow: number, setPage: (page: number) => void, setPageSize: (size: number) => void }} props.pagination
10
17
  */
11
- export default function TicketList({ tickets, loading, error, onRetry, onOpen }: {
18
+ export default function TicketList({ tickets, loading, error, onRetry, onOpen, status, onStatusChange, counts, search, onSearchChange, pagination, }: {
12
19
  tickets: any[];
13
20
  loading: boolean;
14
21
  error?: any;
15
22
  onRetry: () => void;
16
23
  onOpen: (id: string) => void;
24
+ status: string;
25
+ onStatusChange: (status: string) => void;
26
+ counts?: import('../client.js').TicketCounts | null;
27
+ search: string;
28
+ onSearchChange: (search: string) => void;
29
+ pagination: {
30
+ page: number;
31
+ totalPages: number;
32
+ pageSize: number;
33
+ total: number;
34
+ firstRow: number;
35
+ lastRow: number;
36
+ setPage: (page: number) => void;
37
+ setPageSize: (size: number) => void;
38
+ };
17
39
  }): import("react").JSX.Element;
@@ -21,12 +21,27 @@ export declare function useSupportConfig(): {
21
21
  reload: () => void;
22
22
  };
23
23
  /**
24
- * The signed-in customer's tickets.
24
+ * One page of the signed-in customer's tickets, filtered and searched on the server. Loads again
25
+ * whenever any part of the query changes; an answer for a query that has since moved on is dropped.
25
26
  *
26
- * @returns {{ tickets: any[], loading: boolean, error: any, reload: () => void }}
27
+ * @param {import('./client.js').TicketListQuery} [query]
28
+ * @returns {{ tickets: any[], totalCount: number, totalPages: number, loading: boolean, error: any, reload: () => void }}
27
29
  */
28
- export declare function useSupportTickets(): {
30
+ export declare function useSupportTickets(query?: import('./client.js').TicketListQuery): {
29
31
  tickets: any[];
32
+ totalCount: number;
33
+ totalPages: number;
34
+ loading: boolean;
35
+ error: any;
36
+ reload: () => void;
37
+ };
38
+ /**
39
+ * How many of the signed-in customer's tickets are in each status, for labelling status tabs.
40
+ *
41
+ * @returns {{ counts: import('./client.js').TicketCounts|null, loading: boolean, error: any, reload: () => void }}
42
+ */
43
+ export declare function useSupportTicketCounts(): {
44
+ counts: import('./client.js').TicketCounts | null;
30
45
  loading: boolean;
31
46
  error: any;
32
47
  reload: () => void;
@@ -1,5 +1,5 @@
1
1
  export { createSupportClient, SupportApiError } from "./client.js";
2
- export { useSupportClient, useSupportConfig, useSupportTicket, useSupportTickets } from "./context.js";
2
+ export { useSupportClient, useSupportConfig, useSupportTicket, useSupportTicketCounts, useSupportTickets } from "./context.js";
3
3
  export { default as ReportIssue } from "./ReportIssue.jsx";
4
4
  export { default as SupportCenter } from "./SupportCenter.jsx";
5
5
  export { default as SupportContact } from "./SupportContact.jsx";
@@ -1,12 +1,14 @@
1
1
  /**
2
- * The `accept` attribute for a file input, from the config's allowed content types.
2
+ * The `accept` attribute for a file input, from the config's allowed content types. An empty
3
+ * list means any type, so this returns "" rather than undefined: undefined would let the field
4
+ * fall back to its own images-only default.
3
5
  *
4
6
  * @param {{ allowedContentTypes?: string[] }|null|undefined} config
5
- * @returns {string|undefined}
7
+ * @returns {string}
6
8
  */
7
9
  export declare function acceptOf(config: {
8
10
  allowedContentTypes?: string[];
9
- } | null | undefined): string | undefined;
11
+ } | null | undefined): string;
10
12
  /**
11
13
  * Checks files against the config before anything uploads. Returns the first problem as a
12
14
  * sentence for the person, or null when every file can go.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `value`, once it has stopped changing for `delayMs`. Lets a search box ask the server once
3
+ * per pause in typing instead of once per key.
4
+ *
5
+ * @template T
6
+ * @param {T} value
7
+ * @param {number} delayMs
8
+ * @returns {T}
9
+ */
10
+ export declare function useDebouncedValue<T>(value: T, delayMs: number): T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/support-react",
3
- "version": "0.13.1",
3
+ "version": "0.13.4",
4
4
  "description": "Webority customer support for React portals: ticket centre, report-an-issue with a page screenshot, and support contact details, built on @webority/ui-react.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "type": "module",
@@ -32,7 +32,7 @@
32
32
  "react": "^19.0.0",
33
33
  "react-bootstrap": "^2.10.0",
34
34
  "react-dom": "^19.0.0",
35
- "@webority/ui-react": "0.13.1"
35
+ "@webority/ui-react": "0.13.4"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/dom": "10.4.2",
@@ -50,7 +50,7 @@
50
50
  "typescript": "^7.0.2",
51
51
  "vite": "^8.0.12",
52
52
  "vitest": "5.0.1",
53
- "@webority/ui-react": "0.13.1"
53
+ "@webority/ui-react": "0.13.4"
54
54
  },
55
55
  "dependencies": {
56
56
  "html-to-image": "^1.11.13"