Skip to content

Commit ddeb090

Browse files
authored
Let the user know that signing in is required. (#567)
* Let the user know that signing in is required. * Fix formatting
1 parent b75a7da commit ddeb090

5 files changed

Lines changed: 119 additions & 32 deletions

File tree

frontend/app/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export default function Home() {
77
<div className={styles.page}>
88
<section className={styles.hero}>
99
<h1 className={styles.heading}>How can we help you?</h1>
10-
<SearchBar />
10+
<SearchBar showLoginRequiredMessage />
1111
</section>
1212
</div>
1313
)

frontend/app/search/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const PageResults = () => {
2727
<section className={styles.searchWrapper}>
2828
<Filter />
2929
<div className={styles.searchResultsWrapper}>
30-
<SearchBar />
30+
<SearchBar showLoginRequiredMessage />
3131
{error && (
3232
<div style={{ color: "red", padding: "1rem", textAlign: "center" }}>Error: {error}</div>
3333
)}

frontend/components/Nav/navIcons.module.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,27 @@
1212
color: #303463;
1313
}
1414

15+
.signInPulse {
16+
animation: signInPulse 0.9s ease-out;
17+
}
18+
19+
@keyframes signInPulse {
20+
0% {
21+
transform: scale(1);
22+
box-shadow: 0 0 0 0 rgba(48, 52, 99, 0.4);
23+
}
24+
25+
30% {
26+
transform: scale(1.06);
27+
box-shadow: 0 0 0 10px rgba(48, 52, 99, 0.18);
28+
}
29+
30+
100% {
31+
transform: scale(1);
32+
box-shadow: 0 0 0 0 rgba(48, 52, 99, 0);
33+
}
34+
}
35+
1536
@media screen and (max-width: 700px) {
1637
.createIcon {
1738
display: none;

frontend/components/Nav/navIcons.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export default function NavIcons() {
1616
const router = useRouter()
1717

1818
const [open, setOpen] = React.useState(false)
19+
const [pulseSignin, setPulseSignin] = React.useState(false)
1920
const anchorRef = React.useRef<HTMLButtonElement>(null)
2021

2122
const handleToggle = () => {
@@ -59,6 +60,39 @@ export default function NavIcons() {
5960
prevOpen.current = open
6061
}, [open])
6162

63+
React.useEffect(() => {
64+
if (isLoggedIn) {
65+
setPulseSignin(false)
66+
return
67+
}
68+
69+
let timeoutId: number | undefined
70+
71+
const handleLoginRequiredSearch = () => {
72+
setPulseSignin(false)
73+
window.requestAnimationFrame(() => {
74+
setPulseSignin(true)
75+
})
76+
77+
if (timeoutId) {
78+
window.clearTimeout(timeoutId)
79+
}
80+
81+
timeoutId = window.setTimeout(() => {
82+
setPulseSignin(false)
83+
}, 900)
84+
}
85+
86+
window.addEventListener("npdc:login-required-search", handleLoginRequiredSearch)
87+
88+
return () => {
89+
window.removeEventListener("npdc:login-required-search", handleLoginRequiredSearch)
90+
if (timeoutId) {
91+
window.clearTimeout(timeoutId)
92+
}
93+
}
94+
}, [isLoggedIn])
95+
6296
return (
6397
<div className={styles.icons}>
6498
{!isLoggedIn && (
@@ -76,6 +110,7 @@ export default function NavIcons() {
76110
<Button
77111
variant="outlined"
78112
href="/login"
113+
className={pulseSignin ? styles.signInPulse : undefined}
79114
sx={{
80115
color: "#303463",
81116
borderColor: "#303463",

frontend/components/SearchBar/index.tsx

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,33 @@
33
import { TextField, InputAdornment, CircularProgress, Box } from "@mui/material"
44
import { Search } from "@mui/icons-material"
55
import { useSearch } from "@/providers/SearchProvider"
6+
import { useAuth } from "@/providers/AuthProvider"
67
import React from "react"
78

8-
export const SearchBar = () => {
9+
type SearchBarProps = {
10+
showLoginRequiredMessage?: boolean
11+
}
12+
13+
export const SearchBar = ({ showLoginRequiredMessage = false }: SearchBarProps) => {
914
const {
1015
loading,
1116
setTerm,
1217
state: { term }
1318
} = useSearch()
19+
const { accessToken, hasHydrated } = useAuth()
1420
const [localInput, setLocalInput] = React.useState(term)
21+
const [showAuthError, setShowAuthError] = React.useState(false)
1522

1623
React.useEffect(() => {
1724
setLocalInput(term)
1825
}, [term])
1926

27+
React.useEffect(() => {
28+
if (accessToken) {
29+
setShowAuthError(false)
30+
}
31+
}, [accessToken])
32+
2033
return (
2134
<Box
2235
sx={{
@@ -27,35 +40,53 @@ export const SearchBar = () => {
2740
width: "100%"
2841
}}
2942
>
30-
<TextField
31-
variant="outlined"
32-
value={localInput}
33-
onChange={(e) => setLocalInput(e.target.value)}
34-
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
35-
if (e.key === "Enter") {
36-
setTerm(localInput)
37-
}
38-
}}
39-
sx={{
40-
maxWidth: "546px",
41-
width: "100%",
42-
"& fieldset": { borderRadius: "20px" },
43-
"& .MuiInputBase-input": {
44-
overflow: "hidden",
45-
textOverflow: "ellipsis"
46-
}
47-
}}
48-
placeholder="Search officer, unit, or agency"
49-
slotProps={{
50-
input: {
51-
startAdornment: (
52-
<InputAdornment position="start">
53-
<Search />
54-
</InputAdornment>
55-
)
56-
}
57-
}}
58-
/>
43+
<Box sx={{ width: "100%", maxWidth: "546px" }}>
44+
<TextField
45+
variant="outlined"
46+
value={localInput}
47+
onChange={(e) => {
48+
setLocalInput(e.target.value)
49+
if (showAuthError) {
50+
setShowAuthError(false)
51+
}
52+
}}
53+
onKeyDown={(e: React.KeyboardEvent<HTMLInputElement>) => {
54+
if (e.key === "Enter") {
55+
if (showLoginRequiredMessage && hasHydrated && !accessToken) {
56+
setShowAuthError(true)
57+
window.dispatchEvent(new Event("npdc:login-required-search"))
58+
return
59+
}
60+
61+
setTerm(localInput)
62+
}
63+
}}
64+
error={showAuthError}
65+
helperText={showAuthError ? "Please log in before searching." : " "}
66+
sx={{
67+
width: "100%",
68+
"& fieldset": { borderRadius: "20px" },
69+
"& .MuiInputBase-input": {
70+
overflow: "hidden",
71+
textOverflow: "ellipsis"
72+
},
73+
"& .MuiFormHelperText-root": {
74+
marginLeft: 0,
75+
marginRight: 0
76+
}
77+
}}
78+
placeholder="Search officer, unit, or agency"
79+
slotProps={{
80+
input: {
81+
startAdornment: (
82+
<InputAdornment position="start">
83+
<Search />
84+
</InputAdornment>
85+
)
86+
}
87+
}}
88+
/>
89+
</Box>
5990
{loading && (
6091
<CircularProgress
6192
size={24}

0 commit comments

Comments
 (0)