@plutonhq/core-frontend 0.1.23 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist-lib/@types/index.js +4 -1
  2. package/dist-lib/@types/index.js.map +1 -1
  3. package/dist-lib/@types/settings.d.ts +14 -0
  4. package/dist-lib/@types/settings.d.ts.map +1 -1
  5. package/dist-lib/@types/settings.js +34 -0
  6. package/dist-lib/@types/settings.js.map +1 -0
  7. package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.d.ts.map +1 -1
  8. package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.js +148 -90
  9. package/dist-lib/components/Plan/PlanSettings/PlanNotificationSettings.js.map +1 -1
  10. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.d.ts.map +1 -1
  11. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js +52 -47
  12. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.js.map +1 -1
  13. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.module.scss.js +12 -6
  14. package/dist-lib/components/Settings/IntegrationSettings/IntegrationSettings.module.scss.js.map +1 -1
  15. package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.d.ts +9 -0
  16. package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.d.ts.map +1 -0
  17. package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.js +79 -0
  18. package/dist-lib/components/Settings/IntegrationSettings/NtfySettings.js.map +1 -0
  19. package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.d.ts +4 -3
  20. package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.d.ts.map +1 -1
  21. package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.js +37 -35
  22. package/dist-lib/components/Settings/IntegrationSettings/SMTPSettings.js.map +1 -1
  23. package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.d.ts +10 -0
  24. package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.d.ts.map +1 -0
  25. package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.js +49 -0
  26. package/dist-lib/components/Settings/IntegrationSettings/ValidateEmailIntegration.js.map +1 -0
  27. package/dist-lib/components/Storage/EditStorage/EditStorage.js +10 -10
  28. package/dist-lib/components/Storage/EditStorage/EditStorage.js.map +1 -1
  29. package/dist-lib/components/index.d.ts +2 -0
  30. package/dist-lib/components/index.d.ts.map +1 -1
  31. package/dist-lib/components.js +58 -54
  32. package/dist-lib/components.js.map +1 -1
  33. package/dist-lib/services/settings.d.ts +3 -2
  34. package/dist-lib/services/settings.d.ts.map +1 -1
  35. package/dist-lib/services/settings.js +0 -1
  36. package/dist-lib/services/settings.js.map +1 -1
  37. package/dist-lib/styles/core-frontend.css +1 -1
  38. package/package.json +1 -1
  39. package/src/@types/settings.ts +43 -0
  40. package/src/components/Plan/PlanSettings/PlanNotificationSettings.tsx +65 -0
  41. package/src/components/Settings/IntegrationSettings/IntegrationSettings.module.scss +16 -0
  42. package/src/components/Settings/IntegrationSettings/IntegrationSettings.tsx +45 -42
  43. package/src/components/Settings/IntegrationSettings/NtfySettings.tsx +106 -0
  44. package/src/components/Settings/IntegrationSettings/SMTPSettings.tsx +28 -19
  45. package/src/components/Settings/IntegrationSettings/ValidateEmailIntegration.tsx +58 -0
  46. package/src/components/Storage/EditStorage/EditStorage.tsx +1 -1
  47. package/src/components/index.ts +2 -0
  48. package/src/services/settings.ts +2 -2
  49. /package/dist-lib/providers/{azureBlob.png → azureblob.png} +0 -0
  50. /package/dist-lib/providers/{azureFiles.png → azurefiles.png} +0 -0
  51. /package/dist-lib/providers/{files.png → filescom.png} +0 -0
  52. /package/dist-lib/providers/{oracle.png → oracleobjectstorage.png} +0 -0
  53. /package/dist-lib/providers/{proton.png → protondrive.png} +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@plutonhq/core-frontend",
3
3
  "description": "Pluton Core Frontend Library",
4
- "version": "0.1.23",
4
+ "version": "0.1.24",
5
5
  "author": "Plutonhq",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {
@@ -18,3 +18,46 @@ export interface LogItem {
18
18
  deviceId?: string;
19
19
  msg: string;
20
20
  }
21
+
22
+ export interface NtfySettingsType {
23
+ authType: string;
24
+ authToken: string;
25
+ connected: boolean;
26
+ }
27
+
28
+ export interface IntegrationSettings {
29
+ smtp?: SmtpSettingsType;
30
+ ntfy?: NtfySettingsType;
31
+ }
32
+
33
+ export type IntegrationTypes = 'smtp' | 'sendgrid' | 'mailgun' | 'brevo' | 'resend' | 'awsSes' | 'ntfy';
34
+ export const INTEGRATIONS_AVAILABLE: Record<IntegrationTypes, { name: string; required: string[] }> = {
35
+ smtp: {
36
+ name: 'SMTP',
37
+ required: ['server', 'port', 'username', 'password', 'senderEmail'],
38
+ },
39
+ sendgrid: {
40
+ name: 'SendGrid',
41
+ required: ['apiKey', 'senderEmail'],
42
+ },
43
+ mailgun: {
44
+ name: 'Mailgun',
45
+ required: ['apiKey', 'domain', 'senderEmail'],
46
+ },
47
+ brevo: {
48
+ name: 'Brevo',
49
+ required: ['apiKey', 'senderEmail'],
50
+ },
51
+ resend: {
52
+ name: 'Resend',
53
+ required: ['apiKey', 'senderEmail'],
54
+ },
55
+ awsSes: {
56
+ name: 'AWS SES',
57
+ required: ['accessKeyId', 'secretAccessKey', 'region', 'senderEmail'],
58
+ },
59
+ ntfy: {
60
+ name: 'Ntfy',
61
+ required: ['authType', 'authToken'],
62
+ },
63
+ } as const;
@@ -28,6 +28,7 @@ const PlanNotificationSettings = ({
28
28
  }: PlanNotificationSettingsProps) => {
29
29
  const hasConnectedIntegrations = types.length > 0;
30
30
  const defaultEmail = !hasConnectedIntegrations && admin_email ? admin_email : '';
31
+ const hasNtfyConnected = types.includes('ntfy');
31
32
 
32
33
  const updateNotificationEmails = (emails: string) => {
33
34
  onUpdate({ ...notificationSettings, email: { ...notificationSettings.email, emails } });
@@ -203,6 +204,70 @@ const PlanNotificationSettings = ({
203
204
  </div>
204
205
  )}
205
206
  </div>
207
+
208
+ {/* Ntfy Push Notification */}
209
+ <div className={classes.notificationSettingsSection}>
210
+ <div className={`${classes.field} ${classes.notificationToggle}`}>
211
+ <Icon type="ntfy" size={14} />
212
+ <Toggle
213
+ label="Enable Ntfy Push Notifications"
214
+ fieldValue={notificationSettings?.push?.enabled || false}
215
+ onUpdate={(val: boolean) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, enabled: val } })}
216
+ hint={`Send me Push Notifications on Backup failure or success.`}
217
+ inline={true}
218
+ />
219
+ </div>
220
+ {notificationSettings.push?.enabled &&
221
+ (hasNtfyConnected ? (
222
+ <div className={classes.notificationSettings}>
223
+ {!isSync && (
224
+ <div className={classes.field}>
225
+ <Select
226
+ label="Send Notification On"
227
+ fieldValue={notificationSettings?.push?.case || 'failure'}
228
+ options={caseOptions}
229
+ onUpdate={(val: string) =>
230
+ onUpdate({
231
+ ...notificationSettings,
232
+ push: { ...notificationSettings.push, case: val as PlanNotification['push']['case'] },
233
+ })
234
+ }
235
+ inline={true}
236
+ />
237
+ </div>
238
+ )}
239
+ <div className={classes.field}>
240
+ <Input
241
+ label="Ntfy Push URL"
242
+ fieldValue={notificationSettings?.push?.url || ''}
243
+ onUpdate={(val) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, url: val } })}
244
+ placeholder="https://ntfy.sh/mytopic"
245
+ inline={true}
246
+ required={true}
247
+ full={true}
248
+ error={!notificationSettings?.push?.url ? 'Required' : undefined}
249
+ />
250
+ </div>
251
+ <div className={classes.field}>
252
+ <Input
253
+ label="Tags"
254
+ fieldValue={notificationSettings?.push?.tags || ''}
255
+ onUpdate={(val) => onUpdate({ ...notificationSettings, push: { ...notificationSettings.push, tags: val } })}
256
+ placeholder="warning, daily-backup"
257
+ inline={true}
258
+ full={true}
259
+ />
260
+ </div>
261
+ </div>
262
+ ) : (
263
+ <div className={classes.notificationSettings}>
264
+ <div className={classes.fieldNotice}>
265
+ ⚠️ Ntfy Auth Token is not configured. Set it up in <NavLink to={`/settings?t=integration`}>Settings</NavLink> to enable Ntfy
266
+ Push Notifications.
267
+ </div>
268
+ </div>
269
+ ))}
270
+ </div>
206
271
  </>
207
272
  );
208
273
  };
@@ -36,3 +36,19 @@
36
36
  }
37
37
  }
38
38
  }
39
+
40
+ @media only screen and (max-width: 768px) {
41
+ .integrations {
42
+ .field {
43
+ width: 100%;
44
+ }
45
+ }
46
+ .integrationSelect {
47
+ width: 100%;
48
+ margin-bottom: 30px;
49
+
50
+ :global([class*='_dropdown_']) {
51
+ width: 100%;
52
+ }
53
+ }
54
+ }
@@ -1,9 +1,10 @@
1
1
  import { useState } from 'react';
2
2
  import classes from './IntegrationSettings.module.scss';
3
- import Input from '../../common/form/Input/Input';
4
- import ActionModal from '../../common/ActionModal/ActionModal';
5
- import { useValidateIntegration } from '../../../services/settings';
6
3
  import SMTPSettings from './SMTPSettings';
4
+ import NtfySettings from './NtfySettings';
5
+ import Icon from '../../common/Icon/Icon';
6
+ import Select from '../../common/form/Select/Select';
7
+ import { isMobile } from '../../../utils';
7
8
 
8
9
  interface IntegrationSettingsProps {
9
10
  settingsID: number;
@@ -12,12 +13,9 @@ interface IntegrationSettingsProps {
12
13
  }
13
14
 
14
15
  const IntegrationSettings = ({ settingsID, settings, onUpdate }: IntegrationSettingsProps) => {
15
- const [testEmail, setTestEmail] = useState('');
16
- const [showEmailTestModal, setShowEmailTestModal] = useState<'' | 'smtp'>('');
16
+ const [tab, setTab] = useState<'smtp' | 'ntfy'>('smtp');
17
17
  const integrationSettings = settings?.integration || {};
18
- const { smtp } = integrationSettings || {};
19
-
20
- const validationMutation = useValidateIntegration();
18
+ const { smtp, ntfy } = integrationSettings || {};
21
19
 
22
20
  const onIntegrationUpdate = (key: string, intSettings: Record<string, any>) => {
23
21
  console.log('onIntegrationUpdate :', key, intSettings);
@@ -26,42 +24,47 @@ const IntegrationSettings = ({ settingsID, settings, onUpdate }: IntegrationSett
26
24
 
27
25
  return (
28
26
  <div className={classes.integrations}>
29
- <div>
30
- <SMTPSettings
31
- settings={smtp}
32
- onUpdate={(iSettings) => onIntegrationUpdate('smtp', iSettings)}
33
- showTestModal={(type) => setShowEmailTestModal(type)}
34
- />
35
- </div>
36
- {showEmailTestModal && (
37
- <ActionModal
38
- title={`Test SMTP Integration`}
39
- message={
40
- <>
41
- <Input
42
- label="Send Test Email to this email"
43
- full={true}
44
- fieldValue={testEmail}
45
- onUpdate={(val) => setTestEmail(val)}
46
- type="email"
47
- placeholder="test@test.com"
48
- />
49
- </>
50
- }
51
- errorMessage={validationMutation.error?.message}
52
- successMessage={validationMutation.isSuccess ? 'Test email sent. Integration validated successfully.' : ''}
53
- closeModal={() => setShowEmailTestModal('')}
54
- width="400px"
55
- secondaryAction={{ title: 'Close', action: () => setShowEmailTestModal('') }}
56
- primaryAction={{
57
- title: `Send Test Email`,
58
- type: 'default',
59
- icon: 'email',
60
- isPending: validationMutation.isPending,
61
- action: () => validationMutation.mutate({ settingsID, type: 'smtp', settings: settings, test: { email: testEmail } }),
62
- }}
27
+ {isMobile() ? (
28
+ <Select
29
+ customClasses={classes.integrationSelect}
30
+ options={[
31
+ { label: 'SMTP', value: 'smtp', icon: 'email' },
32
+ { label: 'Ntfy', value: 'ntfy', icon: 'ntfy' },
33
+ ]}
34
+ fieldValue={tab}
35
+ full={true}
36
+ onUpdate={(val) => setTab(val as 'smtp' | 'ntfy')}
63
37
  />
38
+ ) : (
39
+ <>
40
+ <ul className={classes.tabs}>
41
+ <li className={`${tab === 'smtp' ? classes.tabActive : ''}`} onClick={() => setTab('smtp')}>
42
+ <Icon type="email" size={14} /> SMTP
43
+ {smtp?.connected && <Icon type="check-circle-filled" size={12} />}
44
+ </li>
45
+ <li className={`${tab === 'ntfy' ? classes.tabActive : ''}`} onClick={() => setTab('ntfy')}>
46
+ <Icon type="ntfy" size={14} /> Ntfy
47
+ {ntfy?.connected && <Icon type="check-circle-filled" size={12} />}
48
+ </li>
49
+ </ul>
50
+ </>
64
51
  )}
52
+ <div>
53
+ {tab === 'smtp' && (
54
+ <SMTPSettings
55
+ settingsID={settingsID}
56
+ settings={integrationSettings}
57
+ onUpdate={(iSettings) => onIntegrationUpdate('smtp', iSettings)}
58
+ />
59
+ )}
60
+ {tab === 'ntfy' && (
61
+ <NtfySettings
62
+ settingsID={settingsID}
63
+ settings={integrationSettings}
64
+ onUpdate={(iSettings) => onIntegrationUpdate('ntfy', iSettings)}
65
+ />
66
+ )}
67
+ </div>
65
68
  </div>
66
69
  );
67
70
  };
@@ -0,0 +1,106 @@
1
+ import { useState } from 'react';
2
+ import classes from './IntegrationSettings.module.scss';
3
+ import { IntegrationSettings, NtfySettingsType } from '../../../@types';
4
+ import Icon from '../../common/Icon/Icon';
5
+ import Select from '../../common/form/Select/Select';
6
+ import Input from '../../common/form/Input/Input';
7
+ import ActionModal from '../../common/ActionModal/ActionModal';
8
+ import { useValidateIntegration } from '../../../services';
9
+ import PasswordField from '../../common/form/PasswordField/PasswordField';
10
+
11
+ interface NtfySettingsProps {
12
+ settingsID: number;
13
+ settings: IntegrationSettings;
14
+ onUpdate: (settings: NtfySettingsType) => void;
15
+ }
16
+
17
+ const NtfySettings = ({ settingsID, settings, onUpdate }: NtfySettingsProps) => {
18
+ const [ntfySettings, setNtfySettings] = useState<NtfySettingsType>(settings?.ntfy || { authType: 'token', authToken: '', connected: false });
19
+ const [errorFields, setErrorFields] = useState<{ authToken: string }>({ authToken: '' });
20
+ const [showTestModal, setShowTestModal] = useState(false);
21
+ const [testUrl, setTestUrl] = useState('');
22
+ const validationMutation = useValidateIntegration();
23
+
24
+ const authType = ntfySettings.authType || 'token';
25
+
26
+ const updateNtfySettings = (updated: NtfySettingsType) => {
27
+ setNtfySettings(updated);
28
+ onUpdate(updated);
29
+ };
30
+
31
+ const validateSettings = (e: React.FormEvent) => {
32
+ e.preventDefault();
33
+
34
+ const newErrors = { authToken: '' };
35
+
36
+ if (!ntfySettings?.authToken) {
37
+ newErrors.authToken = 'Required';
38
+ }
39
+ setErrorFields(newErrors);
40
+ const hasErrors = Object.values(newErrors).some((error) => error !== '');
41
+ if (!hasErrors) {
42
+ setShowTestModal(true);
43
+ }
44
+ };
45
+
46
+ return (
47
+ <div>
48
+ <div className={classes.field}>
49
+ <Select
50
+ label="Auth Type*"
51
+ fieldValue={authType}
52
+ options={[{ label: 'Token', value: 'token' }]}
53
+ onUpdate={(val) => updateNtfySettings({ ...ntfySettings, authType: val })}
54
+ inline={true}
55
+ />
56
+ </div>
57
+ <div className={classes.field}>
58
+ <PasswordField
59
+ label="Auth Token*"
60
+ fieldValue={ntfySettings.authToken}
61
+ onUpdate={(val) => updateNtfySettings({ ...ntfySettings, authToken: val })}
62
+ error={errorFields?.authToken}
63
+ />
64
+ </div>
65
+ <div className={classes.field}>
66
+ <button className={classes.validateBtn} onClick={validateSettings} type="button">
67
+ <Icon type="check" size={10} /> {ntfySettings.connected ? 'Re-validate Ntfy' : 'Validate Ntfy'}
68
+ </button>
69
+ </div>
70
+ {showTestModal && (
71
+ <ActionModal
72
+ title={`Test Ntfy Integration`}
73
+ message={
74
+ <>
75
+ <Input
76
+ label="Send Test Notification to this topic"
77
+ full={true}
78
+ inline={false}
79
+ fieldValue={testUrl}
80
+ onUpdate={(val) => setTestUrl(val)}
81
+ type="text"
82
+ placeholder="test/topic url. Eg: https://ntfy.sh/testtopic"
83
+ />
84
+ </>
85
+ }
86
+ errorMessage={validationMutation.error?.message}
87
+ successMessage={validationMutation.isSuccess ? 'Test notification sent. Integration validated successfully.' : ''}
88
+ closeModal={() => setShowTestModal(false)}
89
+ width="400px"
90
+ secondaryAction={{ title: 'Close', action: () => setShowTestModal(false) }}
91
+ primaryAction={{
92
+ title: `Send Test Notification`,
93
+ type: 'default',
94
+ icon: 'send',
95
+ isPending: validationMutation.isPending,
96
+ action: () =>
97
+ testUrl &&
98
+ validationMutation.mutate({ settingsID, type: 'ntfy', settings: { ...settings, ntfy: ntfySettings }, test: { url: testUrl } }),
99
+ }}
100
+ />
101
+ )}
102
+ </div>
103
+ );
104
+ };
105
+
106
+ export default NtfySettings;
@@ -3,42 +3,47 @@ import Input from '../../common/form/Input/Input';
3
3
  import Icon from '../../common/Icon/Icon';
4
4
  import { isValidEmail } from '../../../utils/helpers';
5
5
  import { SmtpSettingsType } from '../../../@types/settings';
6
+ import { IntegrationSettings } from '../../../@types';
6
7
  import classes from './IntegrationSettings.module.scss';
8
+ import ValidateEmailIntegration from './ValidateEmailIntegration';
7
9
 
8
10
  interface SMTPSettingsProps {
9
- settings: SmtpSettingsType;
11
+ settingsID: number;
12
+ settings: IntegrationSettings;
10
13
  onUpdate: (settings: SmtpSettingsType) => void;
11
- showTestModal: (type: 'smtp') => void;
12
14
  }
13
15
 
14
- const SMTPSettings = ({ settings, onUpdate, showTestModal }: SMTPSettingsProps) => {
16
+ const SMTPSettings = ({ settingsID, settings, onUpdate }: SMTPSettingsProps) => {
17
+ const [showTestModal, setShowTestModal] = useState(false);
15
18
  const [errorFields, setErrorFields] = useState<{ server: string; port: string; senderEmail: string }>({
16
19
  server: '',
17
20
  port: '',
18
21
  senderEmail: '',
19
22
  });
20
23
 
24
+ const smtpSettings = settings?.smtp || { server: '', port: 587, senderEmail: '', username: '', password: '', connected: false };
25
+
21
26
  const validateSettings = (e: React.FormEvent) => {
22
27
  e.preventDefault();
23
28
 
24
29
  const newErrors = { server: '', port: '', senderEmail: '' };
25
30
 
26
- if (!settings?.server) {
31
+ if (!smtpSettings?.server) {
27
32
  newErrors.server = 'Server is required';
28
33
  }
29
- if (!settings?.port) {
34
+ if (!smtpSettings?.port) {
30
35
  newErrors.port = 'Port is required';
31
36
  }
32
- if (!settings?.senderEmail) {
37
+ if (!smtpSettings?.senderEmail) {
33
38
  newErrors.senderEmail = 'Sender Email is required';
34
- } else if (!isValidEmail(settings.senderEmail)) {
39
+ } else if (!isValidEmail(smtpSettings.senderEmail)) {
35
40
  newErrors.senderEmail = 'Invalid email';
36
41
  }
37
42
 
38
43
  setErrorFields(newErrors);
39
44
  const hasErrors = Object.values(newErrors).some((error) => error !== '');
40
45
  if (!hasErrors) {
41
- showTestModal('smtp');
46
+ setShowTestModal(true);
42
47
  }
43
48
  };
44
49
 
@@ -47,16 +52,16 @@ const SMTPSettings = ({ settings, onUpdate, showTestModal }: SMTPSettingsProps)
47
52
  <div className={classes.field}>
48
53
  <Input
49
54
  label="SMTP Server*"
50
- fieldValue={(settings?.server || '') as string}
51
- onUpdate={(val) => onUpdate({ ...settings, server: val })}
55
+ fieldValue={(smtpSettings?.server || '') as string}
56
+ onUpdate={(val) => onUpdate({ ...smtpSettings, server: val })}
52
57
  error={errorFields?.server}
53
58
  />
54
59
  </div>
55
60
  <div className={classes.field}>
56
61
  <Input
57
62
  label="SMTP PORT*"
58
- fieldValue={(settings?.port || '') as string}
59
- onUpdate={(val) => onUpdate({ ...settings, port: parseInt(val, 10) })}
63
+ fieldValue={(smtpSettings?.port || '') as string}
64
+ onUpdate={(val) => onUpdate({ ...smtpSettings, port: parseInt(val, 10) })}
60
65
  error={errorFields?.port}
61
66
  />
62
67
  </div>
@@ -64,32 +69,36 @@ const SMTPSettings = ({ settings, onUpdate, showTestModal }: SMTPSettingsProps)
64
69
  <Input
65
70
  label="Sender Email*"
66
71
  type="email"
67
- fieldValue={(settings?.senderEmail || '') as string}
68
- onUpdate={(val) => onUpdate({ ...settings, senderEmail: val })}
72
+ fieldValue={(smtpSettings?.senderEmail || '') as string}
73
+ onUpdate={(val) => onUpdate({ ...smtpSettings, senderEmail: val })}
69
74
  error={errorFields?.senderEmail}
70
75
  />
71
76
  </div>
72
77
  <div className={classes.field}>
73
78
  <Input
74
79
  label="SMTP Username"
75
- fieldValue={(settings?.username || '') as string}
76
- onUpdate={(val) => onUpdate({ ...settings, username: val })}
80
+ fieldValue={(smtpSettings?.username || '') as string}
81
+ onUpdate={(val) => onUpdate({ ...smtpSettings, username: val })}
77
82
  />
78
83
  </div>
79
84
  <div className={classes.field}>
80
85
  <Input
81
86
  label="SMTP Password"
82
- fieldValue={(settings?.password || '') as string}
87
+ fieldValue={(smtpSettings?.password || '') as string}
83
88
  type="password"
84
- onUpdate={(val) => onUpdate({ ...settings, password: val })}
89
+ onUpdate={(val) => onUpdate({ ...smtpSettings, password: val })}
85
90
  />
86
91
  </div>
87
92
 
88
93
  <div className={classes.field}>
89
94
  <button className={classes.validateBtn} onClick={validateSettings} type="button">
90
- <Icon type="check" size={10} /> {settings?.connected ? 'Re-validate SMTP' : 'Validate SMTP'}
95
+ <Icon type="check" size={10} /> {smtpSettings?.connected ? 'Re-validate SMTP' : 'Validate SMTP'}
91
96
  </button>
92
97
  </div>
98
+
99
+ {showTestModal && (
100
+ <ValidateEmailIntegration settingsID={settingsID} settings={settings} integrationType="smtp" onClose={() => setShowTestModal(false)} />
101
+ )}
93
102
  </div>
94
103
  );
95
104
  };
@@ -0,0 +1,58 @@
1
+ import { useState } from 'react';
2
+ import Input from '../../common/form/Input/Input';
3
+ import ActionModal from '../../common/ActionModal/ActionModal';
4
+ import { useValidateIntegration } from '../../../services';
5
+ import { INTEGRATIONS_AVAILABLE, IntegrationSettings, IntegrationTypes } from '../../../@types';
6
+
7
+ interface ValidateEmailIntegrationProps {
8
+ settingsID: number;
9
+ settings: IntegrationSettings;
10
+ integrationType: IntegrationTypes;
11
+ onClose: () => void;
12
+ }
13
+
14
+ const ValidateEmailIntegration = ({ settingsID, settings, integrationType, onClose }: ValidateEmailIntegrationProps) => {
15
+ const [testEmail, setTestEmail] = useState('');
16
+ const validationMutation = useValidateIntegration();
17
+ const integrationName = INTEGRATIONS_AVAILABLE[integrationType as IntegrationTypes].name;
18
+
19
+ return (
20
+ <ActionModal
21
+ title={`Test ${integrationName} Integration`}
22
+ message={
23
+ <>
24
+ <Input
25
+ label="Send Test Email to this email"
26
+ full={true}
27
+ inline={false}
28
+ fieldValue={testEmail}
29
+ onUpdate={(val) => setTestEmail(val)}
30
+ type="email"
31
+ placeholder="test@test.com"
32
+ />
33
+ </>
34
+ }
35
+ errorMessage={validationMutation.error?.message}
36
+ successMessage={validationMutation.isSuccess ? `Test email sent. ${integrationName} validated successfully.` : ''}
37
+ closeModal={onClose}
38
+ width="400px"
39
+ secondaryAction={{ title: 'Close', action: onClose }}
40
+ primaryAction={{
41
+ title: 'Send Test Email',
42
+ type: 'default',
43
+ icon: 'email',
44
+ isPending: validationMutation.isPending,
45
+ action: () =>
46
+ testEmail &&
47
+ validationMutation.mutate({
48
+ settingsID,
49
+ type: integrationType,
50
+ settings: { ...settings },
51
+ test: { email: testEmail },
52
+ }),
53
+ }}
54
+ />
55
+ );
56
+ };
57
+
58
+ export default ValidateEmailIntegration;
@@ -39,7 +39,7 @@ const EditStorage = ({ close, storage }: EditStorageProps) => {
39
39
  storage.storageFields.forEach((field: storageOptionField) => {
40
40
  if (field.required) {
41
41
  (groupedFields.required as storageOptionField[]).push(field);
42
- } else {
42
+ } else if (!field.authFieldType) {
43
43
  (groupedFields.optional as storageOptionField[]).push(field);
44
44
  }
45
45
  });
@@ -113,6 +113,8 @@ export { default as AppLogs } from './Settings/AppLogs/AppLogs';
113
113
  export { default as GeneralSettings } from './Settings/GeneralSettings/GeneralSettings';
114
114
  export { default as IntegrationSettings } from './Settings/IntegrationSettings/IntegrationSettings';
115
115
  export { default as SMTPSettings } from './Settings/IntegrationSettings/SMTPSettings';
116
+ export { default as NtfySettings } from './Settings/IntegrationSettings/NtfySettings';
117
+ export { default as ValidateEmailIntegration } from './Settings/IntegrationSettings/ValidateEmailIntegration';
116
118
  export { default as TwoFactorSetup } from './Settings/TwoFactorSetup/TwoFactorSetup';
117
119
 
118
120
  // Skeleton components
@@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
2
  import { experimental_createQueryPersister } from '@tanstack/react-query-persist-client';
3
3
  import { API_URL } from '../utils/constants';
4
4
  import { useNavigate } from 'react-router';
5
+ import { IntegrationSettings } from '../@types';
5
6
 
6
7
  // ============== Settings API ==============
7
8
 
@@ -136,10 +137,9 @@ export function useGetDownloadAppLogs() {
136
137
  export async function validateIntegration(updatePayload: {
137
138
  settingsID: number;
138
139
  type: string;
139
- settings: Record<string, string | boolean | number>;
140
+ settings: IntegrationSettings;
140
141
  test: Record<string, string | boolean | number>;
141
142
  }) {
142
- console.log('updatePayload :', updatePayload);
143
143
  const header = new Headers({ 'Content-Type': 'application/json', Accept: 'application/json' });
144
144
  const res = await fetch(`${API_URL}/settings/integration/validate`, {
145
145
  method: 'POST',
File without changes