@plutonhq/core-frontend 0.1.26 → 0.1.28
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/dist-lib/@types/backups.d.ts +3 -0
- package/dist-lib/@types/backups.d.ts.map +1 -1
- package/dist-lib/@types/plans.d.ts +2 -0
- package/dist-lib/@types/plans.d.ts.map +1 -1
- package/dist-lib/@types/restores.d.ts +2 -0
- package/dist-lib/@types/restores.d.ts.map +1 -1
- package/dist-lib/components/Device/DeviceInfo/DeviceInfo.module.scss.js +26 -26
- package/dist-lib/components/Plan/PlanBackups/PlanBackups.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanBackups/PlanBackups.js +26 -26
- package/dist-lib/components/Plan/PlanBackups/PlanBackups.js.map +1 -1
- package/dist-lib/components/Plan/PlanForm/PlanForm.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanForm/PlanForm.js +64 -80
- package/dist-lib/components/Plan/PlanForm/PlanForm.js.map +1 -1
- package/dist-lib/components/Plan/PlanHistory/PlanHistory.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanHistory/PlanHistory.js +7 -7
- package/dist-lib/components/Plan/PlanHistory/PlanHistory.js.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js +39 -30
- package/dist-lib/components/Plan/PlanSettings/PlanSourceSettings.js.map +1 -1
- package/dist-lib/components/Plan/PlanStats/PlanStats.d.ts.map +1 -1
- package/dist-lib/components/Plan/PlanStats/PlanStats.js +25 -25
- package/dist-lib/components/Plan/PlanStats/PlanStats.js.map +1 -1
- package/dist-lib/components/Plan/PlanStats/PlanStats.module.scss.js +1 -1
- package/dist-lib/components/common/Icon/Icon.d.ts.map +1 -1
- package/dist-lib/components/common/Icon/Icon.js +12 -1
- package/dist-lib/components/common/Icon/Icon.js.map +1 -1
- package/dist-lib/components/common/form/IntervalField/IntervalField.d.ts.map +1 -1
- package/dist-lib/components/common/form/IntervalField/IntervalField.js +66 -46
- package/dist-lib/components/common/form/IntervalField/IntervalField.js.map +1 -1
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.d.ts.map +1 -1
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.js +49 -47
- package/dist-lib/components/common/form/StoragePicker/StoragePicker.js.map +1 -1
- package/dist-lib/components/common/form/TagsInput/TagsInput.d.ts +2 -1
- package/dist-lib/components/common/form/TagsInput/TagsInput.d.ts.map +1 -1
- package/dist-lib/components/common/form/TagsInput/TagsInput.js +15 -15
- package/dist-lib/components/common/form/TagsInput/TagsInput.js.map +1 -1
- package/dist-lib/components/common/form/TimePicker/TimePicker.module.scss.js +13 -13
- package/dist-lib/hooks/usePlanSingleActions.js +7 -7
- package/dist-lib/hooks/usePlanSingleActions.js.map +1 -1
- package/dist-lib/routes/Settings/Settings.module.scss.js +2 -2
- package/dist-lib/styles/core-frontend.css +1 -1
- package/dist-lib/styles/global.scss +6 -0
- package/dist-lib/utils/constants.js +1 -1
- package/dist-lib/utils/constants.js.map +1 -1
- package/dist-lib/utils/helpers.d.ts +2 -0
- package/dist-lib/utils/helpers.d.ts.map +1 -1
- package/dist-lib/utils/helpers.js +55 -33
- package/dist-lib/utils/helpers.js.map +1 -1
- package/dist-lib/utils/plans.js +6 -6
- package/dist-lib/utils/plans.js.map +1 -1
- package/dist-lib/utils.js +34 -33
- package/package.json +1 -1
- package/src/@types/backups.ts +3 -0
- package/src/@types/plans.ts +2 -0
- package/src/@types/restores.ts +2 -0
- package/src/components/Device/DeviceInfo/DeviceInfo.module.scss +1 -0
- package/src/components/Plan/PlanBackups/PlanBackups.tsx +5 -2
- package/src/components/Plan/PlanForm/PlanForm.tsx +1 -19
- package/src/components/Plan/PlanHistory/PlanHistory.tsx +16 -13
- package/src/components/Plan/PlanSettings/PlanSourceSettings.tsx +15 -1
- package/src/components/Plan/PlanStats/PlanStats.module.scss +3 -0
- package/src/components/Plan/PlanStats/PlanStats.tsx +2 -8
- package/src/components/common/Icon/Icon.tsx +12 -0
- package/src/components/common/form/IntervalField/IntervalField.tsx +21 -1
- package/src/components/common/form/StoragePicker/StoragePicker.tsx +8 -1
- package/src/components/common/form/TagsInput/TagsInput.tsx +3 -2
- package/src/components/common/form/TimePicker/TimePicker.module.scss +1 -0
- package/src/hooks/usePlanSingleActions.tsx +2 -2
- package/src/routes/Settings/Settings.module.scss +1 -1
- package/src/styles/global.scss +6 -0
- package/src/utils/constants.ts +1 -1
- package/src/utils/helpers.ts +25 -0
- package/src/utils/plans.ts +3 -3
|
@@ -5,7 +5,6 @@ import StoragePicker from '../../common/form/StoragePicker/StoragePicker';
|
|
|
5
5
|
import PlanStrategySettings from '../PlanSettings/PlanStrategySettings';
|
|
6
6
|
import PlanSourceSettings from '../PlanSettings/PlanSourceSettings';
|
|
7
7
|
import { NewPlanSettings } from '../../../@types/plans';
|
|
8
|
-
import NumberInput from '../../common/form/NumberInput/NumberInput';
|
|
9
8
|
import classes from '../AddPlan/AddPlan.module.scss';
|
|
10
9
|
import PFClasses from './PlanForm.module.scss';
|
|
11
10
|
import { useGetSettings } from '../../../services/settings';
|
|
@@ -254,30 +253,13 @@ const PlanForm = ({
|
|
|
254
253
|
<div className={PFClasses.planStep}>
|
|
255
254
|
<div className={classes.field} style={{ width: '150px' }}>
|
|
256
255
|
<IntervalField
|
|
257
|
-
label="Backup Interval
|
|
256
|
+
label="Backup Interval"
|
|
258
257
|
fieldValue={planSettings.settings.interval}
|
|
259
258
|
onUpdate={(intervalSettings) =>
|
|
260
259
|
onPlanSettingsChange({ ...planSettings, settings: { ...planSettings.settings, interval: intervalSettings } })
|
|
261
260
|
}
|
|
262
261
|
/>
|
|
263
262
|
</div>
|
|
264
|
-
|
|
265
|
-
<div className={classes.field} style={{ width: '150px' }}>
|
|
266
|
-
<NumberInput
|
|
267
|
-
label="Backups to Keep"
|
|
268
|
-
fieldValue={planSettings.settings.prune.snapCount}
|
|
269
|
-
onUpdate={(val) =>
|
|
270
|
-
onPlanSettingsChange({
|
|
271
|
-
...planSettings,
|
|
272
|
-
settings: { ...planSettings.settings, prune: { ...planSettings.settings.prune, snapCount: val } },
|
|
273
|
-
})
|
|
274
|
-
}
|
|
275
|
-
placeholder="5"
|
|
276
|
-
min={1}
|
|
277
|
-
inline={false}
|
|
278
|
-
hint="Number of Active Restorable Backups/Snapshots to Keep regardless of the Removal Policy"
|
|
279
|
-
/>
|
|
280
|
-
</div>
|
|
281
263
|
<PlanPruneSettings
|
|
282
264
|
plan={planSettings}
|
|
283
265
|
onUpdate={(pruneSettings) =>
|
|
@@ -40,24 +40,27 @@ const PlanHistory = ({ planId, history, itemsCount = 10 }: PlanHistoryProps) =>
|
|
|
40
40
|
></div>
|
|
41
41
|
))}
|
|
42
42
|
|
|
43
|
-
{history
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
{history
|
|
44
|
+
.slice(0, itemsCount)
|
|
45
|
+
.sort((a, b) => (b.inProgress ? 1 : 0) - (a.inProgress ? 1 : 0))
|
|
46
|
+
.map((h, i) => {
|
|
47
|
+
const duration = !h.inProgress && (h.duration || (h.ended ? (new Date(h.ended).getTime() - new Date(h.started).getTime()) / 1000 : 0));
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
className={`${h.status === 'completed' ? classes.historyItemSuccess : ''} ${h.status === 'failed' || h.status === 'cancelled' ? classes.historyItemFailed : ''} ${h.inProgress ? classes.historyItemPending : ''}`}
|
|
51
|
+
key={`${planId}-history-${i}`}
|
|
52
|
+
data-tooltip-id="htmlToolTip"
|
|
53
|
+
data-tooltip-html={`
|
|
51
54
|
<div><b>Status</b>: ${statusVals[h.status as keyof typeof statusVals] || 'Unknown'}</div>
|
|
52
55
|
<div><b>Changes</b>: ${backupChanges(h.changes)}</div>
|
|
53
56
|
<div><b>Started</b>: ${formatDateTime(h.started)}</div>
|
|
54
57
|
<div><b>Ended</b>: ${h.ended ? formatDateTime(h.ended) : ' - '}</div>
|
|
55
|
-
<div><b>Duration</b>: ${duration ? formatDuration(duration) : '
|
|
58
|
+
<div><b>Duration</b>: ${h.status && h.status !== 'started' && duration ? formatDuration(duration) : '-'} </div>
|
|
56
59
|
`}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
data-tooltip-place="top"
|
|
61
|
+
></div>
|
|
62
|
+
);
|
|
63
|
+
})}
|
|
61
64
|
</div>
|
|
62
65
|
);
|
|
63
66
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
1
2
|
import classes from './PlanSettings.module.scss';
|
|
2
3
|
import PathPicker from '../../common/PathPicker/PathPicker';
|
|
3
4
|
import { NewPlanSettings } from '../../../@types/plans';
|
|
@@ -27,6 +28,19 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
|
|
|
27
28
|
);
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
// When the device changes, reset the sourceConfig paths to prevent invalid paths from being submitted
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (!isEditing) {
|
|
34
|
+
onUpdate({
|
|
35
|
+
...plan,
|
|
36
|
+
sourceConfig: {
|
|
37
|
+
includes: [],
|
|
38
|
+
excludes: [],
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}, [isEditing, deviceId]);
|
|
43
|
+
|
|
30
44
|
return (
|
|
31
45
|
<>
|
|
32
46
|
<div className={classes.field}>
|
|
@@ -47,7 +61,7 @@ const PlanSourceSettings = ({ plan, onUpdate, error, isEditing }: PlanSourceSett
|
|
|
47
61
|
onUpdate={(paths) => onUpdate({ ...plan, sourceConfig: { ...paths } })}
|
|
48
62
|
deviceId={deviceId}
|
|
49
63
|
single={plan.method === 'sync'}
|
|
50
|
-
disallowChange={plan.method === 'sync'}
|
|
64
|
+
disallowChange={plan.method === 'sync' && isEditing}
|
|
51
65
|
/>
|
|
52
66
|
</div>
|
|
53
67
|
</>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
.planStats {
|
|
2
2
|
display: flex;
|
|
3
|
+
justify-content: space-between;
|
|
3
4
|
gap: 20px;
|
|
4
5
|
.widgetTitle {
|
|
5
6
|
padding: 2px 6px;
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
gap: 5px;
|
|
43
44
|
font-size: 0.75rem;
|
|
44
45
|
font-weight: 500;
|
|
46
|
+
min-width: 80px;
|
|
45
47
|
img {
|
|
46
48
|
width: auto;
|
|
47
49
|
max-height: 18px;
|
|
@@ -55,6 +57,7 @@
|
|
|
55
57
|
border: 1px solid var(--line-color);
|
|
56
58
|
border-radius: 50%;
|
|
57
59
|
width: 30px;
|
|
60
|
+
min-width: 30px;
|
|
58
61
|
height: 30px;
|
|
59
62
|
box-sizing: border-box;
|
|
60
63
|
padding-top: 4px;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Icon from '../../common/Icon/Icon';
|
|
2
2
|
import { Plan } from '../../../@types/plans';
|
|
3
|
-
import { formatBytes, formatNumberToK } from '../../../utils/helpers';
|
|
3
|
+
import { formatBytes, formatNumberToK, formatIntervalDisplay } from '../../../utils/helpers';
|
|
4
4
|
import PlanHistory from '../PlanHistory/PlanHistory';
|
|
5
5
|
import classes from './PlanStats.module.scss';
|
|
6
6
|
import PlanStorageInfo from '../PlanStorageInfo/PlanStorageInfo';
|
|
@@ -45,13 +45,7 @@ const PlanStats = ({ plan, isSync, lastBackupItem }: PlanStatsProps) => {
|
|
|
45
45
|
<div
|
|
46
46
|
data-tooltip-id="htmlToolTip"
|
|
47
47
|
data-tooltip-place="top"
|
|
48
|
-
data-tooltip-html={
|
|
49
|
-
isActive
|
|
50
|
-
? isSync
|
|
51
|
-
? `Syncing changes every ${interval.minutes} minutes`
|
|
52
|
-
: `Copying changes ${['hours', 'minutes', 'days'].includes(interval.type) ? interval[interval.type as 'hours' | 'minutes' | 'days'] + interval.type : interval.type}`
|
|
53
|
-
: 'Plan is Not Active'
|
|
54
|
-
}
|
|
48
|
+
data-tooltip-html={isActive ? `${isSync ? 'Syncing' : 'Copying'} changes ${formatIntervalDisplay(interval)}` : 'Plan is Not Active'}
|
|
55
49
|
>
|
|
56
50
|
<Icon type={isActive ? (isSync ? 'reload' : 'copy') : 'pause'} size={16} />
|
|
57
51
|
<div className={classes.sourceArrow}>→</div>
|
|
@@ -732,6 +732,18 @@ const Icon = ({ type, color = 'currentColor', size = 16, title = '', classes = '
|
|
|
732
732
|
</g>
|
|
733
733
|
</IconWrapper>
|
|
734
734
|
)}
|
|
735
|
+
{type === 'folder-exclude' && (
|
|
736
|
+
<IconWrapper size={size} viewBox="0 0 24 24">
|
|
737
|
+
<path
|
|
738
|
+
fill="none"
|
|
739
|
+
stroke={color}
|
|
740
|
+
strokeLinecap="round"
|
|
741
|
+
strokeLinejoin="round"
|
|
742
|
+
strokeWidth={2}
|
|
743
|
+
d="M13.5 19H5a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h4l3 3h7a2 2 0 0 1 2 2v4m1 9l-5-5m0 5l5-5"
|
|
744
|
+
></path>
|
|
745
|
+
</IconWrapper>
|
|
746
|
+
)}
|
|
735
747
|
|
|
736
748
|
{type === 'folders' && (
|
|
737
749
|
<IconWrapper size={size} viewBox="0 0 24 24">
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NumberInput } from '../../..';
|
|
1
2
|
import { PlanInterval } from '../../../../@types/plans';
|
|
2
3
|
import Icon from '../../Icon/Icon';
|
|
3
4
|
import FormField from '../FormField/FormField';
|
|
@@ -44,6 +45,7 @@ const IntervalField = ({
|
|
|
44
45
|
{ label: 'Daily', value: 'daily' },
|
|
45
46
|
{ label: 'Weekly', value: 'weekly' },
|
|
46
47
|
{ label: 'Monthly', value: 'monthly' },
|
|
48
|
+
{ label: 'Every x Minutes', value: 'minutes' },
|
|
47
49
|
{ label: 'Every x Hours', value: 'hours' },
|
|
48
50
|
{ label: 'Every x Days', value: 'days' },
|
|
49
51
|
]}
|
|
@@ -84,7 +86,7 @@ const IntervalField = ({
|
|
|
84
86
|
</div>
|
|
85
87
|
</>
|
|
86
88
|
)}
|
|
87
|
-
{fieldValue.type !== 'hourly' && fieldValue.type !== 'hours' && (
|
|
89
|
+
{fieldValue.type !== 'hourly' && fieldValue.type !== 'hours' && fieldValue.type !== 'minutes' && (
|
|
88
90
|
<>
|
|
89
91
|
<span>at</span>
|
|
90
92
|
<TimePicker fieldValue={fieldValue.time || '10:00AM'} onUpdate={(val) => onUpdate({ ...fieldValue, time: val })} />
|
|
@@ -123,6 +125,24 @@ const IntervalField = ({
|
|
|
123
125
|
/>
|
|
124
126
|
</>
|
|
125
127
|
)}
|
|
128
|
+
{fieldValue.type === 'minutes' && (
|
|
129
|
+
<>
|
|
130
|
+
<span>Every</span>
|
|
131
|
+
<NumberInput
|
|
132
|
+
min={5}
|
|
133
|
+
inline={true}
|
|
134
|
+
fieldValue={fieldValue.minutes || 5}
|
|
135
|
+
onUpdate={(minutes) =>
|
|
136
|
+
onUpdate({
|
|
137
|
+
...fieldValue,
|
|
138
|
+
minutes: minutes,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
placeholder="1"
|
|
142
|
+
/>{' '}
|
|
143
|
+
Minutes
|
|
144
|
+
</>
|
|
145
|
+
)}
|
|
126
146
|
{fieldValue.type === 'hours' && (
|
|
127
147
|
<>
|
|
128
148
|
<span>Every</span>
|
|
@@ -70,6 +70,13 @@ const StoragePicker = ({ onUpdate, storagePath = '', storageId, disabled = false
|
|
|
70
70
|
}
|
|
71
71
|
}, [selectedStorage, path]);
|
|
72
72
|
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (!disabled) {
|
|
75
|
+
setSelectedStorage(null);
|
|
76
|
+
setPath('');
|
|
77
|
+
}
|
|
78
|
+
}, [deviceId, disabled]);
|
|
79
|
+
|
|
73
80
|
// console.log('Storage path :', path, !disabled && isLocalStorage && !path);
|
|
74
81
|
|
|
75
82
|
return (
|
|
@@ -98,7 +105,7 @@ const StoragePicker = ({ onUpdate, storagePath = '', storageId, disabled = false
|
|
|
98
105
|
<Input
|
|
99
106
|
disabled={disabled}
|
|
100
107
|
fieldValue={path}
|
|
101
|
-
onUpdate={(val) => setPath(val.startsWith('/') ? val.slice(1) : val)} //if the val starts with a slash remove it
|
|
108
|
+
onUpdate={(val) => setPath(!isLocalStorage && val.startsWith('/') ? val.slice(1) : val)} //if the val starts with a slash remove it (only for remote storages, local paths need the leading slash)
|
|
102
109
|
placeholder={isLocalStorage ? 'Select a folder' : hasBucketName ? 'subfolder' : `folder-or-bucket/subfolder`}
|
|
103
110
|
full={true}
|
|
104
111
|
required={!disabled && isLocalStorage}
|
|
@@ -10,11 +10,12 @@ type TagsInputProps = {
|
|
|
10
10
|
icon?: string;
|
|
11
11
|
type?: string;
|
|
12
12
|
inline?: boolean;
|
|
13
|
+
hint?: string;
|
|
13
14
|
fieldValue: string[];
|
|
14
15
|
onUpdate: (f: string[]) => void;
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
const TagsInput = ({ label, description, customClasses = '', fieldValue = [], onUpdate, icon, type = 'tag', inline }: TagsInputProps) => {
|
|
18
|
+
const TagsInput = ({ label, description, customClasses = '', hint = '', fieldValue = [], onUpdate, icon, type = 'tag', inline }: TagsInputProps) => {
|
|
18
19
|
const [tags, setTags] = useState<string[]>(() => (Array.isArray(fieldValue) ? fieldValue : []));
|
|
19
20
|
const [newTag, setNewTag] = useState('');
|
|
20
21
|
|
|
@@ -56,7 +57,7 @@ const TagsInput = ({ label, description, customClasses = '', fieldValue = [], on
|
|
|
56
57
|
};
|
|
57
58
|
|
|
58
59
|
return (
|
|
59
|
-
<FormField type={'tags'} label={label} description={description} inline={inline} classes={`${classes.tagField} ${customClasses}`}>
|
|
60
|
+
<FormField type={'tags'} label={label} hint={hint} description={description} inline={inline} classes={`${classes.tagField} ${customClasses}`}>
|
|
60
61
|
<div className={classes.tagBox}>
|
|
61
62
|
{tags.map((t, i) => (
|
|
62
63
|
<div className={classes.tag} key={t + i}>
|
|
@@ -50,8 +50,8 @@ export const usePlanSingleActions = (): {
|
|
|
50
50
|
|
|
51
51
|
const sortedHistory = [...(plan.backups || [])].sort((a, b) => b.started - a.started);
|
|
52
52
|
const activeBackups = sortedHistory.filter((s) => s.inProgress);
|
|
53
|
-
const activeRestores = (plan.restores || []).filter((s) => s.
|
|
54
|
-
const lastBackupItem = sortedHistory[0];
|
|
53
|
+
const activeRestores = (plan.restores || []).filter((s) => s.inProgress);
|
|
54
|
+
const lastBackupItem = isSync ? sortedHistory.filter((s) => s.status === 'completed')[0] : sortedHistory[0];
|
|
55
55
|
const actionInProgress = activeBackups.length > 0 || activeRestores.length > 0;
|
|
56
56
|
const taskPending = pauseMutation.isPending || resumeMutation.isPending || performBackupMutation.isPending;
|
|
57
57
|
|
package/src/styles/global.scss
CHANGED
package/src/utils/constants.ts
CHANGED
package/src/utils/helpers.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { storageOptionField } from '../@types/storages';
|
|
2
2
|
import { FileItem } from '../@types/system';
|
|
3
|
+
import { PlanInterval } from '../@types/plans';
|
|
3
4
|
|
|
4
5
|
export const isMobile = (): boolean => {
|
|
5
6
|
// Server-side rendering check
|
|
@@ -472,6 +473,30 @@ export const getAvailableCliApps = (platform?: string) => {
|
|
|
472
473
|
}
|
|
473
474
|
};
|
|
474
475
|
|
|
476
|
+
export const formatIntervalDisplay = (interval: PlanInterval): string => {
|
|
477
|
+
const time = interval.time ? ` at ${interval.time}` : '';
|
|
478
|
+
switch (interval.type) {
|
|
479
|
+
case 'hourly':
|
|
480
|
+
return 'every hour';
|
|
481
|
+
case 'hours':
|
|
482
|
+
return `every ${interval.hours?.replace('hrs', '')} hours`;
|
|
483
|
+
case 'minutes':
|
|
484
|
+
return `every ${interval.minutes} minutes`;
|
|
485
|
+
case 'daily':
|
|
486
|
+
return `daily${time}`;
|
|
487
|
+
case 'weekly':
|
|
488
|
+
return `weekly on ${interval.days}${time}`;
|
|
489
|
+
case 'days': {
|
|
490
|
+
const dayList = interval.days?.replace(/-$/, '').replace(/-/g, ', ');
|
|
491
|
+
return `every ${dayList}${time}`;
|
|
492
|
+
}
|
|
493
|
+
case 'monthly':
|
|
494
|
+
return `monthly (${interval.days})${time}`;
|
|
495
|
+
default:
|
|
496
|
+
return interval.type;
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
475
500
|
export const secondsToMinutes = (seconds: number) => {
|
|
476
501
|
if (seconds === 0) {
|
|
477
502
|
return '';
|
package/src/utils/plans.ts
CHANGED
|
@@ -13,11 +13,11 @@ export function planIntervalName(interval: PlanInterval): string {
|
|
|
13
13
|
case 'monthly':
|
|
14
14
|
return 'Every Month';
|
|
15
15
|
case 'days':
|
|
16
|
-
return `Every ${interval.days}
|
|
16
|
+
return `Every ${interval.days}`;
|
|
17
17
|
case 'hours':
|
|
18
|
-
return `Every ${interval.hours}
|
|
18
|
+
return `Every ${interval.hours}`;
|
|
19
19
|
case 'minutes':
|
|
20
|
-
return `Every ${interval.minutes}
|
|
20
|
+
return `Every ${interval.minutes}`;
|
|
21
21
|
default:
|
|
22
22
|
return '';
|
|
23
23
|
}
|