ui-soxo-bootstrap-core 2.6.62-dev.4 → 2.6.63-dev.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.
@@ -7,7 +7,7 @@ import { motion, useAnimation } from 'framer-motion';
7
7
  import { boxVariants } from './animations';
8
8
  import { GlobalContext, GlobalProvider } from './../../Store';
9
9
  import { Link, useLocation } from 'react-router-dom';
10
- import { Avatar, Input, Tooltip, Typography } from 'antd';
10
+ import { Avatar, Input, Tooltip, Typography, message } from 'antd';
11
11
  import ProgressBar from '../progress-bar/progress-bar'; // Adjust the path as needed
12
12
  import { Button } from '../../elements';
13
13
  import GenericHeader from '../header/generic-header';
@@ -20,23 +20,53 @@ import LanguageSwitcher from '../language-switcher/language-switcher';
20
20
  import { useTranslation } from 'react-i18next';
21
21
  import SettingsUtil from '../../../utils/settings.utils';
22
22
  import SpotlightSearch from '../spotlight-search/spotlight-search.component';
23
- import LicenseAlert from '../../../components/license-management/license-alert';
23
+ import LicenseAlert from '../../../components/license-management/license-alert';
24
24
  import SupportDesk from '../support-desk/support-desk';
25
- import { resolveSupportConfig } from '../support-desk/support-desk.utils';
25
+ import { MODES, getSupportUrl, resolveSupportConfig } from '../support-desk/support-desk.utils';
26
26
 
27
27
  const { Title } = Typography;
28
- function GlobalHeaderContent({ loading, appSettings, children, isConnected, history, modules = [], sidemenu = [], reload, meta = {},
29
- licAlert,
30
- licenseData,
31
- ...props }) {
28
+ function GlobalHeaderContent({
29
+ loading,
30
+ appSettings,
31
+ children,
32
+ isConnected,
33
+ history,
34
+ modules = [],
35
+ sidemenu = [],
36
+ reload,
37
+ meta = {},
38
+ licAlert,
39
+ licenseData,
40
+ ...props
41
+ }) {
32
42
  let location = useLocation();
33
43
  // let location = {};
34
44
  const { isMobile, user = { locations: [] }, kiosk, state, settings } = useContext(GlobalContext);
35
45
  const [visible, setVisible] = useState(false);
36
46
  const helpDeskSetting = settings?.HELPATR || {};
37
47
  // Memoised: the drawer's effects depend on this object, so it must keep its identity across renders.
38
- const supportConfig = useMemo(() => resolveSupportConfig(helpDeskSetting, user), [helpDeskSetting, user]);
48
+ const supportConfig = useMemo(() => resolveSupportConfig(helpDeskSetting), [helpDeskSetting]);
39
49
  const [supportOpen, setSupportOpen] = useState(false);
50
+ const [supportLoading, setSupportLoading] = useState(false);
51
+ // Link fetched on Help click, handed to the drawer so the form does not request it twice.
52
+ const [supportUrl, setSupportUrl] = useState('');
53
+
54
+ /**
55
+ * Fetch the first link before opening the drawer, so the button shows a loader and a
56
+ * failure is reported without opening an empty drawer.
57
+ */
58
+ const openSupport = async () => {
59
+ if (supportLoading) return;
60
+ setSupportLoading(true);
61
+ try {
62
+ setSupportUrl(await getSupportUrl(supportConfig, MODES.RAISE));
63
+ setSupportOpen(true);
64
+ } catch (err) {
65
+ message.error(err?.message || 'Unable to open Support right now.');
66
+ } finally {
67
+ setSupportLoading(false);
68
+ }
69
+ };
40
70
  // Variable to handle toggling of menu
41
71
  const [collapsed, setCollapsed] = useState(false);
42
72
  // varibale handle branch switcher
@@ -88,42 +118,28 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
88
118
  useEffect(() => {}, [state.theme]);
89
119
  return (
90
120
  <>
91
- <div
92
- className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? 'connected' : ''}`}
93
- style={{
94
- // background: state.theme.colors.bodyBackground,
95
- height: 'auto',
96
- }}
97
- >
98
- {/* <MenuOutlined style={{left:'1%',top:'1%', fontSize:18, position:'absolute', zIndex:999}} onClick={showSidebar}/> */}
99
- <div className="layout-content">
100
- <div
101
- //whileHover="hover"
102
- //whileTap="tap"
103
- className={`left-bar ${!isConnected && !isMobile ? '' : 'hide'}${!collapsed ? 'open' : 'close'}${kiosk && !isConnected ? 'kioskon' : ''}`}
104
- // style={{ background: state.theme.colors.leftSectionBackground }}
105
- >
106
- {!isMobile && !isConnected ? (
107
- <motion.div
108
- variants={boxVariants}
109
- initial={['entering']}
110
- animate={boxControls}
111
- className={`sidebar-container ${!collapsed ? 'open' : 'close'}${kiosk ? '' : 'kioskon'}`}
112
- >
113
- <SideMenu
114
- // isOpen={isOpen}
115
- collapsed={collapsed}
116
- setCollapsed={setCollapsed}
117
- loading={loading}
118
- appSettings={appSettings}
119
- callback={onClose}
120
- modules={modules}
121
- history={history}
122
- />
123
- </motion.div>
124
- ) : (
125
- <Drawer placement="left" onClose={onClose} visible={visible} className="side-drawer-content">
126
- <div className={`sidebar-container ${!collapsed ? 'open' : 'close'}`}>
121
+ <div
122
+ className={`global-header ${process.env.REACT_APP_THEME} ${isConnected && !kiosk ? 'connected' : ''}`}
123
+ style={{
124
+ // background: state.theme.colors.bodyBackground,
125
+ height: 'auto',
126
+ }}
127
+ >
128
+ {/* <MenuOutlined style={{left:'1%',top:'1%', fontSize:18, position:'absolute', zIndex:999}} onClick={showSidebar}/> */}
129
+ <div className="layout-content">
130
+ <div
131
+ //whileHover="hover"
132
+ //whileTap="tap"
133
+ className={`left-bar ${!isConnected && !isMobile ? '' : 'hide'}${!collapsed ? 'open' : 'close'}${kiosk && !isConnected ? 'kioskon' : ''}`}
134
+ // style={{ background: state.theme.colors.leftSectionBackground }}
135
+ >
136
+ {!isMobile && !isConnected ? (
137
+ <motion.div
138
+ variants={boxVariants}
139
+ initial={['entering']}
140
+ animate={boxControls}
141
+ className={`sidebar-container ${!collapsed ? 'open' : 'close'}${kiosk ? '' : 'kioskon'}`}
142
+ >
127
143
  <SideMenu
128
144
  // isOpen={isOpen}
129
145
  collapsed={collapsed}
@@ -134,120 +150,144 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
134
150
  modules={modules}
135
151
  history={history}
136
152
  />
137
- </div>
138
- </Drawer>
139
- )}
140
- </div>
141
- {/* Right Section of the Component Loader */}
142
- <div
143
- className={`right-section ${!collapsed ? 'open' : 'close'} ${kiosk ? 'kioskon' : ''}`}
144
- style={{ background: state.theme.colors.bodyBackground }}
145
- >
146
- {/* For not connected section */}
147
- {!isConnected && !kiosk ? (
148
- <div
149
- className={`page-wrapper ${!collapsed ? 'open' : 'close'}`}
150
- // style={{ background: state.theme.colors.headerBg }}
151
- >
152
- {/* */}
153
- <div className="page-header-name">
154
- <ProgressBar isLoading={loading} />
155
- <span type onClick={!isMobile ? toggleCollapsed : hideToggle} className="toggle-box toggle-menu">
156
- <MenuOutlined />
157
- </span>
158
- {/* Back Button */}
159
- {location.pathname !== '/' ? (
160
- <span
161
- className="toggle-box"
162
- onClick={() => {
163
- history && history.goBack();
164
- }}
165
- >
166
- <GenericHeader />
153
+ </motion.div>
154
+ ) : (
155
+ <Drawer placement="left" onClose={onClose} visible={visible} className="side-drawer-content">
156
+ <div className={`sidebar-container ${!collapsed ? 'open' : 'close'}`}>
157
+ <SideMenu
158
+ // isOpen={isOpen}
159
+ collapsed={collapsed}
160
+ setCollapsed={setCollapsed}
161
+ loading={loading}
162
+ appSettings={appSettings}
163
+ callback={onClose}
164
+ modules={modules}
165
+ history={history}
166
+ />
167
+ </div>
168
+ </Drawer>
169
+ )}
170
+ </div>
171
+ {/* Right Section of the Component Loader */}
172
+ <div
173
+ className={`right-section ${!collapsed ? 'open' : 'close'} ${kiosk ? 'kioskon' : ''}`}
174
+ style={{ background: state.theme.colors.bodyBackground }}
175
+ >
176
+ {/* For not connected section */}
177
+ {!isConnected && !kiosk ? (
178
+ <div
179
+ className={`page-wrapper ${!collapsed ? 'open' : 'close'}`}
180
+ // style={{ background: state.theme.colors.headerBg }}
181
+ >
182
+ {/* */}
183
+ <div className="page-header-name">
184
+ <ProgressBar isLoading={loading} />
185
+ <span type onClick={!isMobile ? toggleCollapsed : hideToggle} className="toggle-box toggle-menu">
186
+ <MenuOutlined />
167
187
  </span>
168
- ) : null}
169
- {/* Back Button Ends */}
170
- {location.pathname !== '/' ? (
171
- <h4 className="menu-caption header-caption" style={{ color: state.theme.colors.headerColor }}>
172
- {menu.caption}
173
- </h4>
174
- ) : null}
175
- </div>
176
- {/* Page Menu Actions */}
177
- {user.role || user.id ? (
178
- <div className="page-menu">
179
- {/* Search Input in header start */}
180
- {!isMobile && (
181
- <div>
182
- <Input placeholder="Search (Shift + F)" prefix={<SearchOutlined />} onClick={openSearchModal} readOnly style={{ width: 250 }} />
183
- <SpotlightSearch ref={(elem) => SettingsUtil.registerModal(elem)} props={props} />
184
- </div>
185
- )}
186
- {/* Search Input in header start */}
187
- {/** branchswitcher Option */}
188
- {/* branch switcher controlled with env for matria and nura */}
189
- {!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? <div className="branch-switcher">{globalCustomerHeader()}</div> : null}
190
- {/* <div className="branch-switcher">{globalCustomerHeader()}</div> */}
191
- {/* Search Option */}
192
- {/* <ModalSearch /> */}
193
- {/* Search Option Ends */}
194
- {/* Configurator Actions */}
195
- {user.isAdmin ? (
196
- <>
197
- {/* Models */}
198
- {menu && menu.id ? (
199
- <Link to={`/menus/${menu.id}`}>
200
- <Button type="default" size={'small'} icon={<SettingOutlined />}></Button>
201
- </Link>
202
- ) : null}
203
- {/* Models Ends */}
204
- </>
188
+ {/* Back Button */}
189
+ {location.pathname !== '/' ? (
190
+ <span
191
+ className="toggle-box"
192
+ onClick={() => {
193
+ history && history.goBack();
194
+ }}
195
+ >
196
+ <GenericHeader />
197
+ </span>
205
198
  ) : null}
206
- {/* Configurator Actions Ends */}
207
- {/* Reload Button */}
208
- <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={'small'}></Button>
209
- {/* Reload Button Ends */}
210
- {/* Help-desk-btn */}
211
- {/* Shown only when the support settings are complete, so the button never does nothing */}
212
- {helpDeskSetting?.showSupportBtn && supportConfig.enabled ? (
213
- <Tooltip title={helpDeskSetting?.toolTipText} overlayClassName="modern-tooltip">
214
- <span>
215
- <Button onClick={() => setSupportOpen(true)} type="default" size="small">
216
- {helpDeskSetting?.buttonText || 'Help'}
217
- </Button>
218
- </span>
219
- </Tooltip>
199
+ {/* Back Button Ends */}
200
+ {location.pathname !== '/' ? (
201
+ <h4 className="menu-caption header-caption" style={{ color: state.theme.colors.headerColor }}>
202
+ {menu.caption}
203
+ </h4>
220
204
  ) : null}
221
- {/** Switch Languages starts */}
222
- {process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? <LanguageSwitcher /> : null}
223
- {/** Switch Languages ends */}
224
- {/* User Profile */}
225
- <div style={{ padding: '10px' }}>
226
- <ProfileAvatar />
227
- <span style={{ color: state.theme.colors.colorText }}> {user.name} </span>
228
- </div>
229
- {/* User Profile Ends */}
230
205
  </div>
231
- ) : null}
232
- {/* Page Menu Actions Ends */}
233
- </div>
234
- ) : null}
235
- {/* The children is rendered */}
236
- {children}
237
- {/* The children is rendered */}
206
+ {/* Page Menu Actions */}
207
+ {user.role || user.id ? (
208
+ <div className="page-menu">
209
+ {/* Search Input in header start */}
210
+ {!isMobile && (
211
+ <div>
212
+ <Input
213
+ placeholder="Search (Shift + F)"
214
+ prefix={<SearchOutlined />}
215
+ onClick={openSearchModal}
216
+ readOnly
217
+ style={{ width: 250 }}
218
+ />
219
+ <SpotlightSearch ref={(elem) => SettingsUtil.registerModal(elem)} props={props} />
220
+ </div>
221
+ )}
222
+ {/* Search Input in header start */}
223
+ {/** branchswitcher Option */}
224
+ {/* branch switcher controlled with env for matria and nura */}
225
+ {!process.env.REACT_APP_SHOW_BRANCH_SWITCHER ? <div className="branch-switcher">{globalCustomerHeader()}</div> : null}
226
+ {/* <div className="branch-switcher">{globalCustomerHeader()}</div> */}
227
+ {/* Search Option */}
228
+ {/* <ModalSearch /> */}
229
+ {/* Search Option Ends */}
230
+ {/* Configurator Actions */}
231
+ {user.isAdmin ? (
232
+ <>
233
+ {/* Models */}
234
+ {menu && menu.id ? (
235
+ <Link to={`/menus/${menu.id}`}>
236
+ <Button type="default" size={'small'} icon={<SettingOutlined />}></Button>
237
+ </Link>
238
+ ) : null}
239
+ {/* Models Ends */}
240
+ </>
241
+ ) : null}
242
+ {/* Configurator Actions Ends */}
243
+ {/* Reload Button */}
244
+ <Button onClick={reload} icon={<ReloadOutlined />} type="default" size={'small'}></Button>
245
+ {/* Reload Button Ends */}
246
+ {/* Help-desk-btn */}
247
+ {/* Shown only when the support settings are complete, so the button never does nothing */}
248
+ {helpDeskSetting?.showSupportBtn && supportConfig.enabled ? (
249
+ <Tooltip title={helpDeskSetting?.toolTipText} overlayClassName="modern-tooltip">
250
+ <span>
251
+ <Button onClick={openSupport} loading={supportLoading} type="default" size="small">
252
+ {helpDeskSetting?.buttonText || 'Help'}
253
+ </Button>
254
+ </span>
255
+ </Tooltip>
256
+ ) : null}
257
+ {/** Switch Languages starts */}
258
+ {process.env.REACT_APP_ENABLE_LANGUAGE_SWITCHER ? <LanguageSwitcher /> : null}
259
+ {/** Switch Languages ends */}
260
+ {/* User Profile */}
261
+ <div style={{ padding: '10px' }}>
262
+ <ProfileAvatar />
263
+ <span style={{ color: state.theme.colors.colorText }}> {user.name} </span>
264
+ </div>
265
+ {/* User Profile Ends */}
266
+ </div>
267
+ ) : null}
268
+ {/* Page Menu Actions Ends */}
269
+ </div>
270
+ ) : null}
271
+ {/* The children is rendered */}
272
+ {children}
273
+ {/* The children is rendered */}
274
+ </div>
275
+ {/* Right Section of the Component Loader Ends */}
238
276
  </div>
239
- {/* Right Section of the Component Loader Ends */}
240
277
  </div>
241
- </div>
242
-
243
278
 
244
-
245
- {supportConfig.enabled && supportOpen ? (
246
- <SupportDesk open={supportOpen} onClose={() => setSupportOpen(false)} config={supportConfig} title={helpDeskSetting?.buttonText || 'Support'} />
279
+ {supportConfig.enabled && supportOpen ? (
280
+ <SupportDesk
281
+ open={supportOpen}
282
+ onClose={() => setSupportOpen(false)}
283
+ config={supportConfig}
284
+ initialUrl={supportUrl}
285
+ title={helpDeskSetting?.buttonText || 'Support'}
286
+ />
247
287
  ) : null}
248
288
 
249
- {licAlert && licenseData && (
250
- <div
289
+ {licAlert && licenseData && (
290
+ <div
251
291
  style={{
252
292
  top: 0,
253
293
  marginTop: '3rem',
@@ -258,8 +298,8 @@ function GlobalHeaderContent({ loading, appSettings, children, isConnected, hist
258
298
  >
259
299
  <LicenseAlert data={licenseData} />
260
300
  </div>
261
- )}
262
- </>
301
+ )}
302
+ </>
263
303
  );
264
304
  }
265
305
  export default function GlobalHeader(props) {
@@ -56,6 +56,14 @@ import TableWrapper from './table-wrapper/table-wrapper'
56
56
 
57
57
  import TableComponent from '../elements/basic/table/table'
58
58
 
59
+ import {
60
+ withColumnFilters,
61
+ applyColumnFilters,
62
+ hasColumnFilters,
63
+ getColumnFilterValue,
64
+ ColumnFilterDropdown,
65
+ } from '../elements/basic/table/column-filter/column-filter'
66
+
59
67
  import CustomActions from '../models/actions/components/custom-actions/custom-actions'
60
68
 
61
69
  // Generic Components Ends
@@ -154,6 +162,11 @@ export {
154
162
 
155
163
  TableWrapper,
156
164
  TableComponent,
165
+ withColumnFilters,
166
+ applyColumnFilters,
167
+ hasColumnFilters,
168
+ getColumnFilterValue,
169
+ ColumnFilterDropdown,
157
170
  CustomActions,
158
171
 
159
172
  // Generic Components Ends
@@ -1,21 +1,24 @@
1
1
  /**
2
- * "Raise a Ticket" tab: the Helpdesk form in an iframe.
2
+ * One Helpdesk page in an iframe ("Raise a Ticket" or "My Tickets").
3
3
  *
4
- * The link is minted when the tab opens, so an idle session never holds an expired token.
4
+ * The link is requested when the tab opens and on every Reload, so an idle session never
5
+ * holds an expired token.
5
6
  *
6
- * Frappe's session cookie is SameSite=Lax, so some browsers render the embedded form blank.
7
- * If the form has not loaded after FORM_SLOW_MS we offer "Open in new tab", which opens a
7
+ * Frappe's session cookie is SameSite=Lax, so some browsers render the embedded page blank.
8
+ * If the page has not loaded after FORM_SLOW_MS we offer "Open in new tab", which opens a
8
9
  * fresh link as a top level page.
9
10
  */
10
11
  import { useCallback, useEffect, useRef, useState } from 'react';
11
12
  import { Alert, Button, Result, Spin, message } from 'antd';
12
- import { ExportOutlined, ReloadOutlined } from '@ant-design/icons';
13
- import { FORM_SLOW_MS, buildFormUrl, openInNewTab } from '../../support-desk.utils';
13
+ import { ExportOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
14
+ import { FORM_SLOW_MS, getSupportUrl, openInNewTab } from '../../support-desk.utils';
14
15
 
15
- export default function RaiseTicket({ config }) {
16
- const [url, setUrl] = useState('');
16
+ export default function SupportFrame({ config, mode, intro, loadingText, errorTitle, initialUrl = '', allowAnother = false }) {
17
+ const [url, setUrl] = useState(initialUrl);
17
18
  // minting -> loading (iframe requested) -> ready (iframe finished) | error
18
- const [phase, setPhase] = useState('minting');
19
+ const [phase, setPhase] = useState(initialUrl ? 'loading' : 'minting');
20
+ // A link passed in by the caller is used once; Reload always asks for a new one.
21
+ const initialRef = useRef(initialUrl);
19
22
  const [error, setError] = useState('');
20
23
  const [slow, setSlow] = useState(false);
21
24
 
@@ -30,19 +33,23 @@ export default function RaiseTicket({ config }) {
30
33
  setSlow(false);
31
34
 
32
35
  try {
33
- const next = await buildFormUrl(config);
36
+ const next = await getSupportUrl(config, mode);
34
37
  if (requestRef.current !== requestId) return;
35
38
  setUrl(next);
36
39
  setPhase('loading');
37
40
  } catch (err) {
38
41
  if (requestRef.current !== requestId) return;
39
42
  setUrl('');
40
- setError(err?.message || 'Unable to open the Support form right now.');
43
+ setError(err?.message || 'Unable to open Support right now.');
41
44
  setPhase('error');
42
45
  }
43
- }, [config]);
46
+ }, [config, mode]);
44
47
 
45
48
  useEffect(() => {
49
+ if (initialRef.current) {
50
+ initialRef.current = '';
51
+ return undefined;
52
+ }
46
53
  load();
47
54
  // Ignore the result of a request that finishes after unmount.
48
55
  return () => {
@@ -50,7 +57,7 @@ export default function RaiseTicket({ config }) {
50
57
  };
51
58
  }, [load]);
52
59
 
53
- // Warn when the form is slow or blank.
60
+ // Warn when the page is slow or blank.
54
61
  useEffect(() => {
55
62
  if (phase !== 'loading') return undefined;
56
63
  const timer = setTimeout(() => setSlow(true), FORM_SLOW_MS);
@@ -58,13 +65,13 @@ export default function RaiseTicket({ config }) {
58
65
  }, [phase, url]);
59
66
 
60
67
  const handleOpenInNewTab = () =>
61
- openInNewTab(() => buildFormUrl(config)).catch((err) => message.error(err?.message || 'Unable to open the Support form right now.'));
68
+ openInNewTab(() => getSupportUrl(config, mode)).catch((err) => message.error(err?.message || 'Unable to open Support right now.'));
62
69
 
63
70
  if (phase === 'error') {
64
71
  return (
65
72
  <Result
66
73
  status="warning"
67
- title="We couldn't open the Support form"
74
+ title={errorTitle}
68
75
  subTitle={error}
69
76
  extra={
70
77
  <Button type="primary" icon={<ReloadOutlined />} onClick={load}>
@@ -78,8 +85,13 @@ export default function RaiseTicket({ config }) {
78
85
  return (
79
86
  <div className="support-raise">
80
87
  <div className="support-raise__bar">
81
- <span>Tell us about your issue and our support team will reply as soon as possible.</span>
88
+ <span>{intro}</span>
82
89
  <span>
90
+ {allowAnother && (
91
+ <Button type="primary" size="small" icon={<PlusOutlined />} onClick={load} disabled={phase === 'minting'}>
92
+ Submit another ticket
93
+ </Button>
94
+ )}
83
95
  <Button type="link" size="small" icon={<ReloadOutlined />} onClick={load} disabled={phase === 'minting'}>
84
96
  Reload
85
97
  </Button>
@@ -94,7 +106,7 @@ export default function RaiseTicket({ config }) {
94
106
  banner
95
107
  type="warning"
96
108
  showIcon
97
- message="The form is taking longer than usual. If it stays blank, open it in a new tab."
109
+ message="This page is taking longer than usual. If it stays blank, open it in a new tab."
98
110
  action={
99
111
  <Button size="small" icon={<ReloadOutlined />} onClick={load}>
100
112
  Reload
@@ -106,10 +118,21 @@ export default function RaiseTicket({ config }) {
106
118
  <div className="support-raise__frame">
107
119
  {phase !== 'ready' && (
108
120
  <div className="support-raise__loading">
109
- <Spin tip="Preparing the Support form…" />
121
+ <Spin tip={loadingText} />
110
122
  </div>
111
123
  )}
112
- {url && <iframe title="Support" src={url} allow="clipboard-write" className="support-frame" onLoad={() => setPhase('ready')} />}
124
+ {url && (
125
+ <iframe
126
+ title="Support"
127
+ src={url}
128
+ allow="clipboard-write"
129
+ // No allow-top-navigation: the embedded page cannot redirect the Nura Desk tab away.
130
+ sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox allow-downloads"
131
+ referrerPolicy="no-referrer"
132
+ className="support-frame"
133
+ onLoad={() => setPhase('ready')}
134
+ />
135
+ )}
113
136
  </div>
114
137
  </div>
115
138
  );
@@ -1,23 +1,23 @@
1
1
  /**
2
2
  * Support drawer opened from the global header Help button.
3
3
  *
4
- * "Raise a Ticket" -> Helpdesk form (components/raise-ticket)
5
- * "My Tickets" -> the user's own tickets (components/my-tickets)
4
+ * "Raise a Ticket" -> Helpdesk form
5
+ * "My Tickets" -> the user's own tickets
6
6
  *
7
7
  * Both tabs stay mounted once opened, so a half written ticket is not lost when the user
8
- * looks at their list. The ticket list reloads every time its tab is shown.
8
+ * looks at their list. Each page asks the API for a fresh link on open and on Reload.
9
9
  */
10
10
  import React, { useState } from 'react';
11
11
  import { Drawer, Tabs } from 'antd';
12
12
  import { CustomerServiceOutlined, FormOutlined, UnorderedListOutlined } from '@ant-design/icons';
13
- import MyTickets from './components/my-tickets/my-tickets';
14
- import RaiseTicket from './components/raise-ticket/raise-ticket';
13
+ import SupportFrame from './components/support-frame/support-frame';
14
+ import { MODES } from './support-desk.utils';
15
15
  import './support-desk.scss';
16
16
 
17
17
  const RAISE = 'raise';
18
18
  const MINE = 'mine';
19
19
 
20
- export default function SupportDesk({ open, onClose, config, title = 'Support' }) {
20
+ export default function SupportDesk({ open, onClose, config, initialUrl = '', title = 'Support' }) {
21
21
  const [tab, setTab] = useState(RAISE);
22
22
 
23
23
  return (
@@ -45,7 +45,7 @@ export default function SupportDesk({ open, onClose, config, title = 'Support' }
45
45
  <FormOutlined /> Raise a Ticket
46
46
  </span>
47
47
  ),
48
- children: <RaiseTicket config={config} />,
48
+ children: <SupportFrame config={config} mode={MODES.RAISE} initialUrl={initialUrl} allowAnother intro="Tell us about your issue and our support team will reply as soon as possible." loadingText="Preparing the Support form…" errorTitle="We couldn't open the Support form" />,
49
49
  },
50
50
  {
51
51
  key: MINE,
@@ -54,7 +54,7 @@ export default function SupportDesk({ open, onClose, config, title = 'Support' }
54
54
  <UnorderedListOutlined /> My Tickets
55
55
  </span>
56
56
  ),
57
- children: <MyTickets config={config} active={tab === MINE} onRaiseTicket={() => setTab(RAISE)} />,
57
+ children: <SupportFrame config={config} mode={MODES.MINE} intro="Your tickets, with their latest status." loadingText="Loading your tickets…" errorTitle="We couldn't open your tickets" />,
58
58
  },
59
59
  ]}
60
60
  />