diff --git a/src/components/statics/error.tsx b/src/components/statics/error.tsx new file mode 100644 index 00000000..3f4012d7 --- /dev/null +++ b/src/components/statics/error.tsx @@ -0,0 +1,41 @@ +import React from "react"; +import { Container, Card, Button } from "react-bootstrap"; +import { useRouteError } from "react-router-dom"; + +interface ErrorPageProps { + onRetry?: () => void; +} + +const GenericErrorPage: React.FC = ({ onRetry }) => { + const error = useRouteError() as Error; + const title = "An Error Occurred"; + const message = "We are sorry, something went wrong."; + + return ( + + + + + {title} + {message} + {error && ( + + {error.message} + + )} + {onRetry && ( +
+ +
+ )} +
+
+
+ ); +}; + +export default GenericErrorPage; diff --git a/src/index.tsx b/src/index.tsx index 89f64ea3..20e9d6de 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,25 +1,16 @@ +import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import Main from "./pages/index"; -import { BrowserRouter } from "react-router-dom"; -import { StrictMode } from "react"; -import NiceModal from "@ebay/nice-modal-react"; -import RollbarMiddleware from "./components/middlewares/rollbar"; -import MapsMiddleware from "./components/middlewares/googlemap"; -import PostHogMiddleware from "./components/middlewares/posthog"; -const root = createRoot(document.getElementById("root") as HTMLElement); -root.render( - - - - - - -
- - - - - - -); +const rootElement = document.getElementById("root"); + +if (rootElement) { + const root = createRoot(rootElement); + root.render( + +
+ + ); +} else { + console.error("Root element not found"); +} diff --git a/src/pages/admin.tsx b/src/pages/admin.tsx index dea29ee1..405ea9ce 100644 --- a/src/pages/admin.tsx +++ b/src/pages/admin.tsx @@ -9,7 +9,6 @@ import { push, get, query, - DataSnapshot, orderByChild, equalTo } from "firebase/database"; @@ -38,8 +37,7 @@ import { Spinner, ButtonGroup } from "react-bootstrap"; -import { database, auth, functions } from "../firebase"; -import { httpsCallable } from "firebase/functions"; +import { database, auth } from "../firebase"; import { valuesDetails, floorDetails, @@ -61,13 +59,9 @@ import { useRollbar } from "@rollbar/react"; import { LinkSession, Policy } from "../utils/policies"; import AdminTable from "../components/table/admin"; import pollingQueryFunction from "../utils/helpers/pollingquery"; -import processAddressData from "../utils/helpers/processadddata"; -import processLinkDetails from "../utils/helpers/processlinkct"; import errorHandler from "../utils/helpers/errorhandler"; import ZeroPad from "../utils/helpers/zeropad"; import assignmentMessage from "../utils/helpers/assignmentmsg"; -import getMaxUnitLength from "../utils/helpers/maxunitlength"; -import SetPollerInterval from "../utils/helpers/pollinginterval"; import pollingVoidFunction from "../utils/helpers/pollingvoid"; import TerritoryListing from "../components/navigation/territorylist"; import UserListing from "../components/navigation/userlist"; @@ -95,7 +89,6 @@ import { DEFAULT_CONGREGATION_MAX_TRIES, DEFAULT_CONGREGATION_OPTION_IS_MULTIPLE, DEFAULT_MAP_DIRECTION_CONGREGATION_LOCATION, - CLOUD_FUNCTIONS_CALLS, DEFAULT_AGGREGATES } from "../utils/constants"; import ModalManager from "@ebay/nice-modal-react"; @@ -108,6 +101,11 @@ import setLink from "../utils/helpers/setlink"; import getTerritoryData from "../utils/helpers/getterritorydetails"; import { usePostHog } from "posthog-js/react"; import updateAddressDelta from "../utils/helpers/updateaddressdelta"; +import getAddressData from "../utils/helpers/getaddressdata"; +import getCongregationUsers from "../utils/helpers/getcongregationusers"; +import deleteTerritoryAddress from "../utils/helpers/deleteterritoryaddress"; +import deleteAddress from "../utils/helpers/deleteaddress"; +import deleteTerritoryData from "../utils/helpers/deleteterritory"; const UnauthorizedPage = SuspenseComponent( lazy(() => import("../components/statics/unauth")) @@ -238,32 +236,9 @@ function Admin({ user }: adminProps) { }; const getUsers = useCallback(async () => { - const getCongregationUsers = httpsCallable( - functions, - `${import.meta.env.VITE_SYSTEM_ENVIRONMENT}-${CLOUD_FUNCTIONS_CALLS.GET_CONGREGATION_USERS}` - ); try { setIsShowingUserListing(true); - const result = (await getCongregationUsers({ - congregation: code - // eslint-disable-next-line @typescript-eslint/no-explicit-any - })) as any; - if (Object.keys(result.data).length === 0) { - alert("There are no users to manage."); - return; - } - const userListing = new Map(); - for (const key in result.data) { - const data = result.data[key]; - userListing.set(key, { - uid: key, - name: data.name, - verified: data.verified, - email: data.email, - role: data.role - }); - } - setCongUsers(userListing); + setCongUsers(await getCongregationUsers(code)); toggleUserListing(); } catch (error) { errorHandler(error, rollbar); @@ -273,14 +248,13 @@ function Admin({ user }: adminProps) { }, [code]); const logoutUser = useCallback(async () => { - refreshAddressState(); posthog?.capture("logout", { email: user.email }); await signOut(auth); }, []); const processSelectedTerritory = async (selectedTerritoryCode: string) => { try { - const territoryAddsResult = await getTerritoryData( + const territoryAddresses = await getTerritoryData( code, selectedTerritoryCode ); @@ -288,84 +262,39 @@ function Admin({ user }: adminProps) { setSelectedTerritoryName(territories.get(selectedTerritoryCode)?.name); refreshAddressState(); unsubscribers.current = [] as Array; - - if (!territoryAddsResult) return; - const detailsListing = getDetailsListing(territoryAddsResult); - setSortedAddressList(detailsListing); - - const pollerId = SetPollerInterval(); - - await processAddressListing(detailsListing, pollerId); + setSortedAddressList(territoryAddresses); + await processAddressListing(territoryAddresses); } catch (error) { console.error("Error processing selected territory: ", error); errorHandler(error, rollbar); } }; - const getDetailsListing = (territoryAddsResult: DataSnapshot) => { - const detailsListing = [] as Array; - territoryAddsResult.forEach((addElement: DataSnapshot) => { - detailsListing.push(addElement.val()); - }); - return detailsListing; - }; - - const processAddressListing = async ( - addressCodeListing: Array, - pollerId: NodeJS.Timeout - ) => { - for (const addressCode of addressCodeListing) { - setAccordionKeys((existingKeys) => [...existingKeys, addressCode]); - unsubscribers.current.push( - onValue( - child(ref(database), `addresses/${code}/${addressCode}`), - async (snapshot) => { - clearInterval(pollerId); - if (snapshot.exists()) { - const addressData = await getAddressData( - code, - addressCode, - snapshot.val() - ); - setAddressData( - (existingAddresses) => - new Map( - existingAddresses.set(addressCode, addressData) - ) - ); + const processAddressListing = async (addressCodeListing: Array) => { + const processListenerData = async () => { + const listenerPromises = addressCodeListing.map((addressCode) => { + setAccordionKeys((existingKeys) => [...existingKeys, addressCode]); + return new Promise((resolve) => { + const unsubscribe = onValue( + child(ref(database), `addresses/${code}/${addressCode}`), + async (snapshot) => { + const data = await getAddressData(code, addressCode, snapshot); + if (data) { + setAddressData( + (existingAddresses) => + new Map(existingAddresses.set(addressCode, data)) + ); + } + resolve(); } - } - ) - ); - } - }; - - const getAddressData = async ( - code: string, - postalCode: string, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - postalSnapshot: any - ) => { - const floorData = await processAddressData( - code, - postalCode, - postalSnapshot.units - ); - const linkDetails = await processLinkDetails(code, postalCode); - return { - assigneeDetailsList: linkDetails.assigneeDetailsList, - personalDetailsList: linkDetails.personalDetailsList, - name: postalSnapshot.name, - postalCode: postalCode, - floors: floorData, - feedback: postalSnapshot.feedback, - type: postalSnapshot.type, - instructions: postalSnapshot.instructions, - location: postalSnapshot.location, - coordinates: postalSnapshot.coordinates, - aggregates: postalSnapshot.aggregates, - maxUnitLength: getMaxUnitLength(floorData) + ); + unsubscribers.current.push(unsubscribe); + }); + }); + await Promise.all(listenerPromises); }; + + await processListenerData(); }; const deleteBlockFloor = useCallback( @@ -387,20 +316,6 @@ function Admin({ user }: adminProps) { [code] ); - const getTerritoryAddress = useCallback( - async (territoryCode: string) => { - return await pollingQueryFunction(() => - get( - ref( - database, - `congregations/${code}/territories/${territoryCode}/addresses` - ) - ) - ); - }, - [code] - ); - const getAddressDetails = useCallback( async (postalcode: string) => { return await pollingQueryFunction(() => @@ -413,25 +328,7 @@ function Admin({ user }: adminProps) { const deleteTerritory = useCallback(async () => { if (!selectedTerritoryCode) return; try { - const addressesSnapshot = await getTerritoryAddress( - selectedTerritoryCode - ); - if (addressesSnapshot.exists()) { - const addressData = addressesSnapshot.val(); - for (const addkey in addressData) { - const postalcode = addressData[addkey]; - const postalPath = `addresses/${code}/${postalcode}`; - await pollingVoidFunction(() => remove(ref(database, postalPath))); - } - } - await pollingVoidFunction(() => - remove( - ref( - database, - `congregations/${code}/territories/${selectedTerritoryCode}` - ) - ) - ); + await deleteTerritoryData(code, selectedTerritoryCode); posthog?.capture("delete_territory", { territory: selectedTerritoryCode }); @@ -442,40 +339,12 @@ function Admin({ user }: adminProps) { } }, [selectedTerritoryCode, code]); - const deleteTerritoryAddress = useCallback( - async (territoryCode: string, postalCode: string) => { - const addressesSnapshot = await getTerritoryAddress(territoryCode); - if (addressesSnapshot.exists()) { - const addressData = addressesSnapshot.val(); - for (const addkey in addressData) { - const currentPostalcode = addressData[addkey]; - if (currentPostalcode === postalCode) { - await pollingVoidFunction(() => - remove( - ref( - database, - `congregations/${code}/territories/${selectedTerritoryCode}/addresses/${addkey}` - ) - ) - ); - posthog?.capture("delete_address", { - mapId: postalCode, - territory: territoryCode - }); - break; - } - } - } - }, - [code, selectedTerritoryCode] - ); - const deleteBlock = useCallback( async (postalCode: string, name: string, showAlert: boolean) => { if (!selectedTerritoryCode) return; try { - await remove(ref(database, `addresses/${code}/${postalCode}`)); - await deleteTerritoryAddress(selectedTerritoryCode, postalCode); + await deleteAddress(code, postalCode); + await deleteTerritoryAddress(code, selectedTerritoryCode, postalCode); posthog?.capture("delete_block", { mapId: postalCode }); @@ -534,15 +403,12 @@ function Admin({ user }: adminProps) { const resetTerritory = useCallback(async () => { if (!selectedTerritoryCode) return; try { - const addressesSnapshot = await getTerritoryAddress( + const addressesSnapshot = await getTerritoryData( + code, selectedTerritoryCode ); - if (addressesSnapshot.exists()) { - const addressData = addressesSnapshot.val(); - for (const addkey in addressData) { - const postalcode = addressData[addkey]; - await resetBlock(postalcode); - } + for (const postalcode of addressesSnapshot) { + await resetBlock(postalcode); } posthog?.capture("reset_territory", { territory: selectedTerritoryCode @@ -798,6 +664,7 @@ function Admin({ user }: adminProps) { ) ); await deleteTerritoryAddress( + code, selectedTerritoryCode as string, selectedPostalcode ); @@ -824,14 +691,12 @@ function Admin({ user }: adminProps) { const congregationCode = newCongCode as string; setCongregationCode(congregationCode); setCode(congregationCode); - refreshAddressState(); setName(""); setSelectedPostal(""); setSelectedTerritoryCode(""); setSelectedTerritoryName(""); toggleCongregationListing(); }, - // eslint-disable-next-line react-hooks/exhaustive-deps [showCongregationListing] ); @@ -904,20 +769,21 @@ function Admin({ user }: adminProps) { }; fetchData(); - }, []); - useEffect(() => { const handleScroll = () => { setShowBkTopButton(window.scrollY > PIXELS_TILL_BK_TO_TOP_BUTTON_DISPLAY); }; window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); + return () => { + console.log("Unsubscribing from admin data"); + window.removeEventListener("scroll", handleScroll); + refreshAddressState(); + }; }, []); useEffect(() => { if (!code) return; - refreshAddressState(); setUserAccessLevel(congregationAccess.current[code]); const congregationUnsub = onValue( @@ -977,7 +843,11 @@ function Admin({ user }: adminProps) { } ); - return () => congregationUnsub(); + return () => { + console.log("Unsubscribing from congregation data"); + congregationUnsub(); + refreshAddressState(); + }; }, [code]); if (isLoading) return ; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 0d05fb23..06b3525f 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,5 @@ -import { lazy, Suspense } from "react"; -import { Routes, Route } from "react-router-dom"; -// Load bootstrap first followed by your custom styles +import React, { lazy, Suspense, ReactNode } from "react"; +import { createBrowserRouter, RouterProvider } from "react-router-dom"; import "../App.scss"; import "../css/main.css"; import "../css/common.css"; @@ -9,27 +8,69 @@ import Loader from "../components/statics/loader"; import MaintenanceMiddleware from "../components/middlewares/maintenance"; import MainMiddleware from "../components/middlewares/main"; import StateMiddleware from "../components/middlewares/context"; +import MapsMiddleware from "../components/middlewares/googlemap"; +import PostHogMiddleware from "../components/middlewares/posthog"; +import RollbarMiddleware from "../components/middlewares/rollbar"; +import { Provider as NiceModelMiddleware } from "@ebay/nice-modal-react"; + const Map = lazy(() => import("./slip")); const NotFoundPage = lazy(() => import("../components/statics/notfound")); const FrontPage = lazy(() => import("./frontpage")); const UserManagement = lazy(() => import("./usrmgmt")); -function Main() { +const ErrorPage = lazy(() => import("../components/statics/error")); + +const router = createBrowserRouter([ + { + path: "/", + element: , + errorElement: + }, + { + path: "/:code/:id", + element: , + errorElement: + }, + { + path: "/usermgmt", + element: , + errorElement: + }, + { + path: "*", + element: + } +]); + +interface CombinedMiddlewareProps { + children: ReactNode; +} + +const CombinedMiddleware: React.FC = ({ + children +}) => ( + + + + + + + {children} + + + + + + +); + +const Main: React.FC = () => { return ( - - - - }> - - } /> - } /> - } /> - } /> - - - - - + + }> + + + ); -} +}; export default Main; diff --git a/src/pages/slip.tsx b/src/pages/slip.tsx index 0d224f29..f176ea53 100644 --- a/src/pages/slip.tsx +++ b/src/pages/slip.tsx @@ -21,10 +21,7 @@ import { TERRITORY_TYPES, USER_ACCESS_LEVELS, WIKI_CATEGORIES, - DEFAULT_CONGREGATION_OPTION_IS_MULTIPLE, - DEFAULT_MAP_DIRECTION_CONGREGATION_LOCATION, - DEFAULT_COORDINATES, - DEFAULT_CONGREGATION_MAX_TRIES + DEFAULT_COORDINATES } from "../utils/constants"; import "../css/slip.css"; import InfoImg from "../assets/information.svg?react"; @@ -39,8 +36,10 @@ import InvalidPage from "../components/statics/invalidpage"; import { useRollbar } from "@rollbar/react"; import SuspenseComponent from "../components/utils/suspense"; import { usePostHog } from "posthog-js/react"; -import { processOptions } from "../utils/helpers/getcongoptions"; -import getCongregationDetails from "../utils/helpers/getcongdetails"; +import { getOptions } from "../utils/helpers/getcongoptions"; +import getCongregationOrigin from "../utils/helpers/getcongorigin"; +import getOptionIsMultiSelect from "../utils/helpers/getoptionmultiselect"; +import getCongregationMaxTries from "../utils/helpers/getcongmaxtries"; const UpdateUnitStatus = lazy(() => import("../components/modal/updatestatus")); @@ -159,20 +158,11 @@ const Map = () => { useEffect(() => { const getMapData = async () => { if (!code || !postalcode) return; - const congregationDetails = await getCongregationDetails(code); - if (!congregationDetails.exists()) { - setIsLoading(false); - return; - } - const congregationData = congregationDetails.val(); - const options = processOptions(congregationData.options?.list); - const isMultiselect = - congregationData.options?.isMultiSelect || - DEFAULT_CONGREGATION_OPTION_IS_MULTIPLE; - const origin = - congregationData.origin || DEFAULT_MAP_DIRECTION_CONGREGATION_LOCATION; - const congregationMaxTries = - congregationData.maxTries || DEFAULT_CONGREGATION_MAX_TRIES; + const options = await getOptions(code); + const isMultiselect = await getOptionIsMultiSelect(code); + const origin = await getCongregationOrigin(code); + const congregationMaxTries = await getCongregationMaxTries(code); + await getMapListenerData(code, postalcode); setOptions(options); setPolicy( new Policy( @@ -183,37 +173,48 @@ const Map = () => { origin ) ); - onValue( - child(ref(database), `addresses/${code}/${postalcode}`), - async (snapshot) => { - try { - if (!snapshot.exists()) { - return; + setIsLoading(false); + }; + + const getMapListenerData = ( + congregationCode: string, + postalCode: string + ): Promise => { + return new Promise((resolve, reject) => { + onValue( + child(ref(database), `addresses/${congregationCode}/${postalCode}`), + async (snapshot) => { + try { + if (!snapshot.exists()) { + return; + } + const postalSnapshot = snapshot.val(); + setValues((values) => ({ + ...values, + feedback: postalSnapshot.feedback, + instructions: postalSnapshot.instructions + })); + setPostalName(postalSnapshot.name); + setTerritoryType(postalSnapshot.type); + setCoordinates( + postalSnapshot.coordinates || DEFAULT_COORDINATES.Singapore + ); + setAggregate(postalSnapshot.aggregates); + const data = await processAddressData( + congregationCode, + postalCode, + postalSnapshot.units + ); + setFloors(data); + document.title = postalSnapshot.name; + } catch (error) { + reject(error); + } finally { + resolve(); } - const postalSnapshot = snapshot.val(); - setValues((values) => ({ - ...values, - feedback: postalSnapshot.feedback, - instructions: postalSnapshot.instructions - })); - setPostalName(postalSnapshot.name); - setTerritoryType(postalSnapshot.type); - setCoordinates( - postalSnapshot.coordinates || DEFAULT_COORDINATES.Singapore - ); - setAggregate(postalSnapshot.aggregates); - const data = await processAddressData( - code, - postalcode, - postalSnapshot.units - ); - setFloors(data); - document.title = postalSnapshot.name; - } finally { - setIsLoading(false); } - } - ); + ); + }); }; getMapData(); diff --git a/src/utils/helpers/deleteaddress.ts b/src/utils/helpers/deleteaddress.ts new file mode 100644 index 00000000..e0592813 --- /dev/null +++ b/src/utils/helpers/deleteaddress.ts @@ -0,0 +1,11 @@ +import { ref, remove } from "firebase/database"; +import pollingVoidFunction from "./pollingvoid"; +import { database } from "../../firebase"; + +const deleteAddress = async (congregationCode: string, addressCode: string) => { + await pollingVoidFunction(() => + remove(ref(database, `addresses/${congregationCode}/${addressCode}`)) + ); +}; + +export default deleteAddress; diff --git a/src/utils/helpers/deleteterritory.ts b/src/utils/helpers/deleteterritory.ts new file mode 100644 index 00000000..b955b8be --- /dev/null +++ b/src/utils/helpers/deleteterritory.ts @@ -0,0 +1,30 @@ +import { remove, ref } from "firebase/database"; +import { database } from "../../firebase"; +import deleteAddress from "./deleteaddress"; +import getTerritoryData from "./getterritorydetails"; +import pollingVoidFunction from "./pollingvoid"; + +const deleteTerritoryData = async ( + congregationCode: string, + selectedTerritoryCode: string +) => { + if (!selectedTerritoryCode) return; + + const addressesSnapshot = await getTerritoryData( + congregationCode, + selectedTerritoryCode + ); + for (const postalcode of addressesSnapshot) { + await deleteAddress(congregationCode, postalcode); + } + await pollingVoidFunction(() => + remove( + ref( + database, + `congregations/${congregationCode}/territories/${selectedTerritoryCode}` + ) + ) + ); +}; + +export default deleteTerritoryData; diff --git a/src/utils/helpers/deleteterritoryaddress.ts b/src/utils/helpers/deleteterritoryaddress.ts new file mode 100644 index 00000000..368db95f --- /dev/null +++ b/src/utils/helpers/deleteterritoryaddress.ts @@ -0,0 +1,43 @@ +import { + ref, + query, + orderByValue, + equalTo, + limitToFirst, + get, + remove +} from "firebase/database"; +import pollingQueryFunction from "./pollingquery"; +import pollingVoidFunction from "./pollingvoid"; +import { database } from "../../firebase"; + +const deleteTerritoryAddress = async ( + congregationCode: string, + territoryCode: string, + addressCode: string +) => { + const addressesRef = ref( + database, + `congregations/${congregationCode}/territories/${territoryCode}/addresses` + ); + const addressCodeQuery = query( + addressesRef, + orderByValue(), + equalTo(addressCode), + limitToFirst(1) + ); + + const snapshot = await pollingQueryFunction(() => get(addressCodeQuery)); + if (!snapshot.exists()) return; + const addressKey = Object.keys(snapshot.val())[0]; + await pollingVoidFunction(() => + remove( + ref( + database, + `congregations/${congregationCode}/territories/${territoryCode}/addresses/${addressKey}` + ) + ) + ); +}; + +export default deleteTerritoryAddress; diff --git a/src/utils/helpers/getaddressdata.ts b/src/utils/helpers/getaddressdata.ts new file mode 100644 index 00000000..374033e9 --- /dev/null +++ b/src/utils/helpers/getaddressdata.ts @@ -0,0 +1,38 @@ +import processAddressData from "./processadddata"; +import processLinkDetails from "./processlinkct"; +import getMaxUnitLength from "./maxunitlength"; +import { DataSnapshot } from "firebase/database"; + +const getAddressData = async ( + code: string, + postalCode: string, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + snapShot: DataSnapshot +) => { + if (!snapShot.exists()) { + return null; + } + const postalSnapshot = snapShot.val(); + const floorData = await processAddressData( + code, + postalCode, + postalSnapshot.units + ); + const linkDetails = await processLinkDetails(code, postalCode); + return { + assigneeDetailsList: linkDetails.assigneeDetailsList, + personalDetailsList: linkDetails.personalDetailsList, + name: postalSnapshot.name, + postalCode: postalCode, + floors: floorData, + feedback: postalSnapshot.feedback, + type: postalSnapshot.type, + instructions: postalSnapshot.instructions, + location: postalSnapshot.location, + coordinates: postalSnapshot.coordinates, + aggregates: postalSnapshot.aggregates, + maxUnitLength: getMaxUnitLength(floorData) + }; +}; + +export default getAddressData; diff --git a/src/utils/helpers/getcongdetails.ts b/src/utils/helpers/getcongdetails.ts deleted file mode 100644 index 5c734e93..00000000 --- a/src/utils/helpers/getcongdetails.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { get, child, ref } from "firebase/database"; -import { database } from "../../firebase"; -import pollingQueryFunction from "./pollingquery"; - -const getCongregationDetails = async (congregationCode: string) => { - return await pollingQueryFunction(() => - get(child(ref(database), `congregations/${congregationCode}`)) - ); -}; - -export default getCongregationDetails; diff --git a/src/utils/helpers/getcongmaxtries.ts b/src/utils/helpers/getcongmaxtries.ts new file mode 100644 index 00000000..bbaea07f --- /dev/null +++ b/src/utils/helpers/getcongmaxtries.ts @@ -0,0 +1,13 @@ +import { get, child, ref } from "firebase/database"; +import { database } from "../../firebase"; +import pollingQueryFunction from "./pollingquery"; +import { DEFAULT_CONGREGATION_MAX_TRIES } from "../constants"; + +const getCongregationMaxTries = async (code: string) => { + const data = await pollingQueryFunction(() => + get(child(ref(database), `congregations/${code}/maxTries`)) + ); + return data.exists() ? data.val() : DEFAULT_CONGREGATION_MAX_TRIES; +}; + +export default getCongregationMaxTries; diff --git a/src/utils/helpers/getcongorigin.ts b/src/utils/helpers/getcongorigin.ts index 47118be4..6d74ce55 100644 --- a/src/utils/helpers/getcongorigin.ts +++ b/src/utils/helpers/getcongorigin.ts @@ -1,11 +1,15 @@ import { get, child, ref } from "firebase/database"; import { database } from "../../firebase"; import pollingQueryFunction from "./pollingquery"; +import { DEFAULT_MAP_DIRECTION_CONGREGATION_LOCATION } from "../constants"; const getCongregationOrigin = async (code: string) => { - return await pollingQueryFunction(() => + const data = await pollingQueryFunction(() => get(child(ref(database), `congregations/${code}/origin`)) ); + return data.exists() + ? data.val() + : DEFAULT_MAP_DIRECTION_CONGREGATION_LOCATION; }; export default getCongregationOrigin; diff --git a/src/utils/helpers/getcongregationusers.ts b/src/utils/helpers/getcongregationusers.ts new file mode 100644 index 00000000..e698b226 --- /dev/null +++ b/src/utils/helpers/getcongregationusers.ts @@ -0,0 +1,38 @@ +import { httpsCallable } from "firebase/functions"; +import { functions } from "../../firebase"; +import { CLOUD_FUNCTIONS_CALLS } from "../constants"; +import { userDetails } from "../interface"; + +const getCongregationUsers = async ( + code: string +): Promise> => { + const getCongregationUsersCallable = httpsCallable( + functions, + `${import.meta.env.VITE_SYSTEM_ENVIRONMENT}-${CLOUD_FUNCTIONS_CALLS.GET_CONGREGATION_USERS}` + ); + + const result = (await getCongregationUsersCallable({ + congregation: code + // eslint-disable-next-line @typescript-eslint/no-explicit-any + })) as any; + + if (Object.keys(result.data).length === 0) { + throw new Error("There are no users to manage."); + } + + const userListing = new Map(); + for (const key in result.data) { + const data = result.data[key]; + userListing.set(key, { + uid: key, + name: data.name, + verified: data.verified, + email: data.email, + role: data.role + }); + } + + return userListing; +}; + +export default getCongregationUsers; diff --git a/src/utils/helpers/getoptionmultiselect.ts b/src/utils/helpers/getoptionmultiselect.ts index 76d368aa..af6c8460 100644 --- a/src/utils/helpers/getoptionmultiselect.ts +++ b/src/utils/helpers/getoptionmultiselect.ts @@ -1,11 +1,13 @@ import { get, ref, query } from "firebase/database"; import { database } from "../../firebase"; import pollingQueryFunction from "./pollingquery"; +import { DEFAULT_CONGREGATION_OPTION_IS_MULTIPLE } from "../constants"; const getOptionIsMultiSelect = async (code: string) => { - return await pollingQueryFunction(() => + const data = await pollingQueryFunction(() => get(query(ref(database, `congregations/${code}/options/isMultiSelect`))) ); + return data.exists() ? data.val() : DEFAULT_CONGREGATION_OPTION_IS_MULTIPLE; }; export default getOptionIsMultiSelect; diff --git a/src/utils/helpers/getterritorydetails.ts b/src/utils/helpers/getterritorydetails.ts index 10e251fa..4ed1e647 100644 --- a/src/utils/helpers/getterritorydetails.ts +++ b/src/utils/helpers/getterritorydetails.ts @@ -3,14 +3,15 @@ import { database } from "../../firebase"; import pollingQueryFunction from "./pollingquery"; const getTerritoryData = async ( - code: string, + congregationCode: string, selectedTerritoryCode: string ) => { + const detailsListing = [] as Array; try { const territoryRef = query( ref( database, - `congregations/${code}/territories/${selectedTerritoryCode}/addresses` + `congregations/${congregationCode}/territories/${selectedTerritoryCode}/addresses` ), orderByValue() ); @@ -19,11 +20,17 @@ const getTerritoryData = async ( get(territoryRef) ); - return territoryAddsResult; + if (!territoryAddsResult.exists()) { + return detailsListing; + } + + territoryAddsResult.forEach((address) => { + detailsListing.push(address.val()); + }); } catch (error) { console.error("Error fetching territory data:", error); - return; } + return detailsListing; }; export default getTerritoryData;