• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CBIIT / crdc-datahub-ui / 18789341118

24 Oct 2025 06:57PM UTC coverage: 78.178% (+15.5%) from 62.703%
18789341118

push

github

web-flow
Merge pull request #888 from CBIIT/3.4.0

3.4.0 Release

4977 of 5488 branches covered (90.69%)

Branch coverage included in aggregate %.

8210 of 9264 new or added lines in 257 files covered. (88.62%)

6307 existing lines in 120 files now uncovered.

30203 of 39512 relevant lines covered (76.44%)

213.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.62
/src/components/InactivityDialog/InactivityDialog.tsx
1
import { Button, Dialog, DialogTitle, styled } from "@mui/material";
1✔
2
import { useSnackbar } from "notistack";
1✔
3
import React, { useState, useEffect, FC } from "react";
1✔
4
import { useNavigate } from "react-router-dom";
1✔
5

6
import CloseIcon from "../../assets/icons/close_icon.svg?url";
1✔
7
import { Logger, secondsToMinuteString } from "../../utils";
1✔
8
import { useAuthContext } from "../Contexts/AuthContext";
1✔
9

10
const InactivityWarningDialog = styled(Dialog)({
1✔
11
  "& .MuiDialog-paper": {
1✔
12
    width: "550px",
1✔
13
    height: "320px",
1✔
14
    borderRadius: "8px",
1✔
15
    border: "2px solid var(--secondary-one, #0B7F99)",
1✔
16
    background: "linear-gradient(0deg, #F2F6FA 0%, #F2F6FA 100%), #2E4D7B",
1✔
17
    boxShadow: "0px 4px 45px 0px rgba(0, 0, 0, 0.40)",
1✔
18
  },
1✔
19
  "& #customized-dialog-title": {
1✔
20
    margin: 0,
1✔
21
    paddingLeft: "30px",
1✔
22
    backgroundColor: "#6D89A2",
1✔
23
    color: "#FFFFFF",
1✔
24
    fontFamily: "Lato",
1✔
25
    fontSize: "20px",
1✔
26
    fontWeight: 600,
1✔
27
    letterSpacing: 0,
1✔
28
  },
1✔
29
});
1✔
30

31
const InactivityWarningContent = styled("div")({
1✔
32
  margin: "50px auto",
1✔
33
  color: "#000000",
1✔
34
  fontFamily: "'Nunito', 'Rubik', sans-serif",
1✔
35
  fontSize: "17px",
1✔
36
  fontWeight: 300,
1✔
37
  letterSpacing: 0,
1✔
38
  lineHeight: "24px",
1✔
39
  "& .buttonWrapper": {
1✔
40
    display: "flex",
1✔
41
    justifyContent: "center",
1✔
42
    alignItems: "center",
1✔
43
    textAlign: "center",
1✔
44
  },
1✔
45
  "& .buttonGroup": {
1✔
46
    color: "#FFFFFF",
1✔
47
    fontFamily: "Lato",
1✔
48
    fontSize: "11px",
1✔
49
    lineHeight: "22px",
1✔
50
    width: "150px",
1✔
51
    border: "1px solid #626262",
1✔
52
    marginTop: "30px",
1✔
53
    borderRadius: "4px",
1✔
54
    fontWeight: 500,
1✔
55
  },
1✔
56
  "& .extendButton": {
1✔
57
    backgroundColor: "#566672 !important",
1✔
58
  },
1✔
59
  "& .logOutButton": {
1✔
60
    marginLeft: "20px",
1✔
61
    backgroundColor: "#437BBE !important",
1✔
62
  },
1✔
63
});
1✔
64

65
const SessionTimeoutDialog = styled(Dialog)({
1✔
66
  "& .MuiDialog-paper": {
1✔
67
    width: "550px",
1✔
68
    height: "320px",
1✔
69
    borderRadius: "8px",
1✔
70
    border: "2px solid var(--secondary-one, #0B7F99)",
1✔
71
    background: "linear-gradient(0deg, #F2F6FA 0%, #F2F6FA 100%), #2E4D7B",
1✔
72
    boxShadow: "0px 4px 45px 0px rgba(0, 0, 0, 0.40)",
1✔
73
  },
1✔
74
  "& .closeIcon": {
1✔
75
    cursor: "pointer",
1✔
76
    textAlign: "end",
1✔
77
  },
1✔
78
});
1✔
79

80
const SessionTimeoutContent = styled("div")({
1✔
81
  justifyContent: "space-between",
1✔
82
  paddingRight: "33px",
1✔
83
  paddingLeft: "33px",
1✔
84
  paddingTop: "10px",
1✔
85
  fontFamily: "lato",
1✔
86
  textAlign: "center",
1✔
87
  "& .sessionTimeoutTitle": {
1✔
88
    fontSize: "25px",
1✔
89
    fontWeight: "bold",
1✔
90
    paddingBottom: "12px",
1✔
91
    color: "#566672",
1✔
92
  },
1✔
93
  "& .sessionTimeoutMessage": {
1✔
94
    fontSize: "17px",
1✔
95
    paddingBottom: "14px",
1✔
96
  },
1✔
97
  "& .buttonWrapper": {
1✔
98
    display: "flex",
1✔
99
    justifyContent: "center",
1✔
100
    alignItems: "center",
1✔
101
    textAlign: "center",
1✔
102
  },
1✔
103
  "& .buttonGroup": {
1✔
104
    color: "#FFFFFF",
1✔
105
    fontFamily: "Lato",
1✔
106
    fontSize: "11px",
1✔
107
    lineHeight: "22px",
1✔
108
    width: "90px",
1✔
109
    border: "1px solid #626262",
1✔
110
    marginTop: "30px",
1✔
111
    borderRadius: "4px",
1✔
112
    fontWeight: 500,
1✔
113
  },
1✔
114
  "& .closeButton": {
1✔
115
    backgroundColor: "#566672 !important",
1✔
116
  },
1✔
117
  "& .loginButton": {
1✔
118
    marginLeft: "20px",
1✔
119
    backgroundColor: "#437BBE !important",
1✔
120
  },
1✔
121
});
1✔
122

123
/**
124
 * The time (in seconds) at which the timeout warning banner should be displayed.
125
 */
126
const timeoutThresholdSeconds = 300;
1✔
127

128
/**
129
 * An inactivity dialog that handles session the TTL ping and timeout.
130
 *
131
 * @returns InactivityDialog component
132
 */
133
const InactivityDialog: FC = () => {
1✔
134
  const navigate = useNavigate();
5✔
135
  const { enqueueSnackbar } = useSnackbar();
5✔
136
  const { isLoggedIn, logout } = useAuthContext();
5✔
137

138
  const [warning, setWarning] = useState<boolean>(false);
5✔
139
  const [timedOut, setTimedOut] = useState<boolean>(false);
5✔
140
  const [timeLeft, setTimeLeft] = useState<number>(timeoutThresholdSeconds);
5✔
141

142
  const extendSession = async () => {
5✔
143
    try {
×
144
      const res = await fetch(`${window.origin}/api/authn/authenticated`, {
×
UNCOV
145
        method: "POST",
×
UNCOV
146
        headers: {
×
UNCOV
147
          Accept: "application/json",
×
UNCOV
148
          "Content-Type": "application/json",
×
UNCOV
149
        },
×
UNCOV
150
      })
×
151
        .then((response) => response.json())
×
UNCOV
152
        .catch(() => {});
×
153

154
      if (res.status) {
×
155
        setWarning(false);
×
UNCOV
156
      }
×
UNCOV
157
    } catch (e) {
×
158
      Logger.error("Error in extending session", e);
×
UNCOV
159
    }
×
UNCOV
160
  };
×
161

162
  const handleExtendSession = () => {
5✔
163
    extendSession();
×
UNCOV
164
  };
×
165

166
  const handleSignOutNoBanner = async () => {
5✔
167
    const logoutStatus = await logout();
1✔
168
    if (logoutStatus) {
1✔
169
      navigate("/");
1✔
170
      setWarning(false);
1✔
171
    }
1✔
172
  };
1✔
173

174
  const handleSignOut = async () => {
5✔
175
    const logoutStatus = await logout();
×
176
    if (logoutStatus) {
×
177
      navigate("/");
×
178
      setWarning(false);
×
179
      enqueueSnackbar("You have been logged out.", { variant: "default" });
×
UNCOV
180
    }
×
UNCOV
181
  };
×
182

183
  const loadData = async () => {
5✔
184
    try {
3✔
185
      const res = await fetch(`${window.origin}/api/authn/session-ttl`);
3✔
186
      const data = await res.json();
2✔
187
      const { ttl } = data;
2✔
188
      if (ttl <= 0) {
3✔
189
        // If user did not select any option and timed out in BE.
190
        handleSignOutNoBanner();
1✔
191
        setTimedOut(true);
1✔
192
      } else if (ttl > 0 && ttl <= timeoutThresholdSeconds) {
1!
193
        setTimeLeft(ttl);
×
194
        setWarning(true);
×
UNCOV
195
      }
×
196
    } catch (e) {
3✔
197
      Logger.error("Error in fetching session ttl", e);
1✔
198
    }
1✔
199
  };
3✔
200

201
  useEffect(() => {
5✔
202
    let ID: ReturnType<typeof setInterval>;
3✔
203
    if (isLoggedIn) {
3✔
204
      ID = setInterval(loadData, 10 * 1000);
3✔
205
    } else {
3!
206
      clearInterval(ID);
×
UNCOV
207
    }
×
208

209
    return () => clearInterval(ID);
3✔
210
  }, [isLoggedIn]);
5✔
211

212
  return (
5✔
213
    <>
5✔
214
      <InactivityWarningDialog open={warning}>
5✔
215
        <DialogTitle id="customized-dialog-title">Session Timeout Warning</DialogTitle>
5✔
216
        <InactivityWarningContent>
5✔
217
          This session is about to expire due to inactivity.
218
          <br />
5✔
219
          You will be logged out in
220
          {` ${secondsToMinuteString(timeLeft)} `}
5✔
221
          minutes.
222
          <br />
5✔
223
          Please elect to extend this session or logout.
224
          <div className="buttonWrapper">
5✔
225
            <Button
5✔
226
              variant="contained"
5✔
227
              className="buttonGroup extendButton"
5✔
228
              onClick={handleExtendSession}
5✔
229
              disableElevation={false}
5✔
230
            >
231
              EXTEND SESSION
232
            </Button>
5✔
233
            <Button
5✔
234
              variant="contained"
5✔
235
              className="buttonGroup logOutButton"
5✔
236
              onClick={handleSignOut}
5✔
237
              disableElevation={false}
5✔
238
            >
239
              LOGOUT
240
            </Button>
5✔
241
          </div>
5✔
242
        </InactivityWarningContent>
5✔
243
      </InactivityWarningDialog>
5✔
244
      <SessionTimeoutDialog open={timedOut}>
5✔
245
        <DialogTitle>
5✔
246
          <div
5✔
247
            role="button"
5✔
248
            className="closeIcon"
5✔
249
            onClick={() => setTimedOut(false)}
5✔
250
            tabIndex={0}
5✔
251
            onKeyDown={(e) => {
5✔
252
              if (e.key === "Enter") {
×
253
                setTimedOut(false);
×
UNCOV
254
              }
×
UNCOV
255
            }}
×
256
          >
257
            <img style={{ height: 10, marginBottom: 2 }} src={CloseIcon} alt="close icon" />
5✔
258
          </div>
5✔
259
        </DialogTitle>
5✔
260
        <SessionTimeoutContent>
5✔
261
          <div className="sessionTimeoutTitle">Your session has expired.</div>
5✔
262
          <br />
5✔
263
          <div className="sessionTimeoutMessage">Please login again to continue working.</div>
5✔
264
          <div className="buttonWrapper">
5✔
265
            <Button
5✔
266
              variant="contained"
5✔
267
              className="buttonGroup closeButton"
5✔
268
              onClick={() => setTimedOut(false)}
5✔
269
              disableElevation={false}
5✔
270
            >
271
              CLOSE
272
            </Button>
5✔
273
            <Button
5✔
274
              variant="contained"
5✔
275
              className="buttonGroup loginButton"
5✔
276
              onClick={() => {
5✔
277
                setTimedOut(false);
×
278
                navigate("login");
×
UNCOV
279
              }}
×
280
              disableElevation={false}
5✔
281
            >
282
              LOGIN
283
            </Button>
5✔
284
          </div>
5✔
285
        </SessionTimeoutContent>
5✔
286
      </SessionTimeoutDialog>
5✔
287
    </>
5✔
288
  );
289
};
5✔
290

291
export default InactivityDialog;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc