Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit f073aec

Browse files
committedMar 19, 2025
DBLab email notifications settings
1 parent e838cc8 commit f073aec

File tree

10 files changed

+644
-1
lines changed

10 files changed

+644
-1
lines changed
 

‎ui/packages/platform/src/actions/actions.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const Actions = Reflux.createActions([{
5656
createOrg: ASYNC_ACTION,
5757
updateAiBotSettings: ASYNC_ACTION,
5858
updateAuditSettings: ASYNC_ACTION,
59+
updateDBLabSettings: ASYNC_ACTION,
5960
inviteUser: ASYNC_ACTION,
6061
useDemoData: ASYNC_ACTION,
6162
setReportsProject: {},
@@ -730,6 +731,42 @@ Actions.updateAuditSettings.listen(function (token, orgId, orgData) {
730731
});
731732
});
732733

734+
Actions.updateDBLabSettings.listen(function (token, orgId, orgData) {
735+
let action = this;
736+
737+
if (!api) {
738+
settings.init(function () {
739+
api = new Api(settings);
740+
});
741+
}
742+
743+
action.progressed({ orgId } + orgData);
744+
timeoutPromise(REQUEST_TIMEOUT, api.updateDBLabSettings(token, orgId, orgData))
745+
746+
.then(result => {
747+
result.json()
748+
.then(json => {
749+
if (json) {
750+
action.completed(json);
751+
} else {
752+
action.failed(new Error('wrong_reply'));
753+
}
754+
})
755+
.catch(err => {
756+
console.error(err);
757+
action.failed(new Error('wrong_reply'));
758+
});
759+
})
760+
.catch(err => {
761+
console.error(err);
762+
if (err && err.message && err.message === 'timeout') {
763+
action.failed(new Error('failed_fetch'));
764+
} else {
765+
action.failed(new Error('wrong_reply'));
766+
}
767+
});
768+
});
769+
733770
Actions.createOrg.listen(function (token, orgData) {
734771
let action = this;
735772

‎ui/packages/platform/src/api/api.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ class Api {
132132
body.last_name = data.last_name;
133133
}
134134

135+
if (data.dblab_low_disk_space_notifications_enabled !== 'undefined') {
136+
body.dblab_low_disk_space_notifications_enabled = data.dblab_low_disk_space_notifications_enabled;
137+
}
138+
139+
if (data.dblab_old_clones_notifications_enabled !== 'undefined') {
140+
body.dblab_old_clones_notifications_enabled = data.dblab_old_clones_notifications_enabled;
141+
}
142+
135143
return this.post(`${this.apiServer}/rpc/update_user_profile`, body, {
136144
headers: headers
137145
});
@@ -528,6 +536,27 @@ class Api {
528536
});
529537
}
530538

539+
updateDBLabSettings(token, orgId, orgData) {
540+
let params = {};
541+
let headers = {
542+
Authorization: 'Bearer ' + token,
543+
prefer: 'return=representation'
544+
};
545+
546+
if (typeof orgData.dblab_low_disk_space_notifications_threshold_percent !== 'undefined') {
547+
params.dblab_low_disk_space_notifications_threshold_percent = orgData.dblab_low_disk_space_notifications_threshold_percent
548+
}
549+
550+
if (typeof orgData.dblab_old_clones_notifications_threshold_hours !== 'undefined') {
551+
params.dblab_old_clones_notifications_threshold_hours = orgData.dblab_old_clones_notifications_threshold_hours
552+
}
553+
554+
return this.patch(`${this.apiServer}/orgs?id=eq.` + orgId, params, {
555+
headers: headers
556+
});
557+
}
558+
559+
531560
testSiemServiceConnection(token, data) {
532561
let params = {};
533562
let headers = {

‎ui/packages/platform/src/components/DBLabSettingsForm/DBLabSettingsForm.tsx

Lines changed: 413 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from "react";
2+
import DBLabSettingsForm from "./DBLabSettingsForm";
3+
4+
export interface DBLabSettingsFormProps {
5+
mode?: string | undefined
6+
project?: string | undefined
7+
org?: string | number
8+
orgId?: number
9+
orgPermissions?: {
10+
settingsOrganizationUpdate?: boolean
11+
}
12+
orgData?: {
13+
priveleged_until: Date
14+
chats_private_allowed: boolean
15+
consulting_type: string | null
16+
}
17+
match: {
18+
params: {
19+
project?: string
20+
projectId?: string | number | undefined
21+
org?: string
22+
}
23+
}
24+
}
25+
26+
27+
28+
export const DBLabSettingsFormWrapper = (props: DBLabSettingsFormProps) => {
29+
return <DBLabSettingsForm {...props} />
30+
}

‎ui/packages/platform/src/components/IndexPage/IndexPage.tsx

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import React, { Component, useState } from 'react'
9-
import { Switch, Route, NavLink, Redirect, useRouteMatch } from 'react-router-dom'
9+
import { Switch, Route, NavLink, Redirect } from 'react-router-dom'
1010
import {
1111
AppBar,
1212
Toolbar,
@@ -90,6 +90,7 @@ import cn from "classnames";
9090
import { BotSettingsFormWrapper } from "../BotSettingsForm/BotSettingsFormWrapper";
9191
import { AuditSettingsFormWrapper } from "../AuditSettingsForm/AuditSettingsFormWrapper";
9292
import { ExpandLess, ExpandMore } from "@material-ui/icons";
93+
import { DBLabSettingsFormWrapper } from "../DBLabSettingsForm/DBLabSettingsFormWrapper";
9394

9495

9596
interface IndexPageWithStylesProps extends IndexPageProps {
@@ -725,6 +726,21 @@ function OrganizationMenu(parentProps: OrganizationMenuProps) {
725726
</NavLink>
726727
</ListItem>
727728
)}
729+
{orgData !== null && Permissions.isAdmin(orgData) && (
730+
<ListItem
731+
button
732+
className={parentProps.classes.menuItem}
733+
id="menuSettingsDBLab"
734+
>
735+
<NavLink
736+
className={parentProps.classes.menuItemLink}
737+
activeClassName={parentProps.classes.menuItemActiveLink}
738+
to={'/' + org + '/dblab-settings'}
739+
>
740+
DBLab settings
741+
</NavLink>
742+
</ListItem>
743+
)}
728744
{orgPermissions && orgPermissions.auditLogView && (
729745
<ListItem
730746
disabled={
@@ -1039,6 +1055,12 @@ function OrganizationWrapper(parentProps: OrganizationWrapperProps) {
10391055
<AuditSettingsFormWrapper {...props} {...customProps} {...queryProps} />
10401056
)}
10411057
/>
1058+
<Route
1059+
path="/:org/dblab-settings"
1060+
render={(props) => (
1061+
<DBLabSettingsFormWrapper {...props} {...customProps} {...queryProps} />
1062+
)}
1063+
/>
10421064
<Route
10431065
path="/:org/billing"
10441066
render={(props) => (

‎ui/packages/platform/src/components/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export interface Orgs {
4141
is_chat_public_by_default: boolean
4242
chats_private_allowed: boolean
4343
consulting_type: string | null
44+
dblab_old_clones_notifications_threshold_hours: number | null
45+
dblab_low_disk_space_notifications_threshold_percent: number | null
4446
data: {
4547
plan: string
4648
} | null

‎ui/packages/platform/src/pages/Profile/ProfileWrapper.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ export const ProfileWrapper = () => {
3939
color: '#000!important',
4040
fontWeight: 'bold',
4141
},
42+
subLabel: {
43+
marginTop: theme.spacing(2),
44+
marginLeft: theme.spacing(1),
45+
marginBottom: theme.spacing(1),
46+
color: '#000!important',
47+
fontWeight: 500,
48+
width: '100%'
49+
},
4250
dense: {
4351
marginTop: 16,
4452
},

‎ui/packages/platform/src/pages/Profile/index.jsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class Profile extends Component {
6868
is_chats_email_notifications_enabled: values.is_chats_email_notifications_enabled,
6969
first_name: values.first_name,
7070
last_name: values.last_name,
71+
dblab_low_disk_space_notifications_enabled: values.is_dblab_low_disk_space_notifications_enabled,
72+
dblab_old_clones_notifications_enabled: values.is_dblab_old_clones_notifications_enabled
7173
});
7274
}
7375
};
@@ -80,6 +82,8 @@ class Profile extends Component {
8082
first_name: data?.data?.info?.first_name || '',
8183
last_name: data?.data?.info?.last_name || '',
8284
is_chats_email_notifications_enabled: data?.data?.info?.chats_email_notifications_enabled || false,
85+
is_dblab_low_disk_space_notifications_enabled: data?.data?.info?.dblab_low_disk_space_notifications_enabled,
86+
is_dblab_old_clones_notifications_enabled: data?.data?.info?.dblab_old_clones_notifications_enabled
8387
};
8488

8589

@@ -182,6 +186,41 @@ class Profile extends Component {
182186
}
183187
label="Send an email notification if a new message from AI Assistant remains unread for more than one minute"
184188
/>
189+
<InputLabel className={classes.subLabel} id="visibility-radio-buttons-group-sub-label">
190+
DBLab notifications
191+
</InputLabel>
192+
<FormControlLabel
193+
className={classes.formControlLabel}
194+
control={
195+
<Checkbox
196+
icon={<CheckBoxOutlineBlankIcon fontSize="large" />}
197+
checkedIcon={<CheckBoxIcon fontSize="large" />}
198+
name="is_dblab_low_disk_space_notifications_enabled"
199+
className={classes.formControlLabelCheckbox}
200+
checked={values.is_dblab_low_disk_space_notifications_enabled}
201+
onChange={(event) =>
202+
setFieldValue('is_dblab_low_disk_space_notifications_enabled', event.target.checked)
203+
}
204+
/>
205+
}
206+
label="Receive notifications about low disk space" //@TODO: @Nik, help me with text here, I think it should be connected with "Administrators" role in org
207+
/>
208+
<FormControlLabel
209+
className={classes.formControlLabel}
210+
control={
211+
<Checkbox
212+
icon={<CheckBoxOutlineBlankIcon fontSize="large" />}
213+
checkedIcon={<CheckBoxIcon fontSize="large" />}
214+
name="is_dblab_old_clones_notifications_enabled"
215+
className={classes.formControlLabelCheckbox}
216+
checked={values.is_dblab_old_clones_notifications_enabled}
217+
onChange={(event) =>
218+
setFieldValue('is_dblab_old_clones_notifications_enabled', event.target.checked)
219+
}
220+
/>
221+
}
222+
label="Receive notifications about old clones"
223+
/>
185224
</Grid>
186225
<Grid
187226
item

‎ui/packages/platform/src/stores/store.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,36 @@ const Store = Reflux.createStore({
651651
this.trigger(this.data);
652652
},
653653

654+
onUpdateDBLabSettingsFailed: function (error) {
655+
this.data.orgProfile.isUpdating = false;
656+
this.data.orgProfile.updateError = true;
657+
this.data.orgProfile.updateErrorMessage = error.message;
658+
this.trigger(this.data);
659+
},
660+
661+
onUpdateDBLabSettingsProgressed: function (data) {
662+
this.data.orgProfile.updateErrorFields = null;
663+
this.data.orgProfile.isUpdating = true;
664+
665+
this.trigger(this.data);
666+
},
667+
668+
onUpdateDBLabSettingsCompleted: function (data) {
669+
this.data.orgProfile.isUpdating = false;
670+
this.data.orgProfile.updateErrorMessage = this.getError(data);
671+
this.data.orgProfile.updateError = !!this.data.orgProfile.updateErrorMessage;
672+
673+
if (!this.data.orgProfile.updateError && data.length > 0) {
674+
this.data.orgProfile.updateErrorFields = null;
675+
this.data.orgProfile.data = data[0];
676+
Actions.getUserProfile(this.data.auth.token);
677+
Actions.getOrgs(this.data.auth.token, this.data.orgProfile.orgId);
678+
Actions.showNotification('DBLab settings successfully saved.', 'success');
679+
}
680+
681+
this.trigger(this.data);
682+
},
683+
654684
onTestSiemServiceConnectionFailed: function (error) {
655685
this.data.orgProfile.isUpdating = false;
656686
this.trigger(this.data);

‎ui/packages/platform/src/utils/utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*--------------------------------------------------------------------------
66
*/
77

8+
import parse, { IPostgresInterval } from "postgres-interval";
9+
810
export const generateToken = function () {
911
const a =
1012
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'.split('')
@@ -68,4 +70,35 @@ export const isMobileDevice = (): boolean => {
6870
const isMobileScreen = window.innerWidth <= 1366;
6971

7072
return hasTouchScreen && isMobileScreen;
73+
}
74+
75+
export const pgIntervalToHours = (interval?: string | null): number | null => {
76+
if (!interval) {
77+
return null;
78+
}
79+
80+
const parsed: IPostgresInterval = parse(interval);
81+
82+
const yearsToHours = (parsed.years ?? 0) * 365 * 24;
83+
const monthsToHours = (parsed.months ?? 0) * 30 * 24;
84+
const daysToHours = (parsed.days ?? 0) * 24;
85+
const hours = parsed.hours ?? 0;
86+
const minutesToHours = (parsed.minutes ?? 0) / 60;
87+
const secondsToHours = (parsed.seconds ?? 0) / 3600;
88+
89+
return yearsToHours + monthsToHours + daysToHours + hours + minutesToHours + secondsToHours;
90+
}
91+
92+
export const hoursToPgInterval = (hours: number): string => {
93+
const totalMinutes = Math.floor(hours * 60);
94+
const days = Math.floor(totalMinutes / (24 * 60));
95+
const remainingMinutes = totalMinutes % (24 * 60);
96+
const h = Math.floor(remainingMinutes / 60);
97+
const m = remainingMinutes % 60;
98+
99+
if (days > 0) {
100+
return `${days} days ${h}:${m}:00`;
101+
} else {
102+
return `${h}:${m}:00`;
103+
}
71104
}

0 commit comments

Comments
 (0)
Please sign in to comment.