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

CBIIT / crdc-datahub-ui / 16006182009

01 Jul 2025 05:32PM UTC coverage: 62.703% (-8.6%) from 71.278%
16006182009

Pull #756

github

web-flow
Merge pull request #755 from CBIIT/revert-omb-date

revert: OMB expiration update
Pull Request #756: Sync 3.4.0 with 3.3.0

3560 of 6102 branches covered (58.34%)

Branch coverage included in aggregate %.

4920 of 7422 relevant lines covered (66.29%)

227.7 hits per line

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

56.58
/src/components/InactivityDialog/InactivityDialog.tsx
1
import React, { useState, useEffect, FC } from "react";
2
import { Button, Dialog, DialogTitle, styled } from "@mui/material";
3
import { useNavigate } from "react-router-dom";
4
import { useSnackbar } from "notistack";
5
import { useAuthContext } from "../Contexts/AuthContext";
6
import { Logger, secondsToMinuteString } from "../../utils";
7
import CloseIcon from "../../assets/icons/close_icon.svg";
8

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

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

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

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

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

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

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

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

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

161
  const handleExtendSession = () => {
8✔
162
    extendSession();
×
163
  };
164

165
  const handleSignOutNoBanner = async () => {
8✔
166
    const logoutStatus = await logout();
2✔
167
    if (logoutStatus) {
2!
168
      navigate("/");
2✔
169
      setWarning(false);
2✔
170
    }
171
  };
172

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

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

200
  useEffect(() => {
8✔
201
    let ID: NodeJS.Timer;
202
    if (isLoggedIn) {
6!
203
      ID = setInterval(loadData, 10 * 1000);
6✔
204
    } else {
205
      clearInterval(ID);
×
206
    }
207

208
    return () => clearInterval(ID);
6✔
209
  }, [isLoggedIn]);
210

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

290
export default InactivityDialog;
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