@consilioweb/payload-support 4.0.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -35
- package/dist/components/RichTextEditor/index.js +1 -1
- package/dist/components/TicketConversation/RewriteDropdown.js +1 -1
- package/dist/components/TicketConversation/components/AISummaryPanel.js +3 -0
- package/dist/components/TicketConversation/components/ActionPanels.js +13 -8
- package/dist/components/TicketConversation/components/ClientHistory.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlock.js +1 -0
- package/dist/components/TicketConversation/components/CodeBlockInserter.js +1 -1
- package/dist/components/TicketConversation/components/QuickActions.js +10 -8
- package/dist/components/TicketConversation/components/TicketHeader.js +2 -0
- package/dist/components/TicketConversation/components/TimeTrackingPanel.js +15 -9
- package/dist/components/TicketConversation/index.d.ts +10 -0
- package/dist/components/TicketConversation/index.js +25 -7
- package/dist/index.cjs +421 -73
- package/dist/index.d.cts +127 -38
- package/dist/index.d.ts +127 -38
- package/dist/index.js +415 -72
- package/dist/styles/BillingView.module.scss +11 -11
- package/dist/styles/ChatView.module.scss +7 -7
- package/dist/styles/CommandPalette.module.scss +1 -1
- package/dist/styles/CrmView.module.scss +9 -9
- package/dist/styles/EmailTracking.module.scss +6 -6
- package/dist/styles/ImportConversation.module.scss +5 -5
- package/dist/styles/Logs.module.scss +5 -5
- package/dist/styles/NewTicket.module.scss +2 -2
- package/dist/styles/PendingEmails.module.scss +13 -13
- package/dist/styles/SupportDashboard.module.scss +24 -8
- package/dist/styles/TicketDetail.module.scss +20 -20
- package/dist/styles/TicketInbox.module.scss +6 -6
- package/dist/styles/TicketingSettings.module.scss +10 -10
- package/dist/styles/TimeDashboard.module.scss +6 -6
- package/dist/styles/_tokens.scss +3 -1
- package/dist/utils/db.d.ts +34 -0
- package/dist/utils/readSettings.d.ts +90 -0
- package/dist/views/BillingView/client.js +15 -8
- package/dist/views/BillingView/index.js +4 -4
- package/dist/views/ChatView/client.js +2 -0
- package/dist/views/ChatView/index.js +4 -4
- package/dist/views/CrmView/client.js +2 -0
- package/dist/views/CrmView/index.js +4 -4
- package/dist/views/EmailTrackingView/client.js +23 -11
- package/dist/views/EmailTrackingView/index.js +4 -4
- package/dist/views/ImportConversationView/client.js +1 -0
- package/dist/views/ImportConversationView/index.js +4 -4
- package/dist/views/LogsView/client.js +1 -1
- package/dist/views/LogsView/index.js +4 -2
- package/dist/views/NewTicketView/client.js +20 -13
- package/dist/views/NewTicketView/index.js +4 -2
- package/dist/views/PendingEmailsView/client.js +9 -3
- package/dist/views/PendingEmailsView/index.js +4 -4
- package/dist/views/SupportDashboardView/client.js +17 -19
- package/dist/views/SupportDashboardView/index.js +4 -4
- package/dist/views/TicketDetailView/client.js +23 -11
- package/dist/views/TicketDetailView/index.js +4 -4
- package/dist/views/TicketInboxView/client.js +52 -26
- package/dist/views/TicketInboxView/index.js +4 -2
- package/dist/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/dist/views/TicketingSettingsView/client.js +44 -26
- package/dist/views/TicketingSettingsView/index.js +4 -4
- package/dist/views/TimeDashboardView/client.js +10 -7
- package/dist/views/TimeDashboardView/index.js +4 -4
- package/dist/views/shared/ErrorBoundary.d.ts +36 -1
- package/dist/views/shared/ErrorBoundary.js +59 -28
- package/dist/views/shared/adminTokens.d.ts +15 -5
- package/dist/views/shared/adminTokens.js +23 -7
- package/dist/views/shared/icons.d.ts +49 -0
- package/dist/views/shared/icons.js +92 -0
- package/dist/views/shared/locales/en.json +15 -1
- package/dist/views/shared/locales/fr.json +15 -1
- package/dist/views/shared/viewAccess.d.ts +29 -0
- package/dist/views/shared/viewAccess.js +24 -0
- package/package.json +31 -21
- package/scripts/uninstall-data.mjs +178 -0
- package/scripts/uninstall.mjs +184 -0
- package/src/collections/Tickets.ts +7 -0
- package/src/collections/TimeEntries.ts +11 -3
- package/src/components/RichTextEditor/index.tsx +1 -1
- package/src/components/TicketConversation/RewriteDropdown.tsx +1 -1
- package/src/components/TicketConversation/components/AISummaryPanel.tsx +3 -0
- package/src/components/TicketConversation/components/ActionPanels.tsx +13 -8
- package/src/components/TicketConversation/components/ClientHistory.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlock.tsx +1 -0
- package/src/components/TicketConversation/components/CodeBlockInserter.tsx +1 -1
- package/src/components/TicketConversation/components/QuickActions.tsx +10 -8
- package/src/components/TicketConversation/components/TicketHeader.tsx +2 -0
- package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +17 -9
- package/src/components/TicketConversation/index.tsx +41 -7
- package/src/endpoints/auth-2fa.ts +53 -8
- package/src/endpoints/capabilities.ts +2 -4
- package/src/endpoints/chatbot.ts +2 -2
- package/src/endpoints/delete-account.ts +191 -47
- package/src/endpoints/export-data.ts +104 -4
- package/src/endpoints/import-conversation.ts +2 -2
- package/src/endpoints/login.ts +13 -3
- package/src/endpoints/purge-logs.ts +6 -15
- package/src/endpoints/push.ts +14 -1
- package/src/endpoints/statuses.ts +17 -0
- package/src/index.ts +2 -0
- package/src/plugin.ts +51 -1
- package/src/portal/LiveChat.tsx +1 -1
- package/src/portal/auth/profile/page.tsx +3 -2
- package/src/portal/icons.tsx +91 -0
- package/src/portal/login/page.tsx +14 -4
- package/src/styles/BillingView.module.scss +11 -11
- package/src/styles/ChatView.module.scss +7 -7
- package/src/styles/CommandPalette.module.scss +1 -1
- package/src/styles/CrmView.module.scss +9 -9
- package/src/styles/EmailTracking.module.scss +6 -6
- package/src/styles/ImportConversation.module.scss +5 -5
- package/src/styles/Logs.module.scss +5 -5
- package/src/styles/NewTicket.module.scss +2 -2
- package/src/styles/PendingEmails.module.scss +13 -13
- package/src/styles/SupportDashboard.module.scss +24 -8
- package/src/styles/TicketDetail.module.scss +20 -20
- package/src/styles/TicketInbox.module.scss +6 -6
- package/src/styles/TicketingSettings.module.scss +10 -10
- package/src/styles/TimeDashboard.module.scss +6 -6
- package/src/styles/_tokens.scss +3 -1
- package/src/types.ts +20 -0
- package/src/utils/push.ts +22 -0
- package/src/utils/rateLimiter.ts +98 -0
- package/src/utils/retention.ts +111 -0
- package/src/utils/slugs.ts +15 -0
- package/src/utils/twoFactorChallenge.ts +6 -1
- package/src/utils/urlSafety.ts +36 -1
- package/src/views/BillingView/client.tsx +17 -8
- package/src/views/BillingView/index.tsx +4 -4
- package/src/views/ChatView/client.tsx +2 -0
- package/src/views/ChatView/index.tsx +4 -4
- package/src/views/CrmView/client.tsx +2 -0
- package/src/views/CrmView/index.tsx +4 -4
- package/src/views/EmailTrackingView/client.tsx +18 -7
- package/src/views/EmailTrackingView/index.tsx +4 -4
- package/src/views/ImportConversationView/client.tsx +1 -0
- package/src/views/ImportConversationView/index.tsx +4 -4
- package/src/views/LogsView/client.tsx +1 -1
- package/src/views/LogsView/index.tsx +4 -2
- package/src/views/NewTicketView/client.tsx +22 -13
- package/src/views/NewTicketView/index.tsx +4 -2
- package/src/views/PendingEmailsView/client.tsx +9 -3
- package/src/views/PendingEmailsView/index.tsx +4 -4
- package/src/views/SupportDashboardView/client.tsx +11 -5
- package/src/views/SupportDashboardView/index.tsx +4 -4
- package/src/views/TicketDetailView/client.tsx +25 -11
- package/src/views/TicketDetailView/index.tsx +4 -4
- package/src/views/TicketInboxView/client.tsx +47 -7
- package/src/views/TicketInboxView/index.tsx +4 -2
- package/src/views/TicketingSettingsView/TicketingSettings.module.scss +7 -7
- package/src/views/TicketingSettingsView/client.tsx +201 -144
- package/src/views/TicketingSettingsView/index.tsx +4 -4
- package/src/views/TimeDashboardView/client.tsx +10 -7
- package/src/views/TimeDashboardView/index.tsx +4 -4
- package/src/views/shared/ErrorBoundary.tsx +95 -31
- package/src/views/shared/adminTokens.ts +28 -11
- package/src/views/shared/icons.tsx +146 -0
- package/src/views/shared/locales/en.json +15 -1
- package/src/views/shared/locales/fr.json +15 -1
- package/src/views/shared/viewAccess.ts +73 -0
- package/src/types/lucide-react.d.ts +0 -42
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React, { useState, useEffect } from 'react'
|
|
4
|
-
import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from '
|
|
3
|
+
import React, { useState, useEffect, useId } from 'react'
|
|
4
|
+
import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from '../shared/icons'
|
|
5
5
|
import { V, btnStyle } from '../shared/adminTokens'
|
|
6
6
|
import { AdminViewHeader } from '../shared/AdminViewHeader'
|
|
7
7
|
import { DEFAULT_FEATURES, type TicketingFeatures } from '../../components/TicketConversation/config'
|
|
@@ -280,19 +280,33 @@ const CollapsibleSection: React.FC<{
|
|
|
280
280
|
)
|
|
281
281
|
}
|
|
282
282
|
|
|
283
|
+
/**
|
|
284
|
+
* Row of the settings form.
|
|
285
|
+
*
|
|
286
|
+
* It owns the id of the control it labels: `children` is called with a
|
|
287
|
+
* generated id that the control must carry, which turns the row title into a
|
|
288
|
+
* real `<label>` instead of a floating `<div>`. The id is generated with
|
|
289
|
+
* `useId` because several settings views can be mounted at once.
|
|
290
|
+
*/
|
|
283
291
|
const FieldRow: React.FC<{
|
|
284
292
|
label: string
|
|
285
293
|
description?: string
|
|
286
|
-
children: React.ReactNode
|
|
287
|
-
}> = ({ label, description, children }) =>
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
294
|
+
children: React.ReactNode | ((id: string) => React.ReactNode)
|
|
295
|
+
}> = ({ label, description, children }) => {
|
|
296
|
+
const id = useId()
|
|
297
|
+
|
|
298
|
+
return (
|
|
299
|
+
<div className={ts.fieldRow}>
|
|
300
|
+
<div className={ts.fieldLabel}>
|
|
301
|
+
<label htmlFor={id}>{label}</label>
|
|
302
|
+
{description && <div className={ts.fieldDescription}>{description}</div>}
|
|
303
|
+
</div>
|
|
304
|
+
<div className={ts.fieldContent}>
|
|
305
|
+
{typeof children === 'function' ? children(id) : children}
|
|
306
|
+
</div>
|
|
292
307
|
</div>
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
)
|
|
308
|
+
)
|
|
309
|
+
}
|
|
296
310
|
|
|
297
311
|
/* ============================================
|
|
298
312
|
* Main Component
|
|
@@ -393,7 +407,7 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
393
407
|
subtitle={t('settings.subtitle', { enabled: String(enabledCount), total: String(totalCount) })}
|
|
394
408
|
actions={
|
|
395
409
|
<div style={{ display: 'flex', gap: 8 }}>
|
|
396
|
-
<button onClick={handleReset} style={btnStyle(
|
|
410
|
+
<button type="button" onClick={handleReset} style={btnStyle(V.neutralBg, { small: true, fg: V.neutralFg })}>
|
|
397
411
|
{t('settingsView.reset')}
|
|
398
412
|
</button>
|
|
399
413
|
<button
|
|
@@ -477,54 +491,69 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
477
491
|
</p>
|
|
478
492
|
|
|
479
493
|
<FieldRow label={t('settingsView.senderAddress')} description={t('settingsView.senderAddressDesc')}>
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
494
|
+
{(id) => (
|
|
495
|
+
<input
|
|
496
|
+
id={id}
|
|
497
|
+
type="email"
|
|
498
|
+
value={settings.email.fromAddress}
|
|
499
|
+
onChange={(e) => updateEmail('fromAddress', e.target.value)}
|
|
500
|
+
placeholder="support@example.com"
|
|
501
|
+
className={ts.input}
|
|
502
|
+
/>
|
|
503
|
+
)}
|
|
487
504
|
</FieldRow>
|
|
488
505
|
|
|
489
506
|
<FieldRow label={t('settingsView.senderName')} description={t('settingsView.senderNameDesc')}>
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
507
|
+
{(id) => (
|
|
508
|
+
<input
|
|
509
|
+
id={id}
|
|
510
|
+
type="text"
|
|
511
|
+
value={settings.email.fromName}
|
|
512
|
+
onChange={(e) => updateEmail('fromName', e.target.value)}
|
|
513
|
+
placeholder="Support ConsilioWEB"
|
|
514
|
+
className={ts.input}
|
|
515
|
+
/>
|
|
516
|
+
)}
|
|
497
517
|
</FieldRow>
|
|
498
518
|
|
|
499
519
|
<FieldRow label={t('settingsView.replyTo')} description={t('settingsView.replyToDesc')}>
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
520
|
+
{(id) => (
|
|
521
|
+
<input
|
|
522
|
+
id={id}
|
|
523
|
+
type="email"
|
|
524
|
+
value={settings.email.replyToAddress}
|
|
525
|
+
onChange={(e) => updateEmail('replyToAddress', e.target.value)}
|
|
526
|
+
placeholder="(identique a l'expediteur)"
|
|
527
|
+
className={ts.input}
|
|
528
|
+
/>
|
|
529
|
+
)}
|
|
507
530
|
</FieldRow>
|
|
508
531
|
|
|
509
532
|
<div className={ts.separator} />
|
|
510
533
|
|
|
511
534
|
<FieldRow label={t('settingsView.smtpServer')} description={t('settingsView.smtpServerDesc')}>
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
535
|
+
{(id) => (
|
|
536
|
+
<input
|
|
537
|
+
id={id}
|
|
538
|
+
type="text"
|
|
539
|
+
value={smtpHost}
|
|
540
|
+
readOnly
|
|
541
|
+
className={ts.inputReadonly}
|
|
542
|
+
/>
|
|
543
|
+
)}
|
|
518
544
|
</FieldRow>
|
|
519
545
|
|
|
520
546
|
<FieldRow label={t('settingsView.smtpPort')}>
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
547
|
+
{(id) => (
|
|
548
|
+
<input
|
|
549
|
+
id={id}
|
|
550
|
+
type="text"
|
|
551
|
+
value={smtpPort}
|
|
552
|
+
readOnly
|
|
553
|
+
className={ts.inputReadonly}
|
|
554
|
+
style={{ maxWidth: 100 }}
|
|
555
|
+
/>
|
|
556
|
+
)}
|
|
528
557
|
</FieldRow>
|
|
529
558
|
</CollapsibleSection>
|
|
530
559
|
|
|
@@ -547,47 +576,56 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
547
576
|
</p>
|
|
548
577
|
|
|
549
578
|
<FieldRow label={t('settingsView.aiProvider')} description={t('settingsView.aiProviderDesc')}>
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
579
|
+
{(id) => (
|
|
580
|
+
<select
|
|
581
|
+
id={id}
|
|
582
|
+
value={settings.ai.provider}
|
|
583
|
+
onChange={(e) => updateAI('provider', e.target.value as AISettings['provider'])}
|
|
584
|
+
className={ts.select}
|
|
585
|
+
>
|
|
586
|
+
<option value="ollama">Ollama (local / tunnel)</option>
|
|
587
|
+
<option value="anthropic">Anthropic (Claude)</option>
|
|
588
|
+
<option value="openai">OpenAI (GPT)</option>
|
|
589
|
+
<option value="gemini">Google (Gemini)</option>
|
|
590
|
+
</select>
|
|
591
|
+
)}
|
|
560
592
|
</FieldRow>
|
|
561
593
|
|
|
562
594
|
{settings.ai.provider !== 'ollama' && (
|
|
563
595
|
<FieldRow label={t('settingsView.aiApiKey')} description={t('settingsView.aiApiKeyDesc')}>
|
|
564
|
-
|
|
565
|
-
<
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
596
|
+
{(id) => (
|
|
597
|
+
<div className={ts.apiKeyRow}>
|
|
598
|
+
<input
|
|
599
|
+
id={id}
|
|
600
|
+
type={showApiKey ? 'text' : 'password'}
|
|
601
|
+
value={settings.ai.apiKey}
|
|
602
|
+
onChange={(e) => updateAI('apiKey', e.target.value)}
|
|
603
|
+
placeholder="sk-..."
|
|
604
|
+
className={ts.input}
|
|
605
|
+
style={{ flex: 1 }}
|
|
606
|
+
/>
|
|
607
|
+
<button
|
|
608
|
+
onClick={() => setShowApiKey(!showApiKey)}
|
|
609
|
+
className={ts.apiKeyToggle}
|
|
610
|
+
>
|
|
611
|
+
{showApiKey ? t('settingsView.hideKey') : t('settingsView.showKey')}
|
|
612
|
+
</button>
|
|
613
|
+
</div>
|
|
614
|
+
)}
|
|
580
615
|
</FieldRow>
|
|
581
616
|
)}
|
|
582
617
|
|
|
583
618
|
<FieldRow label={t('settingsView.aiModel')} description={t('settingsView.aiModelDesc')}>
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
619
|
+
{(id) => (
|
|
620
|
+
<input
|
|
621
|
+
id={id}
|
|
622
|
+
type="text"
|
|
623
|
+
value={settings.ai.model}
|
|
624
|
+
onChange={(e) => updateAI('model', e.target.value)}
|
|
625
|
+
placeholder="qwen2.5:32b"
|
|
626
|
+
className={ts.input}
|
|
627
|
+
/>
|
|
628
|
+
)}
|
|
591
629
|
</FieldRow>
|
|
592
630
|
|
|
593
631
|
<div className={ts.separator} />
|
|
@@ -631,33 +669,39 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
631
669
|
</p>
|
|
632
670
|
|
|
633
671
|
<FieldRow label={t('settingsView.slaFirstResponse')} description={t('settingsView.slaFirstResponseDesc')}>
|
|
634
|
-
|
|
635
|
-
<
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
672
|
+
{(id) => (
|
|
673
|
+
<div className={ts.slaInline}>
|
|
674
|
+
<input
|
|
675
|
+
id={id}
|
|
676
|
+
type="number"
|
|
677
|
+
min={1}
|
|
678
|
+
value={settings.sla.firstResponseMinutes}
|
|
679
|
+
onChange={(e) => updateSLA('firstResponseMinutes', parseInt(e.target.value) || 0)}
|
|
680
|
+
className={ts.numberInput}
|
|
681
|
+
/>
|
|
682
|
+
<span className={ts.slaHint}>
|
|
683
|
+
{t('settingsView.minutes')} ({Math.floor(settings.sla.firstResponseMinutes / 60)}h{String(settings.sla.firstResponseMinutes % 60).padStart(2, '0')})
|
|
684
|
+
</span>
|
|
685
|
+
</div>
|
|
686
|
+
)}
|
|
646
687
|
</FieldRow>
|
|
647
688
|
|
|
648
689
|
<FieldRow label={t('settingsView.slaResolution')} description={t('settingsView.slaResolutionDesc')}>
|
|
649
|
-
|
|
650
|
-
<
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
690
|
+
{(id) => (
|
|
691
|
+
<div className={ts.slaInline}>
|
|
692
|
+
<input
|
|
693
|
+
id={id}
|
|
694
|
+
type="number"
|
|
695
|
+
min={1}
|
|
696
|
+
value={settings.sla.resolutionMinutes}
|
|
697
|
+
onChange={(e) => updateSLA('resolutionMinutes', parseInt(e.target.value) || 0)}
|
|
698
|
+
className={ts.numberInput}
|
|
699
|
+
/>
|
|
700
|
+
<span className={ts.slaHint}>
|
|
701
|
+
{t('settingsView.minutes')} ({Math.floor(settings.sla.resolutionMinutes / 60)}h{String(settings.sla.resolutionMinutes % 60).padStart(2, '0')})
|
|
702
|
+
</span>
|
|
703
|
+
</div>
|
|
704
|
+
)}
|
|
661
705
|
</FieldRow>
|
|
662
706
|
|
|
663
707
|
<div className={ts.toggleRow}>
|
|
@@ -678,13 +722,16 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
678
722
|
<div className={ts.separator} />
|
|
679
723
|
|
|
680
724
|
<FieldRow label={t('settingsView.escalationEmail')} description={t('settingsView.escalationEmailDesc')}>
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
725
|
+
{(id) => (
|
|
726
|
+
<input
|
|
727
|
+
id={id}
|
|
728
|
+
type="email"
|
|
729
|
+
value={settings.sla.escalationEmail}
|
|
730
|
+
onChange={(e) => updateSLA('escalationEmail', e.target.value)}
|
|
731
|
+
placeholder="admin@example.com"
|
|
732
|
+
className={ts.input}
|
|
733
|
+
/>
|
|
734
|
+
)}
|
|
688
735
|
</FieldRow>
|
|
689
736
|
</CollapsibleSection>
|
|
690
737
|
|
|
@@ -720,33 +767,39 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
720
767
|
{settings.autoClose.enabled && (
|
|
721
768
|
<>
|
|
722
769
|
<FieldRow label={t('settingsView.autoCloseDelay')} description={t('settingsView.autoCloseDelayDesc')}>
|
|
723
|
-
|
|
724
|
-
<
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
770
|
+
{(id) => (
|
|
771
|
+
<div className={ts.slaInline}>
|
|
772
|
+
<input
|
|
773
|
+
id={id}
|
|
774
|
+
type="number"
|
|
775
|
+
min={1}
|
|
776
|
+
max={90}
|
|
777
|
+
value={settings.autoClose.daysBeforeClose}
|
|
778
|
+
onChange={(e) => updateAutoClose('daysBeforeClose', parseInt(e.target.value) || 7)}
|
|
779
|
+
className={ts.numberInput}
|
|
780
|
+
/>
|
|
781
|
+
<span className={ts.slaHint}>{t('settingsView.days')}</span>
|
|
782
|
+
</div>
|
|
783
|
+
)}
|
|
734
784
|
</FieldRow>
|
|
735
785
|
|
|
736
786
|
<FieldRow label={t('settingsView.autoCloseReminder')} description={t('settingsView.autoCloseReminderDesc')}>
|
|
737
|
-
|
|
738
|
-
<
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
787
|
+
{(id) => (
|
|
788
|
+
<div className={ts.slaInline}>
|
|
789
|
+
<input
|
|
790
|
+
id={id}
|
|
791
|
+
type="number"
|
|
792
|
+
min={1}
|
|
793
|
+
max={settings.autoClose.daysBeforeClose - 1}
|
|
794
|
+
value={settings.autoClose.reminderDaysBefore}
|
|
795
|
+
onChange={(e) => updateAutoClose('reminderDaysBefore', parseInt(e.target.value) || 2)}
|
|
796
|
+
className={ts.numberInput}
|
|
797
|
+
/>
|
|
798
|
+
<span className={ts.slaHint}>
|
|
799
|
+
{t('settingsView.autoCloseDaysBeforeReminder', { count: String(settings.autoClose.reminderDaysBefore) })}
|
|
800
|
+
</span>
|
|
801
|
+
</div>
|
|
802
|
+
)}
|
|
750
803
|
</FieldRow>
|
|
751
804
|
</>
|
|
752
805
|
)}
|
|
@@ -783,14 +836,17 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
783
836
|
</p>
|
|
784
837
|
|
|
785
838
|
<FieldRow label={t('settingsView.language')} description={t('settingsView.languageDesc')}>
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
839
|
+
{(id) => (
|
|
840
|
+
<select
|
|
841
|
+
id={id}
|
|
842
|
+
value={settings.locale.language}
|
|
843
|
+
onChange={(e) => updateLocale('language', e.target.value as 'fr' | 'en')}
|
|
844
|
+
className={ts.select}
|
|
845
|
+
>
|
|
846
|
+
<option value="fr">{t('settingsView.french')}</option>
|
|
847
|
+
<option value="en">{t('settingsView.english')}</option>
|
|
848
|
+
</select>
|
|
849
|
+
)}
|
|
794
850
|
</FieldRow>
|
|
795
851
|
</CollapsibleSection>
|
|
796
852
|
|
|
@@ -808,6 +864,7 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
808
864
|
</p>
|
|
809
865
|
|
|
810
866
|
<textarea
|
|
867
|
+
aria-label={t('settingsView.signatureTitle')}
|
|
811
868
|
value={signature}
|
|
812
869
|
onChange={(e) => { setSignature(e.target.value); setSaved(false) }}
|
|
813
870
|
placeholder={t('settingsView.signaturePlaceholder')}
|
|
@@ -843,7 +900,7 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
843
900
|
if (res.ok) { const d = await res.json(); alert(`${d.purged} ${t('settingsView.purgeDone')}`) }
|
|
844
901
|
} catch { alert(t('import.errorTitle')) }
|
|
845
902
|
}}
|
|
846
|
-
style={{ ...btnStyle(opt.days === 0 ?
|
|
903
|
+
style={{ ...btnStyle(opt.days === 0 ? V.red : V.neutralBg, { small: true, fg: opt.days === 0 ? undefined : V.neutralFg }), fontSize: 11 }}
|
|
847
904
|
>
|
|
848
905
|
{opt.label}
|
|
849
906
|
</button>
|
|
@@ -856,7 +913,7 @@ export const TicketingSettingsClient: React.FC = () => {
|
|
|
856
913
|
|
|
857
914
|
{/* Bottom save bar */}
|
|
858
915
|
<div className={ts.bottomBar}>
|
|
859
|
-
<button onClick={handleReset} style={btnStyle(
|
|
916
|
+
<button type="button" onClick={handleReset} style={btnStyle(V.neutralBg, { small: true, fg: V.neutralFg })}>
|
|
860
917
|
{t('settingsView.resetAll')}
|
|
861
918
|
</button>
|
|
862
919
|
<button
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdminViewServerProps } from 'payload'
|
|
2
2
|
import { DefaultTemplate } from '@payloadcms/next/templates'
|
|
3
3
|
import { redirect } from 'next/navigation'
|
|
4
|
+
import { supportViewRedirectTarget } from '../shared/viewAccess.js'
|
|
4
5
|
import React from 'react'
|
|
5
6
|
import { AdminErrorBoundary } from '../shared/ErrorBoundary'
|
|
6
7
|
import { TicketingSettingsClient } from './client'
|
|
@@ -8,9 +9,8 @@ import { TicketingSettingsClient } from './client'
|
|
|
8
9
|
export const TicketingSettingsView: React.FC<AdminViewServerProps> = ({ initPageResult }) => {
|
|
9
10
|
const { req, visibleEntities } = initPageResult
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
12
|
+
const redirectTo = supportViewRedirectTarget(initPageResult)
|
|
13
|
+
if (redirectTo) redirect(redirectTo)
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<DefaultTemplate
|
|
@@ -20,7 +20,7 @@ export const TicketingSettingsView: React.FC<AdminViewServerProps> = ({ initPage
|
|
|
20
20
|
payload={req.payload}
|
|
21
21
|
permissions={initPageResult.permissions}
|
|
22
22
|
searchParams={{}}
|
|
23
|
-
user={req.user}
|
|
23
|
+
user={req.user ?? undefined}
|
|
24
24
|
visibleEntities={visibleEntities}
|
|
25
25
|
>
|
|
26
26
|
<AdminErrorBoundary viewName="TicketingSettingsView">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable @next/next/no-html-link-for-pages */
|
|
2
2
|
'use client'
|
|
3
3
|
|
|
4
|
-
import React, { useState, useEffect, useCallback } from 'react'
|
|
4
|
+
import React, { useState, useEffect, useCallback, useId } from 'react'
|
|
5
5
|
import { SkeletonDashboard } from '../shared/Skeleton'
|
|
6
6
|
import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation'
|
|
7
7
|
import styles from '../../styles/TimeDashboard.module.scss'
|
|
@@ -44,6 +44,9 @@ const MONTHS_FR = ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jun', 'Jul', 'Aou', 'Sep'
|
|
|
44
44
|
|
|
45
45
|
export const TimeDashboardClient: React.FC = () => {
|
|
46
46
|
const { t } = useTranslation()
|
|
47
|
+
const fromId = useId()
|
|
48
|
+
const toId = useId()
|
|
49
|
+
const groupById = useId()
|
|
47
50
|
const [entries, setEntries] = useState<TimeEntry[]>([])
|
|
48
51
|
const [loading, setLoading] = useState(true)
|
|
49
52
|
const [from, setFrom] = useState(() => getMonthRange(0).from)
|
|
@@ -149,16 +152,16 @@ export const TimeDashboardClient: React.FC = () => {
|
|
|
149
152
|
</div>
|
|
150
153
|
<div className={styles.filterRow}>
|
|
151
154
|
<div className={styles.fieldGroup}>
|
|
152
|
-
<label className={styles.label}>{t('timeDashboard.filters.from')}</label>
|
|
153
|
-
<input type="date" value={from} onChange={(e) => setFrom(e.target.value)} className={styles.input} />
|
|
155
|
+
<label className={styles.label} htmlFor={fromId}>{t('timeDashboard.filters.from')}</label>
|
|
156
|
+
<input id={fromId} type="date" value={from} onChange={(e) => setFrom(e.target.value)} className={styles.input} />
|
|
154
157
|
</div>
|
|
155
158
|
<div className={styles.fieldGroup}>
|
|
156
|
-
<label className={styles.label}>{t('timeDashboard.filters.to')}</label>
|
|
157
|
-
<input type="date" value={to} onChange={(e) => setTo(e.target.value)} className={styles.input} />
|
|
159
|
+
<label className={styles.label} htmlFor={toId}>{t('timeDashboard.filters.to')}</label>
|
|
160
|
+
<input id={toId} type="date" value={to} onChange={(e) => setTo(e.target.value)} className={styles.input} />
|
|
158
161
|
</div>
|
|
159
162
|
<div className={styles.fieldGroup}>
|
|
160
|
-
<label className={styles.label}>{t('timeDashboard.filters.groupBy')}</label>
|
|
161
|
-
<select value={groupBy} onChange={(e) => setGroupBy(e.target.value as 'day' | 'week' | 'project')} className={styles.select}>
|
|
163
|
+
<label className={styles.label} htmlFor={groupById}>{t('timeDashboard.filters.groupBy')}</label>
|
|
164
|
+
<select id={groupById} value={groupBy} onChange={(e) => setGroupBy(e.target.value as 'day' | 'week' | 'project')} className={styles.select}>
|
|
162
165
|
<option value="day">{t('timeDashboard.filters.day')}</option>
|
|
163
166
|
<option value="week">{t('timeDashboard.filters.week')}</option>
|
|
164
167
|
<option value="project">{t('timeDashboard.filters.project')}</option>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AdminViewServerProps } from 'payload'
|
|
2
2
|
import { DefaultTemplate } from '@payloadcms/next/templates'
|
|
3
3
|
import { redirect } from 'next/navigation'
|
|
4
|
+
import { supportViewRedirectTarget } from '../shared/viewAccess.js'
|
|
4
5
|
import React from 'react'
|
|
5
6
|
import { AdminErrorBoundary } from '../shared/ErrorBoundary'
|
|
6
7
|
import { TimeDashboardClient } from './client'
|
|
@@ -8,9 +9,8 @@ import { TimeDashboardClient } from './client'
|
|
|
8
9
|
export const TimeDashboardView: React.FC<AdminViewServerProps> = ({ initPageResult }) => {
|
|
9
10
|
const { req, visibleEntities } = initPageResult
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
12
|
+
const redirectTo = supportViewRedirectTarget(initPageResult)
|
|
13
|
+
if (redirectTo) redirect(redirectTo)
|
|
14
14
|
|
|
15
15
|
return (
|
|
16
16
|
<DefaultTemplate
|
|
@@ -20,7 +20,7 @@ export const TimeDashboardView: React.FC<AdminViewServerProps> = ({ initPageResu
|
|
|
20
20
|
payload={req.payload}
|
|
21
21
|
permissions={initPageResult.permissions}
|
|
22
22
|
searchParams={{}}
|
|
23
|
-
user={req.user}
|
|
23
|
+
user={req.user ?? undefined}
|
|
24
24
|
visibleEntities={visibleEntities}
|
|
25
25
|
>
|
|
26
26
|
<AdminErrorBoundary viewName="TimeDashboardView">
|