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

CBIIT / crdc-datahub-ui / 12674013360

08 Jan 2025 03:44PM UTC coverage: 57.519%. First build
12674013360

Pull #586

github

web-flow
Merge 9f7e2bbde into 88c8f5664
Pull Request #586: fix: CLI Tool should not be bound by `data_submission:create`

2736 of 5186 branches covered (52.76%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 2 files covered. (0.0%)

3942 of 6424 relevant lines covered (61.36%)

140.28 hits per line

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

52.63
/src/components/Header/components/NavbarDesktop.tsx
1
import React, { useEffect, useState, useRef } from "react";
2
import { NavLink, Link, useNavigate, useLocation } from "react-router-dom";
3
import { Button, styled } from "@mui/material";
4
import { useAuthContext } from "../../Contexts/AuthContext";
5
import GenericAlert from "../../GenericAlert";
6
import { HeaderLinks, HeaderSubLinks } from "../../../config/HeaderConfig";
7
import APITokenDialog from "../../APITokenDialog";
8
import UploaderToolDialog from "../../UploaderToolDialog";
9

10
const Nav = styled("div")({
2✔
11
  top: 0,
12
  left: 0,
13
  width: "100%",
14
  background: "#ffffff",
15
  boxShadow: "-0.1px 6px 9px -6px rgba(0, 0, 0, 0.5)",
16
  zIndex: 1100,
17
  position: "relative",
18
  "& .dropdownContainer": {
19
    margin: "0 auto",
20
    position: "relative",
21
    width: "1400px",
22
  },
23
  "& .loggedInName": {
24
    color: "#007BBD",
25
    textAlign: "right",
26
    fontSize: "14px",
27
    fontFamily: "Poppins",
28
    fontStyle: "normal",
29
    fontWeight: 600,
30
    lineHeight: "normal",
31
    letterSpacing: "0.42px",
32
    textDecoration: "none",
33
    textTransform: "uppercase",
34
    padding: "10px 0",
35
    marginBottom: "4.5px",
36
    marginRight: "40px",
37
  },
38
  "& .invisible": {
39
    visibility: "hidden",
40
  },
41
});
42

43
const NavContainer = styled("div")({
2✔
44
  margin: "0 auto",
45
  maxWidth: "1400px",
46
  textAlign: "left",
47
  position: "relative",
48
  display: "flex",
49
  justifyContent: "space-between",
50
  alignItems: "end",
51
  "#navbar-dropdown-name-container": {
52
    margin: 0,
53
  },
54
});
55

56
const UlContainer = styled("ul")({
2✔
57
  listStyle: "none",
58
  margin: 0,
59
  paddingTop: "17px",
60
  paddingLeft: "11px",
61
  display: "flex",
62
  width: "100%",
63
});
64

65
const LiSection = styled("li")({
2✔
66
  display: "inline-block",
67
  position: "relative",
68
  lineHeight: "50px",
69
  letterSpacing: "1px",
70
  textAlign: "center",
71
  transition: "all 0.3s ease-in-out",
72
  "& a": {
73
    color: "#585C65",
74
    textDecoration: "none",
75
  },
76
  "& .displayName": {
77
    color: "#007BBD",
78
    fontSize: "14px",
79
    lineHeight: "20px",
80
    padding: "10px 0",
81
    textAlign: "right",
82
    width: "fit-content",
83
  },
84
  "&.name-dropdown-li": {
85
    marginLeft: "auto",
86
  },
87
  "&.login-button": {
88
    lineHeight: "48px",
89
  },
90
  "& .navTitle": {
91
    display: "block",
92
    color: "#585C65",
93
    fontFamily: "poppins",
94
    fontSize: "17px",
95
    fontWeight: 600,
96
    lineHeight: "40px",
97
    letterSpacing: "normal",
98
    textDecoration: "none",
99
    margin: "0 5px",
100
    padding: "0 8px",
101
    userSelect: "none",
102
    borderTop: "4px solid transparent",
103
    borderLeft: "4px solid transparent",
104
    borderRight: "4px solid transparent",
105
    "&:hover": {
106
      cursor: "pointer",
107
    },
108
  },
109
  "& .navText": {
110
    borderBottom: "4px solid transparent",
111
    width: "fit-content",
112
    margin: "auto",
113
    "&:hover": {
114
      cursor: "pointer",
115
      color: "#3A75BD",
116
      borderBottom: "4px solid #3A75BD",
117
      "&::after": {
118
        content: '""',
119
        display: "inline-block",
120
        width: "6px",
121
        height: "6px",
122
        borderBottom: "1px solid #298085",
123
        borderLeft: "1px solid #298085",
124
        margin: "0 0 4px 8px",
125
        transform: "rotate(-45deg)",
126
        WebkitTransform: "rotate(-45deg)",
127
      },
128
    },
129
    "&::after": {
130
      content: '""',
131
      display: "inline-block",
132
      width: "6px",
133
      height: "6px",
134
      borderBottom: "1px solid #585C65",
135
      borderLeft: "1px solid #585C65",
136
      margin: "0 0 4px 8px",
137
      transform: "rotate(-45deg)",
138
      WebkitTransform: "rotate(-45deg)",
139
    },
140
  },
141
  "& .clicked": {
142
    color: "#FFFFFF",
143
    background: "#1F4671",
144
    "&::after": {
145
      borderTop: "1px solid #FFFFFF",
146
      borderRight: "1px solid #FFFFFF",
147
      borderBottom: "0",
148
      borderLeft: "0",
149
      margin: "0 0 0 8px",
150
    },
151
    "&:hover": {
152
      borderBottom: "4px solid #1F4671",
153
      color: "#FFFFFF",
154
      "&::after": {
155
        content: '""',
156
        display: "inline-block",
157
        width: "6px",
158
        height: "6px",
159
        borderTop: "1px solid #FFFFFF",
160
        borderRight: "1px solid #FFFFFF",
161
        borderBottom: "0",
162
        borderLeft: "0",
163
        margin: "0 0 0 8px",
164
        transform: "rotate(-45deg)",
165
        WebkitTransform: "rotate(-45deg)",
166
      },
167
    },
168
  },
169
  "& .directLink::after": {
170
    display: "none",
171
  },
172
  "& .directLink:hover::after": {
173
    display: "none",
174
  },
175
  "& .shouldBeUnderlined": {
176
    borderBottom: "4px solid #3A75BD !important",
177
  },
178
  "& .navTitleClicked": {
179
    display: "block",
180
    color: "#FFFFFF",
181
    fontFamily: "poppins",
182
    fontSize: "17px",
183
    fontWeight: 600,
184
    lineHeight: "40px",
185
    letterSpacing: "normal",
186
    textDecoration: "none",
187
    margin: "0 5px",
188
    padding: "0 8px",
189
    userSelect: "none",
190
    background: "#1F4671",
191
    borderTop: "4px solid #5786FF",
192
    borderLeft: "4px solid #5786FF",
193
    borderRight: "4px solid #5786FF",
194
  },
195
  "& .invisible": {
196
    visibility: "hidden",
197
  },
198
});
199

200
const Dropdown = styled("div")({
2✔
201
  top: "60.5px",
202
  left: 0,
203
  width: "100%",
204
  background: "#1F4671",
205
  zIndex: 1100,
206
  position: "absolute",
207
});
208

209
const NameDropdownContainer = styled("div")({
2✔
210
  margin: "0 auto",
211
  textAlign: "left",
212
  position: "relative",
213
  maxWidth: "1400px",
214
  "& .dropdownList": {
215
    background: "#1F4671",
216
    display: "inline-flex",
217
    gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))",
218
    padding: "32px 32px 0 32px",
219
  },
220
  "& .dropdownItem": {
221
    padding: "0 10px 52px 10px",
222
    textAlign: "left",
223
    fontFamily: "'Poppins', sans-serif",
224
    fontStyle: "normal",
225
    fontWeight: 600,
226
    fontSize: "20px",
227
    lineHeight: "110%",
228
    color: "#FFFFFF",
229
    textDecoration: "none",
230
    cursor: "pointer",
231
    "&:hover": {
232
      textDecoration: "underline",
233
    },
234
  },
235
  "& .dropdownItemButton": {
236
    paddingBottom: 0,
237
    textTransform: "none",
238
    "&:hover": {
239
      background: "transparent",
240
    },
241
  },
242
  "#navbar-dropdown-item-name-logout": {
243
    maxWidth: "200px",
244
  },
245
});
246

247
const StyledLoginLink = styled(Link)({
2✔
248
  color: "#007BBD !important",
249
  textAlign: "right",
250
  fontSize: "14px",
251
  fontFamily: "Poppins",
252
  fontStyle: "normal",
253
  fontWeight: 600,
254
  lineHeight: "normal",
255
  letterSpacing: "0.42px",
256
  textDecoration: "none",
257
  textTransform: "uppercase",
258
  padding: "10px 0",
259
  marginBottom: "4.5px",
260
  marginRight: "32px",
261
});
262

263
const useOutsideAlerter = (ref1: React.RefObject<HTMLDivElement>) => {
2✔
264
  useEffect(() => {
12✔
265
    function handleClickOutside(event) {
266
      if (
×
267
        !event.target ||
×
268
        (event.target.getAttribute("class") !== "dropdownList" &&
269
          ref1.current &&
270
          !ref1.current.contains(event.target))
271
      ) {
272
        const toggle = document.getElementsByClassName("navText clicked");
×
273
        if (toggle[0] && !event.target.getAttribute("class")?.includes("navText clicked")) {
×
274
          const temp: HTMLElement = toggle[0] as HTMLElement;
×
275
          temp.click();
×
276
        }
277
      }
278
    }
279

280
    document.addEventListener("mousedown", handleClickOutside);
12✔
281
    return () => {
12✔
282
      document.removeEventListener("mousedown", handleClickOutside);
12✔
283
    };
284
  }, [ref1]);
285
};
286

287
const NavBar = () => {
2✔
288
  const { isLoggedIn, user, logout } = useAuthContext();
12✔
289
  const navigate = useNavigate();
12✔
290
  const location = useLocation();
12✔
291

292
  const [clickedTitle, setClickedTitle] = useState("");
12✔
293
  const [openAPITokenDialog, setOpenAPITokenDialog] = useState<boolean>(false);
12✔
294
  const [uploaderToolOpen, setUploaderToolOpen] = useState<boolean>(false);
12✔
295
  const [showLogoutAlert, setShowLogoutAlert] = useState<boolean>(false);
12✔
296
  const [restorePath, setRestorePath] = useState<string>(null);
12✔
297
  const dropdownSelection = useRef<HTMLDivElement>(null);
12✔
298

299
  const clickableObject = HeaderLinks.filter(
12✔
300
    (item) => item.className === "navMobileItem clickable"
72✔
301
  );
302
  const clickableTitle = clickableObject.map((item) => item.name);
24✔
303
  const displayName = user?.firstName?.toUpperCase() || "N/A";
12✔
304

305
  clickableTitle.push(displayName);
12✔
306

307
  HeaderSubLinks[displayName] = [
12✔
308
    {
309
      name: "User Profile",
310
      link: `/profile/${user?._id}`,
311
      id: "navbar-dropdown-item-user-profile",
312
      className: "navMobileSubItem",
313
    },
314
    {
315
      name: "Uploader CLI Tool",
NEW
316
      onClick: () => setUploaderToolOpen(true),
×
317
      id: "navbar-dropdown-item-uploader-tool",
318
      className: "navMobileSubItem action",
319
    },
320
    {
321
      name: "API Token",
NEW
322
      onClick: () => setOpenAPITokenDialog(true),
×
323
      id: "navbar-dropdown-item-api-token",
324
      className: "navMobileSubItem action",
325
      permissions: ["data_submission:create"],
326
    },
327
    {
328
      name: "Manage Studies",
329
      link: "/studies",
330
      id: "navbar-dropdown-item-studies-manage",
331
      className: "navMobileSubItem",
332
      permissions: ["study:manage"],
333
    },
334
    {
335
      name: "Manage Programs",
336
      link: "/programs",
337
      id: "navbar-dropdown-item-program-manage",
338
      className: "navMobileSubItem",
339
      permissions: ["program:manage"],
340
    },
341
    {
342
      name: "Manage Users",
343
      link: "/users",
344
      id: "navbar-dropdown-item-user-manage",
345
      className: "navMobileSubItem",
346
      permissions: ["user:manage"],
347
    },
348
    {
349
      name: "Logout",
350
      onClick: () => handleLogout(),
×
351
      id: "navbar-dropdown-item-logout",
352
      className: "navMobileSubItem action",
353
    },
354
  ];
355

356
  const handleLogout = async () => {
12✔
357
    setClickedTitle("");
×
358
    const logoutStatus = await logout();
×
359
    if (logoutStatus) {
×
360
      navigate("/");
×
361
      setShowLogoutAlert(true);
×
362
      setTimeout(() => setShowLogoutAlert(false), 10000);
×
363
    }
364
  };
365

366
  const handleMenuClick = (e) => {
12✔
367
    if (e.target.textContent === clickedTitle || !clickableTitle.includes(e.target.textContent)) {
×
368
      setClickedTitle("");
×
369
    } else {
370
      setClickedTitle(e.target.textContent);
×
371
    }
372
  };
373

374
  const onKeyPressHandler = (e) => {
12✔
375
    if (e.key === "Enter") {
×
376
      handleMenuClick(e);
×
377
    }
378
  };
379

380
  const shouldBeUnderlined = (item) => {
12✔
381
    const linkName = item.name;
60✔
382
    const correctPath = window.location.pathname;
60✔
383
    if (item.className === "navMobileItem") {
60✔
384
      return correctPath === item.link;
36✔
385
    }
386
    if (HeaderSubLinks[linkName] === undefined) {
24!
387
      return false;
×
388
    }
389
    const linkNames = Object.values(HeaderSubLinks[linkName]).map((e: NavBarSubItem) => e.link);
108✔
390
    return linkNames.includes(correctPath);
24✔
391
  };
392

393
  useOutsideAlerter(dropdownSelection);
12✔
394

395
  useEffect(() => {
12✔
396
    if (!isLoggedIn) {
12✔
397
      setClickedTitle("");
10✔
398
    }
399
  }, [isLoggedIn]);
400

401
  useEffect(() => {
12✔
402
    setClickedTitle("");
12✔
403
  }, []);
404

405
  useEffect(() => {
12✔
406
    if (!location?.pathname || location?.pathname === "/") {
12!
407
      setRestorePath(null);
12✔
408
      return;
12✔
409
    }
410

411
    setRestorePath(location?.pathname);
×
412
  }, [location]);
413

414
  return (
12✔
415
    <Nav>
416
      <GenericAlert open={showLogoutAlert}>
417
        <span>You have been logged out.</span>
418
      </GenericAlert>
419
      <NavContainer>
420
        <UlContainer>
421
          {HeaderLinks.map((navItem: NavBarItem) => {
422
            if (
72✔
423
              navItem?.permissions?.length > 0 &&
42✔
424
              !navItem?.permissions?.every(
425
                (permission: AuthPermissions) => user?.permissions?.includes(permission)
12✔
426
              )
427
            ) {
428
              return null;
12✔
429
            }
430

431
            return (
60✔
432
              <LiSection key={navItem.id}>
433
                {navItem.className === "navMobileItem" ? (
30✔
434
                  <div className="navTitle directLink">
435
                    <NavLink
436
                      to={navItem.link}
437
                      target={navItem.link.startsWith("https://") ? "_blank" : "_self"}
18✔
438
                    >
439
                      <div
440
                        id={navItem.id}
441
                        role="button"
442
                        tabIndex={0}
443
                        className={`navText directLink ${
444
                          shouldBeUnderlined(navItem) ? "shouldBeUnderlined" : ""
18!
445
                        }`}
446
                        onKeyDown={onKeyPressHandler}
447
                        onClick={handleMenuClick}
448
                      >
449
                        {navItem.name}
450
                      </div>
451
                    </NavLink>
452
                  </div>
453
                ) : (
454
                  <div className={clickedTitle === navItem.name ? "navTitleClicked" : "navTitle"}>
12!
455
                    <div
456
                      id={navItem.id}
457
                      role="button"
458
                      tabIndex={0}
459
                      className={`${
460
                        clickedTitle === navItem.name ? "navText clicked" : "navText"
12!
461
                      } ${shouldBeUnderlined(navItem) ? "shouldBeUnderlined" : ""}`}
12!
462
                      onKeyDown={onKeyPressHandler}
463
                      onClick={handleMenuClick}
464
                    >
465
                      {navItem.name}
466
                    </div>
467
                  </div>
468
                )}
469
              </LiSection>
470
            );
471
          })}
472
          <LiSection className={`name-dropdown-li${isLoggedIn ? "" : " login-button"}`}>
6✔
473
            {isLoggedIn ? (
6✔
474
              <div
475
                id="navbar-dropdown-name-container"
476
                className={clickedTitle === displayName ? "navTitleClicked" : "navTitle"}
1!
477
              >
478
                <div
479
                  id="navbar-dropdown-name"
480
                  role="button"
481
                  tabIndex={0}
482
                  className={
483
                    clickedTitle === displayName
1!
484
                      ? "navText displayName clicked"
485
                      : "navText displayName"
486
                  }
487
                  onKeyDown={onKeyPressHandler}
488
                  onClick={handleMenuClick}
489
                >
490
                  {displayName}
491
                </div>
492
              </div>
493
            ) : (
494
              <StyledLoginLink
495
                id="header-navbar-login-button"
496
                to="/login"
497
                state={{ redirectURLOnLoginSuccess: restorePath }}
498
              >
499
                Login
500
              </StyledLoginLink>
501
            )}
502
          </LiSection>
503
        </UlContainer>
504
      </NavContainer>
505
      <Dropdown ref={dropdownSelection} className={clickedTitle === "" ? "invisible" : ""}>
6!
506
        <NameDropdownContainer>
507
          <div className="dropdownList">
508
            {clickedTitle !== ""
6!
509
              ? HeaderSubLinks[clickedTitle]?.map((dropItem) => {
510
                  if (
×
511
                    dropItem?.permissions?.length > 0 &&
×
512
                    !dropItem?.permissions?.every(
513
                      (permission: AuthPermissions) => user?.permissions?.includes(permission)
×
514
                    )
515
                  ) {
516
                    return null;
×
517
                  }
518

519
                  if (dropItem.link) {
×
520
                    return (
×
521
                      <span className="dropdownItem" key={dropItem.id}>
522
                        <Link
523
                          target={
524
                            dropItem.link.startsWith("https://") || dropItem.link.endsWith(".pdf")
×
525
                              ? "_blank"
526
                              : "_self"
527
                          }
528
                          id={dropItem.id}
529
                          to={dropItem.link}
530
                          className="dropdownItem"
531
                          onClick={() => setClickedTitle("")}
×
532
                        >
533
                          {dropItem.name}
534
                          {dropItem.text && <div className="dropdownItemText">{dropItem.text}</div>}
×
535
                        </Link>
536
                      </span>
537
                    );
538
                  }
539

540
                  if (dropItem.onClick) {
×
541
                    return (
×
542
                      <span className="dropdownItem" key={dropItem.id}>
543
                        <Button
544
                          id={dropItem.id}
545
                          className="dropdownItem dropdownItemButton"
546
                          onClick={dropItem.onClick}
547
                        >
548
                          {dropItem.name}
549
                        </Button>
550
                      </span>
551
                    );
552
                  }
553

554
                  return null;
×
555
                })
556
              : null}
557
          </div>
558
        </NameDropdownContainer>
559
      </Dropdown>
560
      <APITokenDialog open={openAPITokenDialog} onClose={() => setOpenAPITokenDialog(false)} />
×
561
      <UploaderToolDialog open={uploaderToolOpen} onClose={() => setUploaderToolOpen(false)} />
×
562
    </Nav>
563
  );
564
};
565

566
export default NavBar;
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

© 2025 Coveralls, Inc