-
-
Notifications
You must be signed in to change notification settings - Fork 13
Eslint update 2 (split part 2) #2684
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
2de4b81
6bbc3aa
659f096
0602bdb
6e2488a
7c11343
762cb2d
b988221
b637fdd
608828f
b549f36
b4d5614
8a647be
cedf8a0
077f044
405ac51
7937257
4485ea6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ import { | |
| RadioGroup, | ||
| Stack, | ||
| } from "@chakra-ui/react"; | ||
| import { useEffect, useState } from "react"; | ||
| import { useMemo, useState } from "react"; | ||
| import { useNavigate, useLocation, useParams } from "react-router-dom"; | ||
| import { useAtomValue } from "jotai"; | ||
| import { availableBasesAtom, selectedBaseIdAtom } from "stores/globalPreferenceStore"; | ||
|
|
@@ -22,20 +22,36 @@ function BaseSwitcher({ isOpen, onClose }: { isOpen: boolean; onClose: () => voi | |
| const { pathname } = useLocation(); | ||
| const baseId = useAtomValue(selectedBaseIdAtom); | ||
| const availableBases = useAtomValue(availableBasesAtom); | ||
| const currentOrganisationBases = availableBases.filter((base) => base.id !== baseId); | ||
| const firstAvailableBaseId = currentOrganisationBases.find((base) => base)?.id; | ||
| const [value, setValue] = useState(firstAvailableBaseId); | ||
|
|
||
| // Need to set this as soon as we have this value available to set the default radio selection. | ||
| useEffect(() => { | ||
| setValue(firstAvailableBaseId); | ||
| }, [firstAvailableBaseId, baseId]); | ||
| const currentOrganisationBases = useMemo( | ||
| () => availableBases.filter((base) => base.id !== baseId), | ||
| [availableBases, baseId], | ||
| ); | ||
|
|
||
| const firstAvailableBaseId = useMemo( | ||
| () => currentOrganisationBases[0]?.id, | ||
| [currentOrganisationBases], | ||
| ); | ||
|
|
||
| const [value, setValue] = useState(""); | ||
|
|
||
| const switchBase = () => { | ||
| const currentPath = pathname.split(`/bases/${urlBaseId}`)[1]; | ||
|
|
||
| navigate(`/bases/${value}${currentPath}`); | ||
| const actValue = value || firstAvailableBaseId; | ||
|
|
||
| if (!actValue) { | ||
| return; | ||
| } | ||
|
|
||
| navigate(`/bases/${actValue}${currentPath}`); | ||
| onClose(); | ||
|
|
||
| // Need to reset the default radio selection whenever the available bases change. | ||
|
|
||
| const currentOrganisationBases = availableBases.filter((base) => base.id !== actValue); | ||
| const newFirstAvailableBaseId = currentOrganisationBases[0]?.id; | ||
| setValue(newFirstAvailableBaseId || ""); | ||
| }; | ||
|
Comment on lines
+41
to
55
|
||
|
|
||
| return ( | ||
|
|
@@ -46,7 +62,7 @@ function BaseSwitcher({ isOpen, onClose }: { isOpen: boolean; onClose: () => voi | |
| <ModalHeader>Switch Base to</ModalHeader> | ||
| <ModalCloseButton /> | ||
| <ModalBody> | ||
| <RadioGroup onChange={setValue} value={value}> | ||
| <RadioGroup onChange={setValue} value={value || firstAvailableBaseId}> | ||
| <Stack ml={"30%"}> | ||
| {currentOrganisationBases?.map((base) => ( | ||
| <Radio key={base.id} value={base.id}> | ||
|
|
@@ -60,7 +76,12 @@ function BaseSwitcher({ isOpen, onClose }: { isOpen: boolean; onClose: () => voi | |
| <Button onClick={onClose} width="100%"> | ||
| Nevermind | ||
| </Button> | ||
| <Button colorScheme="blue" width="100%" onClick={switchBase} isDisabled={!value}> | ||
| <Button | ||
| colorScheme="blue" | ||
| width="100%" | ||
| onClick={switchBase} | ||
| isDisabled={!(value || firstAvailableBaseId)} | ||
| > | ||
| Switch | ||
| </Button> | ||
| </ModalFooter> | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,4 +1,4 @@ | ||||||
| import { useEffect, useState } from "react"; | ||||||
| import { useEffect, useMemo } from "react"; | ||||||
| import { useAtom, useAtomValue, useSetAtom } from "jotai"; | ||||||
| import { useAuth0 } from "@auth0/auth0-react"; | ||||||
| import { useLazyQuery } from "@apollo/client"; | ||||||
|
|
@@ -17,7 +17,6 @@ export const useLoadAndSetGlobalPreferences = () => { | |||||
| const { user } = useAuth0(); | ||||||
| const authorize = useAuthorization(); | ||||||
| const location = useLocation(); | ||||||
| const [error, setError] = useState<string>(); | ||||||
| const setOrganisation = useSetAtom(organisationAtom); | ||||||
| const [selectedBase, setSelectedBase] = useAtom(selectedBaseAtom); | ||||||
| const [availableBases, setAvailableBases] = useAtom(availableBasesAtom); | ||||||
|
|
@@ -26,112 +25,149 @@ export const useLoadAndSetGlobalPreferences = () => { | |||||
| // Boxtribute God user | ||||||
| const isGod: boolean = (user && user[JWT_ROLE]?.includes("boxtribute_god")) || false; | ||||||
|
|
||||||
| // Set in localStore if current user can Share Public Dashboard Views | ||||||
| localStorage.setItem( | ||||||
| "canShareLink", | ||||||
| authorize({ requiredAbps: ["create_shareable_link"] }).toString(), | ||||||
| ); | ||||||
|
|
||||||
| // validate if base Ids are set in auth0 id token | ||||||
| if (!user || (!isGod && !user[JWT_AVAILABLE_BASES]?.length)) | ||||||
| setError("You do not have access to any bases."); | ||||||
| useEffect(() => { | ||||||
| // Set in localStore if current user can Share Public Dashboard Views | ||||||
| localStorage.setItem( | ||||||
| "canShareLink", | ||||||
| authorize({ requiredAbps: ["create_shareable_link"] }).toString(), | ||||||
| ); | ||||||
| }, [authorize]); | ||||||
|
|
||||||
| const [ | ||||||
| runOrganisationAndBasesQuery, | ||||||
| { loading: isOrganisationAndBasesQueryLoading, data: organisationAndBaseData }, | ||||||
| { loading: isOrganisationAndBasesQueryLoading, data: organisationAndBaseData, error, called }, | ||||||
| ] = useLazyQuery(ORGANISATION_AND_BASES_QUERY); | ||||||
|
|
||||||
| const localError = useMemo(() => { | ||||||
| if (!user || (!isGod && !user[JWT_AVAILABLE_BASES]?.length)) { | ||||||
| return "You do not have access to any bases."; | ||||||
| } else { | ||||||
| const urlBaseIdInput = location.pathname.match(/\/bases\/(\d+)(\/)?/); | ||||||
| const urlBaseId = urlBaseIdInput?.length && urlBaseIdInput[1]; | ||||||
| if (urlBaseId && !isGod && !user[JWT_AVAILABLE_BASES].map(String).includes(urlBaseId)) { | ||||||
| return "The requested base is not available to you."; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| return undefined; | ||||||
| }, [isGod, location.pathname, user]); | ||||||
|
|
||||||
| // run query only if | ||||||
| // - the access token is in the request header from the apollo client and | ||||||
| // - the base Name is not set | ||||||
| useEffect(() => { | ||||||
| if (user && !selectedBase?.name && !error) { | ||||||
| if (user && !selectedBase?.name && !localError) { | ||||||
| runOrganisationAndBasesQuery(); | ||||||
| } | ||||||
| }, [runOrganisationAndBasesQuery, user, selectedBase?.name, error]); | ||||||
| }, [runOrganisationAndBasesQuery, user, selectedBase?.name, localError]); | ||||||
|
|
||||||
| // setting auth atoms initially from auth0 | ||||||
| useEffect(() => { | ||||||
| if (!error && user && (user[JWT_AVAILABLE_BASES] || isGod)) { | ||||||
| // set available bases from auth0 id token only if they are not set yet. | ||||||
| // Otherwise, it would overwrite the names queried from the BE. | ||||||
| if (!availableBases.length && !isGod) { | ||||||
| setAvailableBases(user[JWT_AVAILABLE_BASES].map((id: string) => ({ id }))); | ||||||
| } | ||||||
|
|
||||||
| // extract the current/selected base ID from the URL, default to "0" until a valid base ID is set | ||||||
| const urlBaseIdInput = location.pathname.match(/\/bases\/(\d+)(\/)?/); | ||||||
| const urlBaseId = urlBaseIdInput?.length && urlBaseIdInput[1]; | ||||||
|
|
||||||
| // validate that | ||||||
| // - the selected base ID is part of the available base IDs from Auth0 or | ||||||
| // - that the user is a Boxtribute God | ||||||
| if (urlBaseId) { | ||||||
| if (isGod) { | ||||||
| setSelectedBase({ id: urlBaseId }); | ||||||
| } else if (user[JWT_AVAILABLE_BASES].map(String).includes(urlBaseId)) { | ||||||
| if (selectedBaseId !== urlBaseId) { | ||||||
| // only overwrite the selected base ID if the id is different from the existing one. | ||||||
| setSelectedBase({ id: urlBaseId }); | ||||||
| if (!isOrganisationAndBasesQueryLoading && organisationAndBaseData !== undefined) { | ||||||
| if (!localError && user && (user[JWT_AVAILABLE_BASES] || isGod)) { | ||||||
| const basesWithOrgData = organisationAndBaseData.bases; | ||||||
| const bases = basesWithOrgData.map((base) => ({ | ||||||
| id: base.id, | ||||||
| name: base.name, | ||||||
| })); | ||||||
| if (bases.length > 0) { | ||||||
| if (JSON.stringify(availableBases) !== JSON.stringify(bases)) { | ||||||
| setAvailableBases(bases); | ||||||
| } | ||||||
| // set available bases from auth0 id token only if they are not set yet. | ||||||
|
Comment on lines
+73
to
+77
|
||||||
| // Otherwise, it would overwrite the names queried from the BE. | ||||||
| // if (!availableBases.length && !isGod) { | ||||||
| // setAvailableBases(user[JWT_AVAILABLE_BASES].map((id: string) => ({ id }))); | ||||||
| // } | ||||||
|
|
||||||
| // extract the current/selected base ID from the URL, default to "0" until a valid base ID is set | ||||||
| const urlBaseIdInput = location.pathname.match(/\/bases\/(\d+)(\/)?/); | ||||||
| const urlBaseId = urlBaseIdInput?.length && urlBaseIdInput[1]; | ||||||
|
|
||||||
| // validate that | ||||||
| // - the selected base ID is part of the available base IDs from Auth0 or | ||||||
| // - that the user is a Boxtribute God | ||||||
| if (urlBaseId) { | ||||||
| if (isGod) { | ||||||
| // setSelectedBase({ id: urlBaseId }); | ||||||
| const matchingBase = basesWithOrgData.find((base) => base.id === urlBaseId); | ||||||
|
|
||||||
| if (matchingBase) { | ||||||
| // set selected base | ||||||
| setSelectedBase({ id: matchingBase.id, name: matchingBase.name }); | ||||||
| // set organisation for selected base | ||||||
| setOrganisation(matchingBase.organisation); | ||||||
| } | ||||||
| } else if (user[JWT_AVAILABLE_BASES].map(String).includes(urlBaseId)) { | ||||||
| // if (selectedBaseId !== urlBaseId) { | ||||||
| // only overwrite the selected base ID if the id is different from the existing one. | ||||||
| // setSelectedBase({ id: urlBaseId }); | ||||||
|
|
||||||
| const matchingBase = basesWithOrgData.find((base) => base.id === urlBaseId); | ||||||
|
|
||||||
| if (matchingBase) { | ||||||
| // set selected base | ||||||
| setSelectedBase({ id: matchingBase.id, name: matchingBase.name }); | ||||||
| // set organisation for selected base | ||||||
| setOrganisation(matchingBase.organisation); | ||||||
| } | ||||||
| // } | ||||||
| } | ||||||
| } | ||||||
| } else { | ||||||
| setError("The requested base is not available to you."); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| }, [ | ||||||
| availableBases.length, | ||||||
| error, | ||||||
| availableBases, | ||||||
| localError, | ||||||
| isGod, | ||||||
| isOrganisationAndBasesQueryLoading, | ||||||
| location.pathname, | ||||||
| organisationAndBaseData, | ||||||
| setAvailableBases, | ||||||
| setOrganisation, | ||||||
| setSelectedBase, | ||||||
| user, | ||||||
| isGod, | ||||||
| selectedBaseId, | ||||||
| ]); | ||||||
|
|
||||||
| // handle additional base information being returned from the query | ||||||
| useEffect(() => { | ||||||
| if (!isOrganisationAndBasesQueryLoading && organisationAndBaseData !== undefined) { | ||||||
| const finalError = useMemo(() => { | ||||||
| if (organisationAndBaseData) { | ||||||
| const basesWithOrgData = organisationAndBaseData.bases; | ||||||
| const bases = basesWithOrgData.map((base) => ({ | ||||||
| const bases = basesWithOrgData?.map((base) => ({ | ||||||
| id: base.id, | ||||||
| name: base.name, | ||||||
| })); | ||||||
|
|
||||||
| if (bases.length > 0) { | ||||||
| setAvailableBases(bases); | ||||||
| if (!bases || bases.length <= 0) { | ||||||
| return "There are no available bases."; | ||||||
| } else if (selectedBase?.id) { | ||||||
| const matchingBase = basesWithOrgData?.find((base) => base.id === selectedBase.id); | ||||||
|
|
||||||
| if (selectedBase?.id) { | ||||||
| const matchingBase = basesWithOrgData.find((base) => base.id === selectedBase.id); | ||||||
|
|
||||||
| if (matchingBase) { | ||||||
| // set selected base | ||||||
| setSelectedBase({ id: matchingBase.id, name: matchingBase.name }); | ||||||
| // set organisation for selected base | ||||||
| setOrganisation(matchingBase.organisation); | ||||||
| } else { | ||||||
| // this error is set if the requested base is not part of the available bases | ||||||
| setError("The requested base is not available to you."); | ||||||
| } | ||||||
| if (!matchingBase) { | ||||||
| return "The requested base is not available to you."; | ||||||
| } | ||||||
| } else { | ||||||
| // this error is set if the bases query returned an empty array for bases | ||||||
| setError("There are no available bases."); | ||||||
| } | ||||||
|
|
||||||
| return localError; | ||||||
| } else if (error) { | ||||||
| return "Failed getting information " + error.message; | ||||||
| } else if (!isOrganisationAndBasesQueryLoading && called) { | ||||||
| return "The requested base is not available to you"; | ||||||
|
||||||
| return "The requested base is not available to you"; | |
| return localError; |
Copilot
AI
May 1, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
finalError returns "The requested base is not available to you" whenever organisationAndBaseData is undefined and isOrganisationAndBasesQueryLoading is false. With useLazyQuery, that state also occurs before the query has ever been invoked, which can surface a misleading error during initial renders. Consider using the called flag from useLazyQuery (or an explicit "hasStartedQuery" ref/state) and only return this error when the query was actually executed and finished without data.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error rendering is currently gated behind
isInitialized; if global preferences fail to load (e.g., bases query errors) and selectedBaseId stays "0", the app returns nothing and never displays the error. Handleerrorbefore the early return (or render a loading/error state while !isInitialized) so failures don't result in a blank screen.