From ef134a3154c7196413ed719487c63b10a862b051 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Fri, 21 Nov 2025 18:25:09 -0500 Subject: [PATCH 01/11] fixed onboarding problem --- .../overview/BusinessCard.tsx | 6 +- .../overview/BusinessInfoEditor.tsx | 181 +++++++++--------- frontend/app/signup/onboarding.tsx | 8 +- 3 files changed, 98 insertions(+), 97 deletions(-) diff --git a/frontend/app/business-profile/overview/BusinessCard.tsx b/frontend/app/business-profile/overview/BusinessCard.tsx index 68c3371b..c25827cf 100644 --- a/frontend/app/business-profile/overview/BusinessCard.tsx +++ b/frontend/app/business-profile/overview/BusinessCard.tsx @@ -23,7 +23,7 @@ export default function BusinessCard() { const [error, setError] = useState(null); const [editing, setEditing] = useState(false); - const { mutate: updateBusinessMutate } = useMutation({ + const { mutate: updateBusinessMutate} = useMutation({ mutationFn: (businessInfo: UpdateCompanyRequest) => updateCompany(businessInfo), onError: (error: Error) => { setError(error.message); @@ -32,7 +32,9 @@ export default function BusinessCard() { const handleSave = () => { updateBusinessMutate(businessInfo); - setEditing(false); + if(!error) { + setEditing(false); + } }; const { data: businessQuery, isPending: businessPending } = useQuery({ diff --git a/frontend/app/business-profile/overview/BusinessInfoEditor.tsx b/frontend/app/business-profile/overview/BusinessInfoEditor.tsx index 62ceee4e..a4e51797 100644 --- a/frontend/app/business-profile/overview/BusinessInfoEditor.tsx +++ b/frontend/app/business-profile/overview/BusinessInfoEditor.tsx @@ -61,97 +61,98 @@ export default function CompanyEditor({
{isExpanded ? (
-
-
- - setCompany({ ...company, name: e.target.value })} - /> -
-
- - setCompany({ ...company, name: e.target.value })} + /> +
+
+ + -
-
-
-
- - setUser({ ...user, phoneNumber: e.target.value })} - /> -
-
- - setUser({ ...user, email: e.target.value })} - /> -
-
- - setCompany({ ...company, alternateEmail: e.target.value })} - /> -
+ + + + + {businessTypes.map((type) => ( + + {type} + + ))} + + +
+ + +
+ + setUser({ ...user, phoneNumber: e.target.value })} + /> +
+
+ + setUser({ ...user, email: e.target.value })} + /> +
+
+ + setCompany({ ...company, alternateEmail: e.target.value })} + /> +
{error || saveError ? (

{error || saveError}

@@ -159,7 +160,7 @@ export default function CompanyEditor({ "" )} )} - + + ); +} + +export function LoadingInsuranceCard() { + return ( +
+
+
); } From 7941c40e20f0420e2b766f152275ea36a8ae4401 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 00:55:42 -0500 Subject: [PATCH 03/11] finished loading states --- .../overview/BusinessCard.tsx | 22 +-- .../overview/InsuranceCard.tsx | 12 +- .../overview/LocationsCard.tsx | 55 ++++---- .../expense-table/table-content.tsx | 32 +---- .../dashboard/RevenueAndExpenses.tsx | 127 +++++++++--------- frontend/components/loading.tsx | 11 ++ frontend/components/table/index.tsx | 35 +++-- 7 files changed, 141 insertions(+), 153 deletions(-) create mode 100644 frontend/components/loading.tsx diff --git a/frontend/app/business-profile/overview/BusinessCard.tsx b/frontend/app/business-profile/overview/BusinessCard.tsx index 1046637a..20c39ec4 100644 --- a/frontend/app/business-profile/overview/BusinessCard.tsx +++ b/frontend/app/business-profile/overview/BusinessCard.tsx @@ -7,6 +7,7 @@ import { useEffect, useState } from "react"; import CompanyEditor from "./BusinessInfoEditor"; import { getUser } from "@/api/user"; import { Card } from "@/components/ui/card"; +import Loading from "@/components/loading"; export default function BusinessCard() { const [businessInfo, setBusinessInfo] = useState({ @@ -63,7 +64,12 @@ export default function BusinessCard() { return (
{businessPending ? ( - + +
+

Business Information

+
+ +
) : (
); } - -export function LoadingBusinessProfile() { - return ( - -
-

Business Information

-
-
-
-
-
-
- ); -} diff --git a/frontend/app/business-profile/overview/InsuranceCard.tsx b/frontend/app/business-profile/overview/InsuranceCard.tsx index 25621777..fbf0a606 100644 --- a/frontend/app/business-profile/overview/InsuranceCard.tsx +++ b/frontend/app/business-profile/overview/InsuranceCard.tsx @@ -2,6 +2,7 @@ import { createInsurancePolicy, getInsurancePolicies, updateInsurancePolicy } from "@/api/insurance"; import InsuranceEditor from "@/components/InsuranceEditor"; +import Loading from "@/components/loading"; import { Button } from "@/components/ui/button"; import { CreateInsurancePolicyRequest, UpdateInsurancePolicyRequest } from "@/types/insurance-policy"; import { useMutation, useQuery } from "@tanstack/react-query"; @@ -86,7 +87,7 @@ export default function InsuranceCard() { return (
{insurancePending ? ( - + ) : (
@@ -120,12 +121,3 @@ export default function InsuranceCard() {
); } - -export function LoadingInsuranceCard() { - return ( -
-
-
-
- ); -} diff --git a/frontend/app/business-profile/overview/LocationsCard.tsx b/frontend/app/business-profile/overview/LocationsCard.tsx index d167f5e9..bbf85ce5 100644 --- a/frontend/app/business-profile/overview/LocationsCard.tsx +++ b/frontend/app/business-profile/overview/LocationsCard.tsx @@ -3,8 +3,8 @@ import { getCompanyLocations } from "@/api/company"; import { createLocation, updateLocationAddress } from "@/api/location"; import LocationEditor from "@/components/LocationEditor"; +import Loading from "@/components/loading"; import { Button } from "@/components/ui/button"; -import { Spinner } from "@/components/ui/spinner"; import { CreateLocationRequest, UpdateLocationRequest } from "@/types/location"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useState, useEffect } from "react"; @@ -94,34 +94,37 @@ export default function LocationsCard() { return (
{businessPending ? ( - + ) : ( -
- {locationInfo.map((location, index) => ( -
- updateLocation(index, loc)} - removeLocation={() => removeLocation(index)} - isExpanded={editingLocationIndex === index} - onExpand={() => - editingLocationIndex === index - ? setEditingLocationIndex(null) - : setEditingLocationIndex(index) - } - onCollapse={() => handleSave()} - saveError={saveError} - /> -
- ))} +
+
+ {locationInfo.map((location, index) => ( +
+ updateLocation(index, loc)} + removeLocation={() => removeLocation(index)} + isExpanded={editingLocationIndex === index} + onExpand={() => + editingLocationIndex === index + ? setEditingLocationIndex(null) + : setEditingLocationIndex(index) + } + onCollapse={() => handleSave()} + saveError={saveError} + /> +
+ ))} +
+ +
)} -
); } diff --git a/frontend/app/expense-tracker/expense-table/table-content.tsx b/frontend/app/expense-tracker/expense-table/table-content.tsx index 26fcedad..7f279269 100644 --- a/frontend/app/expense-tracker/expense-table/table-content.tsx +++ b/frontend/app/expense-tracker/expense-table/table-content.tsx @@ -108,7 +108,6 @@ export default function TableContent({ ) : null ) : ( { e.stopPropagation(); @@ -122,33 +121,8 @@ export default function TableContent({ }, { id: "description", - header: () => "Description", + header: "Description", accessorFn: (row) => row.description, - cell: ({ row, cell }) => { - const cellVal = cell.getValue(); - const displayMerchant = cellVal.length > 20 ? `${cellVal.substring(0, 20)}...` : cellVal; - return ( -
0 && "pl-8")}> - {rowOption === "collapsible" ? ( - row.getCanExpand() ? ( - row.toggleExpanded()} - isOpen={row.getIsExpanded()} - /> - ) : null - ) : ( - { - e.stopPropagation(); - }} - /> - )} - {displayMerchant.length > 0 ? displayMerchant : ""} -
- ); - }, }, { id: "amount", @@ -203,9 +177,7 @@ export default function TableContent({ ], }); - if (purchases.isPending) return
Loading expenses...
; - if (purchases.error) return
Error loading expenses
; - return ; + return
; } diff --git a/frontend/components/dashboard/RevenueAndExpenses.tsx b/frontend/components/dashboard/RevenueAndExpenses.tsx index b3b6ad56..ba4c3202 100644 --- a/frontend/components/dashboard/RevenueAndExpenses.tsx +++ b/frontend/components/dashboard/RevenueAndExpenses.tsx @@ -8,8 +8,8 @@ import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; import { ChartContainer, ChartTooltipContent, ChartTooltip, ChartConfig } from "@/components/ui/chart"; import Link from "next/link"; import { FaCircle } from "react-icons/fa"; +import Loading from "../loading"; -// No Data Component export function RevenueAndExpensesNoData() { return ( @@ -110,83 +110,88 @@ export default function RevenueAndExpenses() { return ( - {/* Header with title and percentage change */}
Revenue and Expenses -
-
= 0 ? "bg-seafoam" : "bg-pink"}`} - > - = 0 ? "text-teal" : "text-fuchsia"}`}> - {percentChange >= 0 ? "+" : ""} - {percentChange.toFixed(2)}% - + {!(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) && ( +
+
= 0 ? "bg-seafoam" : "bg-pink"}`} + > + = 0 ? "text-teal" : "text-fuchsia"}`}> + {percentChange >= 0 ? "+" : ""} + {percentChange.toFixed(2)}% + +
+ revenue since last month
- revenue since last month -
+ )}
- - {/* Left side - Stats */} -
-
-
-
-
- + {expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading) ? ( + + ) : ( + +
+
+
+
+
+ +
+ Total Revenue this Month +
+
+ ${(revenueQueries[0].data?.total ?? 0) / 100.0}
- Total Revenue this Month
-
${(revenueQueries[0].data?.total ?? 0) / 100.0}
-
-
-
-
- +
+
+
+ +
+ Total Expenses this Month +
+
+ ${(expensesQueries[0].data?.total ?? 0) / 100.0}
- Total Expenses this Month
-
${(expensesQueries[0].data?.total ?? 0) / 100.0}
-
- - - -
+ + + +
- {/* Right side - Chart with legend */} -
- {/* Legend */} -
-
-
- Revenues -
-
-
- Expenses +
+
+
+
+ Revenues +
+
+
+ Expenses +
-
- {/* Chart */} -
- - - - - } /> - - - - +
+ + + + + } /> + + + + +
-
- + + )} ); } diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx new file mode 100644 index 00000000..f900bbae --- /dev/null +++ b/frontend/components/loading.tsx @@ -0,0 +1,11 @@ +export default function Loading({ lines = 6 }) { + const widths = ["w-full", "w-5/6", "w-4/6"]; + return ( +
+ {Array.from({ length: lines }).map((_, index) => { + const width = widths[index % widths.length]; + return
; + })} +
+ ); +} diff --git a/frontend/components/table/index.tsx b/frontend/components/table/index.tsx index f6d01b48..2b06d36a 100644 --- a/frontend/components/table/index.tsx +++ b/frontend/components/table/index.tsx @@ -1,7 +1,8 @@ import { flexRender, Table as ReactTable } from "@tanstack/react-table"; import { Table as CTable, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"; +import { Spinner } from "../ui/spinner"; -export function Table({ table }: { table: ReactTable }) { +export function Table({ table, isLoading }: { table: ReactTable; isLoading: boolean }) { return ( @@ -17,17 +18,29 @@ export function Table({ table }: { table: ReactTable }) { ))} - - {table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} + {!isLoading ? ( + + + +
+ +
+
- ))} -
+
+ ) : ( + + {table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ))} + + )}
); } From 058d3814d8186e8b1637213c13e94e4b34713b69 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 01:35:19 -0500 Subject: [PATCH 04/11] fixed small dashboard styling mistakes --- frontend/components/dashboard/DisasterStatusBanner.tsx | 6 +++--- frontend/components/dashboard/NextSteps.tsx | 6 +++--- frontend/components/dashboard/RevenueAndExpenses.tsx | 9 ++++++--- frontend/components/loading.tsx | 2 +- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/components/dashboard/DisasterStatusBanner.tsx b/frontend/components/dashboard/DisasterStatusBanner.tsx index ee950182..34ea64de 100644 --- a/frontend/components/dashboard/DisasterStatusBanner.tsx +++ b/frontend/components/dashboard/DisasterStatusBanner.tsx @@ -19,7 +19,7 @@ export default function DisasterStatusBanner({ bannerData }: Props) { There are currently no disasters affecting your business. - + File a claim report @@ -55,7 +55,7 @@ export default function DisasterStatusBanner({ bannerData }: Props) { rel="noopener noreferrer" className="self-end" > - + Register on FEMA's website {/* Escaped quote character */} @@ -91,7 +91,7 @@ export default function DisasterStatusBanner({ bannerData }: Props) { />
- + Continue filing claim report diff --git a/frontend/components/dashboard/NextSteps.tsx b/frontend/components/dashboard/NextSteps.tsx index ae028b74..7f14ccef 100644 --- a/frontend/components/dashboard/NextSteps.tsx +++ b/frontend/components/dashboard/NextSteps.tsx @@ -26,8 +26,8 @@ const NextStepItem = ({ }) => (
  • -
    - +
    +

    {title}

    @@ -90,7 +90,7 @@ export default function NextSteps({ bannerData }: Props) { } return ( - + Next Steps

    {headerText}

    diff --git a/frontend/components/dashboard/RevenueAndExpenses.tsx b/frontend/components/dashboard/RevenueAndExpenses.tsx index ba4c3202..79866b04 100644 --- a/frontend/components/dashboard/RevenueAndExpenses.tsx +++ b/frontend/components/dashboard/RevenueAndExpenses.tsx @@ -9,6 +9,7 @@ import { ChartContainer, ChartTooltipContent, ChartTooltip, ChartConfig } from " import Link from "next/link"; import { FaCircle } from "react-icons/fa"; import Loading from "../loading"; +import { Spinner } from "../ui/spinner"; export function RevenueAndExpensesNoData() { return ( @@ -109,7 +110,7 @@ export default function RevenueAndExpenses() { } satisfies ChartConfig; return ( - +
    Revenue and Expenses @@ -128,8 +129,10 @@ export default function RevenueAndExpenses() { )}
    - {expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading) ? ( - + {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( +
    + +
    ) : (
    diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx index f900bbae..26a78fb7 100644 --- a/frontend/components/loading.tsx +++ b/frontend/components/loading.tsx @@ -4,7 +4,7 @@ export default function Loading({ lines = 6 }) {
    {Array.from({ length: lines }).map((_, index) => { const width = widths[index % widths.length]; - return
    ; + return
    ; })}
    ); From 3530fd596a5829812099895c76242f68bba16629 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 14:33:14 -0500 Subject: [PATCH 05/11] fixed more loading --- .../overview/BusinessCard.tsx | 2 +- .../overview/BusinessInfoEditor.tsx | 2 +- .../overview/InsuranceCard.tsx | 6 +++-- .../overview/LocationsCard.tsx | 8 +++--- .../business-profile/overview/Overview.tsx | 12 ++------- frontend/components/dashboard/NextSteps.tsx | 2 +- .../dashboard/RevenueAndExpenses.tsx | 25 +++++++++++++------ frontend/components/loading.tsx | 2 +- frontend/components/table/index.tsx | 8 +++--- 9 files changed, 37 insertions(+), 30 deletions(-) diff --git a/frontend/app/business-profile/overview/BusinessCard.tsx b/frontend/app/business-profile/overview/BusinessCard.tsx index 20c39ec4..d54b0c02 100644 --- a/frontend/app/business-profile/overview/BusinessCard.tsx +++ b/frontend/app/business-profile/overview/BusinessCard.tsx @@ -64,7 +64,7 @@ export default function BusinessCard() { return (
    {businessPending ? ( - +

    Business Information

    diff --git a/frontend/app/business-profile/overview/BusinessInfoEditor.tsx b/frontend/app/business-profile/overview/BusinessInfoEditor.tsx index 34aa7769..95af1a21 100644 --- a/frontend/app/business-profile/overview/BusinessInfoEditor.tsx +++ b/frontend/app/business-profile/overview/BusinessInfoEditor.tsx @@ -43,7 +43,7 @@ export default function CompanyEditor({ }; return ( - +

    Business Information

    diff --git a/frontend/app/business-profile/overview/InsuranceCard.tsx b/frontend/app/business-profile/overview/InsuranceCard.tsx index fbf0a606..35944000 100644 --- a/frontend/app/business-profile/overview/InsuranceCard.tsx +++ b/frontend/app/business-profile/overview/InsuranceCard.tsx @@ -4,6 +4,7 @@ import { createInsurancePolicy, getInsurancePolicies, updateInsurancePolicy } fr import InsuranceEditor from "@/components/InsuranceEditor"; import Loading from "@/components/loading"; import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; import { CreateInsurancePolicyRequest, UpdateInsurancePolicyRequest } from "@/types/insurance-policy"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useEffect, useState } from "react"; @@ -85,7 +86,8 @@ export default function InsuranceCard() { }, [insuranceQuery]); return ( -
    + +

    Insurance Information

    {insurancePending ? ( ) : ( @@ -118,6 +120,6 @@ export default function InsuranceCard() {
    )} -
    + ); } diff --git a/frontend/app/business-profile/overview/LocationsCard.tsx b/frontend/app/business-profile/overview/LocationsCard.tsx index bbf85ce5..2a10afef 100644 --- a/frontend/app/business-profile/overview/LocationsCard.tsx +++ b/frontend/app/business-profile/overview/LocationsCard.tsx @@ -5,6 +5,7 @@ import { createLocation, updateLocationAddress } from "@/api/location"; import LocationEditor from "@/components/LocationEditor"; import Loading from "@/components/loading"; import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; import { CreateLocationRequest, UpdateLocationRequest } from "@/types/location"; import { useMutation, useQuery } from "@tanstack/react-query"; import { useState, useEffect } from "react"; @@ -92,9 +93,10 @@ export default function LocationsCard() { }, [locationsQuery]); return ( -
    + +

    Locations

    {businessPending ? ( - + ) : (
    @@ -125,6 +127,6 @@ export default function LocationsCard() {
    )} -
    +
    ); } diff --git a/frontend/app/business-profile/overview/Overview.tsx b/frontend/app/business-profile/overview/Overview.tsx index 39cd8f9d..3cc3a4f3 100644 --- a/frontend/app/business-profile/overview/Overview.tsx +++ b/frontend/app/business-profile/overview/Overview.tsx @@ -1,6 +1,4 @@ "use client"; - -import { Card } from "@/components/ui/card"; import BusinessCard from "./BusinessCard"; import LocationsCard from "./LocationsCard"; import InsuranceCard from "./InsuranceCard"; @@ -9,14 +7,8 @@ export default function Overview() { return (
    - -

    Locations

    - -
    - -

    Insurance Information

    - -
    + +
    ); } diff --git a/frontend/components/dashboard/NextSteps.tsx b/frontend/components/dashboard/NextSteps.tsx index 7f14ccef..1a2d37a6 100644 --- a/frontend/components/dashboard/NextSteps.tsx +++ b/frontend/components/dashboard/NextSteps.tsx @@ -27,7 +27,7 @@ const NextStepItem = ({
  • - +

    {title}

    diff --git a/frontend/components/dashboard/RevenueAndExpenses.tsx b/frontend/components/dashboard/RevenueAndExpenses.tsx index 79866b04..09068b8c 100644 --- a/frontend/components/dashboard/RevenueAndExpenses.tsx +++ b/frontend/components/dashboard/RevenueAndExpenses.tsx @@ -8,8 +8,6 @@ import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; import { ChartContainer, ChartTooltipContent, ChartTooltip, ChartConfig } from "@/components/ui/chart"; import Link from "next/link"; import { FaCircle } from "react-icons/fa"; -import Loading from "../loading"; -import { Spinner } from "../ui/spinner"; export function RevenueAndExpensesNoData() { return ( @@ -114,7 +112,7 @@ export default function RevenueAndExpenses() {
    Revenue and Expenses - {!(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) && ( + {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) && (
    = 0 ? "bg-seafoam" : "bg-pink"}`} @@ -129,10 +127,23 @@ export default function RevenueAndExpenses() { )}
    - {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( -
    - -
    + {!(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( + +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    +
    ) : (
    diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx index 26a78fb7..deb3c001 100644 --- a/frontend/components/loading.tsx +++ b/frontend/components/loading.tsx @@ -4,7 +4,7 @@ export default function Loading({ lines = 6 }) {
    {Array.from({ length: lines }).map((_, index) => { const width = widths[index % widths.length]; - return
    ; + return
    ; })}
    ); diff --git a/frontend/components/table/index.tsx b/frontend/components/table/index.tsx index 2b06d36a..89ca198f 100644 --- a/frontend/components/table/index.tsx +++ b/frontend/components/table/index.tsx @@ -18,13 +18,13 @@ export function Table({ table, isLoading }: { table: ReactTable; isLoading ))} - {!isLoading ? ( + {isLoading ? ( -
    - -
    +
    + +
    From 388a9bbcbfaf868db87f2827d99f4f2a3184b5f0 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 20:33:32 -0500 Subject: [PATCH 06/11] fixed more styling --- .../view-documents/DocumentTable.tsx | 30 +++++----- .../LocationBasedRiskCard.tsx | 3 +- .../components/dashboard/BusinessRisk.tsx | 2 +- .../components/dashboard/LocationRisk.tsx | 9 ++- .../dashboard/RevenueAndExpenses.tsx | 56 ++++++++----------- frontend/components/loading.tsx | 19 +++++++ 6 files changed, 67 insertions(+), 52 deletions(-) diff --git a/frontend/app/business-profile/view-documents/DocumentTable.tsx b/frontend/app/business-profile/view-documents/DocumentTable.tsx index e6b3f552..661e5cba 100644 --- a/frontend/app/business-profile/view-documents/DocumentTable.tsx +++ b/frontend/app/business-profile/view-documents/DocumentTable.tsx @@ -49,20 +49,22 @@ export default function DocumentTable({
  • - Title - File Type - Category - -
    - {dateSort === "asc" ? ( - - ) : ( - - )} - Date -
    -
    - + + Title + File Type + Category + +
    + {dateSort === "asc" ? ( + + ) : ( + + )} + Date +
    +
    + +
    {documents.length !== 0 && diff --git a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx b/frontend/app/location-based-risk/LocationBasedRiskCard.tsx index b4b38719..fc5229b3 100644 --- a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx +++ b/frontend/app/location-based-risk/LocationBasedRiskCard.tsx @@ -22,11 +22,10 @@ interface LeafletGeoJSONMapProps { >; } -const LeafletGeoJSONMap = ({ lat, long, femaRiskCountyLookup }: LeafletGeoJSONMapProps) => { +const LeafletGeoJSONMap = ({ lat, long, femaRiskCountyLookup}: LeafletGeoJSONMapProps) => { const mapRef = useRef(null); const { isLoaded: leafletLoaded, error: leafletError } = useLeafletLoader(); const [userLocation, setUserLocation] = useState<[number, number]>([lat || 0, long || 0]); - const { map, isReady: mapReady, panTo } = useLeafletMap(mapRef, leafletLoaded, userLocation); useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); diff --git a/frontend/components/dashboard/BusinessRisk.tsx b/frontend/components/dashboard/BusinessRisk.tsx index 88b3f1e9..2cd5e0e8 100644 --- a/frontend/components/dashboard/BusinessRisk.tsx +++ b/frontend/components/dashboard/BusinessRisk.tsx @@ -4,7 +4,7 @@ import { Card, CardContent, CardFooter, CardTitle } from "@/components/ui/card"; export default function BusinessRisk() { return ( - Location Based Risk + Location Based Risk diff --git a/frontend/components/dashboard/LocationRisk.tsx b/frontend/components/dashboard/LocationRisk.tsx index e3af7d59..86c43feb 100644 --- a/frontend/components/dashboard/LocationRisk.tsx +++ b/frontend/components/dashboard/LocationRisk.tsx @@ -6,13 +6,20 @@ import { HazardIndexOverviewCard, RiskIndexOverviewCard } from "../../app/locati import { useSelectedLocation } from "@/app/location-based-risk/hooks/useSelectedLocation"; import { LocationsDropDown } from "./locationsDropDown"; import { useFEMARiskScore } from "@/app/location-based-risk/hooks/useFEMARiskScore"; +import { useEffect, useState } from "react"; +import { LargeLoading } from "../loading"; export default function LocationRisk() { const { availableLocations, selectedLocation, setSelectedLocation } = useSelectedLocation(); const { countyLookup: femaRiskCountyLookup, lastUpdated } = useFEMARiskScore(); + const [loading, setLoading] = useState(true) + + useEffect(() => { + console.log("Loading", loading) + }, [loading]) return ( - +
    diff --git a/frontend/components/dashboard/RevenueAndExpenses.tsx b/frontend/components/dashboard/RevenueAndExpenses.tsx index 09068b8c..68dd6306 100644 --- a/frontend/components/dashboard/RevenueAndExpenses.tsx +++ b/frontend/components/dashboard/RevenueAndExpenses.tsx @@ -8,27 +8,28 @@ import { Bar, BarChart, CartesianGrid, XAxis } from "recharts"; import { ChartContainer, ChartTooltipContent, ChartTooltip, ChartConfig } from "@/components/ui/chart"; import Link from "next/link"; import { FaCircle } from "react-icons/fa"; +import { LargeLoading } from "../loading"; +import { FaExclamation } from "react-icons/fa6"; export function RevenueAndExpensesNoData() { return ( - -
    -
    - - - -
    + + Revenue and Expenses +
    + + + +
    +
    + +
    -
    -

    No data shown in this range

    -

    - You need to connect QuickBooks or upload a CSV for your data -

    +
    +

    No data shown in this range

    +

    + You need to connect QuickBooks or upload a CSV for your data +

    +
    @@ -112,7 +113,7 @@ export default function RevenueAndExpenses() {
    Revenue and Expenses - {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) && ( + {!(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) && (
    = 0 ? "bg-seafoam" : "bg-pink"}`} @@ -127,23 +128,10 @@ export default function RevenueAndExpenses() { )}
    - {!(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( + {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    -
    + + ) : (
    diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx index deb3c001..5b2cb370 100644 --- a/frontend/components/loading.tsx +++ b/frontend/components/loading.tsx @@ -9,3 +9,22 @@ export default function Loading({ lines = 6 }) {
    ); } + +export function LargeLoading() { + return ( +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + ) +} From 55f15fe35ae79797801c59ff9d6b818eaff073ad Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 20:55:08 -0500 Subject: [PATCH 07/11] fixed business doc bug --- frontend/app/layout.tsx | 1 + .../components/table/CategorySelector.tsx | 45 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 59565de0..7000c35c 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -39,6 +39,7 @@ export default function RootLayout({ {!hideNavbar && }
    {children} +
    diff --git a/frontend/components/table/CategorySelector.tsx b/frontend/components/table/CategorySelector.tsx index 08fe8e9f..73b2cd8d 100644 --- a/frontend/components/table/CategorySelector.tsx +++ b/frontend/components/table/CategorySelector.tsx @@ -1,6 +1,7 @@ "use client"; import { useState, useRef, useEffect } from "react"; +import { createPortal } from "react-dom"; interface CategorySelectorProps { selectedCategory: string; @@ -25,15 +26,38 @@ export default function CategorySelector({ const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const dropdownRef = useRef(null); + const [portalRoot, setPortalRoot] = useState(null); + const portalDropdownRef = useRef(null); + const [coords, setCoords] = useState({ top: 0, left: 0, width: 0 }); + + + + useEffect(() => { + setPortalRoot(document.getElementById("portal-root")); + }, []); // Close dropdown when clicking outside useEffect(() => { + if (isOpen && dropdownRef.current) { + const rect = dropdownRef.current.getBoundingClientRect(); + setCoords({ + top: rect.bottom + window.scrollY, + left: rect.left + window.scrollX, + width: rect.width, + }); + } + const handleClickOutside = (event: MouseEvent) => { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsOpen(false); - } - }; + const target = event.target as Node; + + + if (dropdownRef.current?.contains(target)) return; + + if (portalDropdownRef.current?.contains(target)) return; + + setIsOpen(false); + }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); } @@ -82,8 +106,15 @@ export default function CategorySelector({
    {/* Dropdown */} - {isOpen && ( -
    + {isOpen && portalRoot && + createPortal( +
    {/* Search Input */}
    - )} + ,portalRoot)}
    ); } From 8413644a04c6798d8af3cdda690cf8c78bfa7f91 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Wed, 26 Nov 2025 23:22:02 -0500 Subject: [PATCH 08/11] notification loading state --- .../expense-table/table-content.tsx | 4 +- frontend/app/layout.tsx | 2 +- .../hooks/useLeafletLoader.ts | 1 - frontend/app/notifications/notification.tsx | 8 +- .../components/dashboard/BusinessRisk.tsx | 4 +- .../components/dashboard/LocationRisk.tsx | 7 - .../dashboard/RevenueAndExpenses.tsx | 10 +- frontend/components/loading.tsx | 36 ++-- .../components/table/CategorySelector.tsx | 159 +++++++++--------- frontend/components/table/index.tsx | 24 ++- 10 files changed, 133 insertions(+), 122 deletions(-) diff --git a/frontend/app/expense-tracker/expense-table/table-content.tsx b/frontend/app/expense-tracker/expense-table/table-content.tsx index acf2065b..64c6abeb 100644 --- a/frontend/app/expense-tracker/expense-table/table-content.tsx +++ b/frontend/app/expense-tracker/expense-table/table-content.tsx @@ -175,5 +175,7 @@ export default function TableContent({ if (purchases.error) return
    Error loading expenses
    ; - return
    onRowClick?.(row.originalPurchase)} />; + return ( +
    onRowClick?.(row.originalPurchase)} /> + ); } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 7000c35c..af653c9f 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -39,7 +39,7 @@ export default function RootLayout({ {!hideNavbar && }
    {children} -
    +
    diff --git a/frontend/app/location-based-risk/hooks/useLeafletLoader.ts b/frontend/app/location-based-risk/hooks/useLeafletLoader.ts index 45fdc321..9b5550e1 100644 --- a/frontend/app/location-based-risk/hooks/useLeafletLoader.ts +++ b/frontend/app/location-based-risk/hooks/useLeafletLoader.ts @@ -39,6 +39,5 @@ export const useLeafletLoader = () => { loadLeaflet(); }, []); - return { isLoaded, error }; }; diff --git a/frontend/app/notifications/notification.tsx b/frontend/app/notifications/notification.tsx index 1d360697..b5c53e29 100644 --- a/frontend/app/notifications/notification.tsx +++ b/frontend/app/notifications/notification.tsx @@ -8,6 +8,7 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover import { useMutation } from "@tanstack/react-query"; import { updateNotificationStatus } from "@/api/notifications"; import { useState } from "react"; +import Loading from "@/components/loading"; interface NotificationProps { notification: NotificationType; @@ -70,8 +71,8 @@ export default function Notification({ notification }: NotificationProps) { export function LoadingNotification() { return (
    -
    -

    Notification Loading...

    +
    +

    Notification

    -

    Time Declared

    -

    Description

    +
    ); } diff --git a/frontend/components/dashboard/BusinessRisk.tsx b/frontend/components/dashboard/BusinessRisk.tsx index 2cd5e0e8..e889c38d 100644 --- a/frontend/components/dashboard/BusinessRisk.tsx +++ b/frontend/components/dashboard/BusinessRisk.tsx @@ -4,7 +4,9 @@ import { Card, CardContent, CardFooter, CardTitle } from "@/components/ui/card"; export default function BusinessRisk() { return ( - Location Based Risk + + Location Based Risk + diff --git a/frontend/components/dashboard/LocationRisk.tsx b/frontend/components/dashboard/LocationRisk.tsx index 86c43feb..20f559a9 100644 --- a/frontend/components/dashboard/LocationRisk.tsx +++ b/frontend/components/dashboard/LocationRisk.tsx @@ -6,17 +6,10 @@ import { HazardIndexOverviewCard, RiskIndexOverviewCard } from "../../app/locati import { useSelectedLocation } from "@/app/location-based-risk/hooks/useSelectedLocation"; import { LocationsDropDown } from "./locationsDropDown"; import { useFEMARiskScore } from "@/app/location-based-risk/hooks/useFEMARiskScore"; -import { useEffect, useState } from "react"; -import { LargeLoading } from "../loading"; export default function LocationRisk() { const { availableLocations, selectedLocation, setSelectedLocation } = useSelectedLocation(); const { countyLookup: femaRiskCountyLookup, lastUpdated } = useFEMARiskScore(); - const [loading, setLoading] = useState(true) - - useEffect(() => { - console.log("Loading", loading) - }, [loading]) return ( diff --git a/frontend/components/dashboard/RevenueAndExpenses.tsx b/frontend/components/dashboard/RevenueAndExpenses.tsx index 68dd6306..baadfe22 100644 --- a/frontend/components/dashboard/RevenueAndExpenses.tsx +++ b/frontend/components/dashboard/RevenueAndExpenses.tsx @@ -17,11 +17,11 @@ export function RevenueAndExpensesNoData() { Revenue and Expenses
    - +
    - +
    @@ -128,9 +128,9 @@ export default function RevenueAndExpenses() { )}
    - {(expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading)) ? ( - - + {expensesQueries.some((q) => q.isLoading) || revenueQueries.some((q) => q.isLoading) ? ( + + ) : ( diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx index 5b2cb370..50616987 100644 --- a/frontend/components/loading.tsx +++ b/frontend/components/loading.tsx @@ -12,19 +12,25 @@ export default function Loading({ lines = 6 }) { export function LargeLoading() { return ( -
    -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    -
    - ) +
    +
    +
    +
    +
    +
    + +
    + +
    +
    +
    +
    +
    + ); } diff --git a/frontend/components/table/CategorySelector.tsx b/frontend/components/table/CategorySelector.tsx index 73b2cd8d..763e7c18 100644 --- a/frontend/components/table/CategorySelector.tsx +++ b/frontend/components/table/CategorySelector.tsx @@ -30,11 +30,9 @@ export default function CategorySelector({ const portalDropdownRef = useRef(null); const [coords, setCoords] = useState({ top: 0, left: 0, width: 0 }); - - useEffect(() => { setPortalRoot(document.getElementById("portal-root")); - }, []); + }, []); // Close dropdown when clicking outside useEffect(() => { @@ -50,13 +48,11 @@ export default function CategorySelector({ const handleClickOutside = (event: MouseEvent) => { const target = event.target as Node; - - if (dropdownRef.current?.contains(target)) return; + if (dropdownRef.current?.contains(target)) return; - - if (portalDropdownRef.current?.contains(target)) return; + if (portalDropdownRef.current?.contains(target)) return; - setIsOpen(false); + setIsOpen(false); }; if (isOpen) { document.addEventListener("mousedown", handleClickOutside); @@ -106,82 +102,87 @@ export default function CategorySelector({
    {/* Dropdown */} - {isOpen && portalRoot && + {isOpen && + portalRoot && createPortal( -
    - {/* Search Input */} -
    - setSearchQuery(e.target.value)} - className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 text-sm" - autoFocus - /> -
    - - {/* Category List */} -
    - {filteredCategories.length > 0 ? ( - filteredCategories.map((category) => ( -
    handleCategorySelect(category)} - className="flex items-center gap-3 px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors" - > - {/* Drag Handle */} -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    -
    +
    + {/* Search Input */} +
    + setSearchQuery(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-purple-500 text-sm" + autoFocus + /> +
    + + {/* Category List */} +
    + {filteredCategories.length > 0 ? ( + filteredCategories.map((category) => ( +
    handleCategorySelect(category)} + className="flex items-center gap-3 px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors" + > + {/* Drag Handle */} +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    -
    - {/* Category */} - - {category} - -
    - )) - ) : ( -
    - {searchQuery ? ( -
    -

    No matching categories

    - + {category} +
    - ) : ( - "No categories available" - )} -
    - )} -
    -
    - ,portalRoot)} + )) + ) : ( +
    + {searchQuery ? ( +
    +

    No matching categories

    + +
    + ) : ( + "No categories available" + )} +
    + )} +
    +
    , + portalRoot + )}
    ); } diff --git a/frontend/components/table/index.tsx b/frontend/components/table/index.tsx index f35957e4..c9504356 100644 --- a/frontend/components/table/index.tsx +++ b/frontend/components/table/index.tsx @@ -2,7 +2,15 @@ import { flexRender, Table as ReactTable } from "@tanstack/react-table"; import { Table as CTable, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"; import { Spinner } from "../ui/spinner"; -export function Table({ table, isLoading, onRowClick }: { table: ReactTable; isLoading: boolean; onRowClick?: (row: T) => void }) { +export function Table({ + table, + isLoading, + onRowClick, +}: { + table: ReactTable; + isLoading: boolean; + onRowClick?: (row: T) => void; +}) { return ( @@ -32,13 +40,13 @@ export function Table({ table, isLoading, onRowClick }: { table: ReactTable {table.getRowModel().rows.map((row) => ( onRowClick?.(row.original)} - className={[ - onRowClick ? "cursor-pointer hover:bg-muted/50" : "", - row.depth > 0 ? "bg-muted/100" : "", - ].join(" ")} - > + key={row.id} + onClick={() => onRowClick?.(row.original)} + className={[ + onRowClick ? "cursor-pointer hover:bg-muted/50" : "", + row.depth > 0 ? "bg-muted/100" : "", + ].join(" ")} + > {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} From 414f314c2f8b6b0dddb52c2e3d52ef4ddd469ab2 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Fri, 28 Nov 2025 22:01:03 -0500 Subject: [PATCH 09/11] loading for risk map --- .../LocationBasedRiskCard.tsx | 2 +- .../RiskIndexOverviewCard.tsx | 9 +-- .../hooks/useGeoJSONLayers.ts | 20 +++++-- .../components/dashboard/LocationRisk.tsx | 57 ++++++++++++++++--- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx b/frontend/app/location-based-risk/LocationBasedRiskCard.tsx index fc5229b3..af1310eb 100644 --- a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx +++ b/frontend/app/location-based-risk/LocationBasedRiskCard.tsx @@ -27,7 +27,7 @@ const LeafletGeoJSONMap = ({ lat, long, femaRiskCountyLookup}: LeafletGeoJSONMap const { isLoaded: leafletLoaded, error: leafletError } = useLeafletLoader(); const [userLocation, setUserLocation] = useState<[number, number]>([lat || 0, long || 0]); const { map, isReady: mapReady, panTo } = useLeafletMap(mapRef, leafletLoaded, userLocation); - useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); + const {loading} = useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); // Pan to new location whenever lat or long changes useEffect(() => { diff --git a/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx b/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx index 94089499..bf60014a 100644 --- a/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx +++ b/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx @@ -14,11 +14,12 @@ interface RiskIndexOverviewCardProps { wildFire: string; } | undefined; + loading: boolean } -export const RiskIndexOverviewCard = ({ riskAttributes }: RiskIndexOverviewCardProps) => { +export const RiskIndexOverviewCard = ({ riskAttributes, loading = false }: RiskIndexOverviewCardProps) => { return ( - +

    Risk Index Overview

    @@ -40,9 +41,9 @@ export const RiskIndexOverviewCard = ({ riskAttributes }: RiskIndexOverviewCardP ); }; -export const HazardIndexOverviewCard = ({ riskAttributes }: RiskIndexOverviewCardProps) => { +export const HazardIndexOverviewCard = ({ riskAttributes, loading=false }: RiskIndexOverviewCardProps) => { return ( - +

    Hazard Risk Rating

    diff --git a/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts b/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts index 1ad74e41..43942a9a 100644 --- a/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts +++ b/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState } from "react"; import { useCountyLevelGEOJSONData } from "./useCountyLevelGEOJSONData"; import type { Map as LeafletMap } from "leaflet"; import { colorFromSevarity } from "./colorFromSeverityLevel"; @@ -25,6 +25,7 @@ export const useGeoJSONLayers = ( ) => { const hasAddedLayersRef = useRef(false); + const [loading, setLoading] = useState(true); const { data: geoJsonCountyData, isLoading: isLoadingGeoJsonData, @@ -41,14 +42,20 @@ export const useGeoJSONLayers = ( !map || !window.L || !isMapReady || - hasAddedLayersRef.current || isLoadingGeoJsonData || !geoJsonCountyData || !femaRiskCountyLookup - ) + ) { + setLoading(true); return; - + } + if ( + hasAddedLayersRef.current + ) { + return; + } try { + window.L.geoJSON(geoJsonCountyData, { style: (feature) => ({ color: colorFromSevarity( @@ -71,10 +78,13 @@ export const useGeoJSONLayers = ( } }, }).addTo(map); - + setLoading(false); hasAddedLayersRef.current = true; } catch (err) { + setLoading(false); console.error("Error adding GeoJSON layers:", err); } }, [geoJsonCountyData, femaRiskCountyLookup, map]); + + return {loading} }; diff --git a/frontend/components/dashboard/LocationRisk.tsx b/frontend/components/dashboard/LocationRisk.tsx index 20f559a9..2192627a 100644 --- a/frontend/components/dashboard/LocationRisk.tsx +++ b/frontend/components/dashboard/LocationRisk.tsx @@ -1,15 +1,49 @@ + "use client"; -import LeafletGeoJSONMap from "@/app/location-based-risk/LocationBasedRiskCard"; +import React, { useEffect, useRef, useState } from "react"; import { Card, CardContent, CardTitle } from "@/components/ui/card"; import { InfoIcon } from "lucide-react"; +import { Spinner } from "@/components/ui/spinner"; import { HazardIndexOverviewCard, RiskIndexOverviewCard } from "../../app/location-based-risk/RiskIndexOverviewCard"; import { useSelectedLocation } from "@/app/location-based-risk/hooks/useSelectedLocation"; import { LocationsDropDown } from "./locationsDropDown"; import { useFEMARiskScore } from "@/app/location-based-risk/hooks/useFEMARiskScore"; +import { useLeafletMap } from "@/app/location-based-risk/hooks/useLeafletMap"; +import { useGeoJSONLayers } from "@/app/location-based-risk/hooks/useGeoJSONLayers"; +import { useLeafletLoader } from "@/app/location-based-risk/hooks/useLeafletLoader"; +import { LargeLoading } from "../loading"; export default function LocationRisk() { const { availableLocations, selectedLocation, setSelectedLocation } = useSelectedLocation(); const { countyLookup: femaRiskCountyLookup, lastUpdated } = useFEMARiskScore(); + + const mapRef = useRef(null); + const { isLoaded: leafletLoaded, error: leafletError } = useLeafletLoader(); + const [userLocation, setUserLocation] = useState<[number, number]>([ + selectedLocation?.lat || 0, + selectedLocation?.long || 0 + ]); + const { map, isReady: mapReady, panTo } = useLeafletMap(mapRef, leafletLoaded, userLocation); + const { loading:geoJsonLoading } = useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); + + // Pan to new location whenever selectedLocation changes + useEffect(() => { + if (mapReady && selectedLocation?.lat !== undefined && selectedLocation?.long !== undefined) { + setUserLocation([selectedLocation.lat, selectedLocation.long]); + panTo(selectedLocation.lat, selectedLocation.long); + } + }, [selectedLocation?.lat, selectedLocation?.long, mapReady, panTo]); + + const isLoading = !leafletLoaded || !mapReady || geoJsonLoading; + + useEffect(() => { + console.log("Loading states:", { + leafletLoaded, + mapReady, + geoJsonLoading, + isLoading + }); + }, [isLoading]) return ( @@ -20,7 +54,7 @@ export default function LocationRisk() {
    - {"This data is taken from FEMA\'s National Risk Map"} + {"This data is taken from FEMA's National Risk Map"}
    @@ -31,24 +65,30 @@ export default function LocationRisk() { />
    +
    + {isLoading && + + + + }
    - +
    +
    +
    @@ -64,6 +104,7 @@ export default function LocationRisk() { )}
    +
    ); -} +} \ No newline at end of file From 057d2db26de5ed930d37c197035f342b694d05c9 Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Fri, 28 Nov 2025 22:20:39 -0500 Subject: [PATCH 10/11] linter and last touches --- .../overview/LocationsCard.tsx | 4 +- .../view-documents/ViewDocuments.tsx | 10 +-- .../LocationBasedRiskCard.tsx | 66 -------------- .../RiskIndexOverviewCard.tsx | 6 +- .../hooks/useGeoJSONLayers.ts | 16 +--- frontend/app/notifications/notification.tsx | 2 +- .../components/dashboard/LocationRisk.tsx | 88 +++++++++---------- frontend/components/loading.tsx | 4 +- 8 files changed, 59 insertions(+), 137 deletions(-) delete mode 100644 frontend/app/location-based-risk/LocationBasedRiskCard.tsx diff --git a/frontend/app/business-profile/overview/LocationsCard.tsx b/frontend/app/business-profile/overview/LocationsCard.tsx index 2a10afef..50abfd03 100644 --- a/frontend/app/business-profile/overview/LocationsCard.tsx +++ b/frontend/app/business-profile/overview/LocationsCard.tsx @@ -16,7 +16,7 @@ export default function LocationsCard() { const [editingLocationIndex, setEditingLocationIndex] = useState(null); const [saveError, setSaveError] = useState(null); - const { data: locationsQuery, isPending: businessPending } = useQuery({ + const { data: locationsQuery, isPending: locationPending } = useQuery({ queryKey: ["locations"], queryFn: getCompanyLocations, }); @@ -95,7 +95,7 @@ export default function LocationsCard() { return (

    Locations

    - {businessPending ? ( + {locationPending ? ( ) : (
    diff --git a/frontend/app/business-profile/view-documents/ViewDocuments.tsx b/frontend/app/business-profile/view-documents/ViewDocuments.tsx index 95310338..af72e8fd 100644 --- a/frontend/app/business-profile/view-documents/ViewDocuments.tsx +++ b/frontend/app/business-profile/view-documents/ViewDocuments.tsx @@ -16,6 +16,7 @@ import { uploadToS3, } from "@/api/business-profile"; import { BusinessDocument, DocumentCategories } from "@/types/documents"; +import { Spinner } from "@/components/ui/spinner"; type SortOrder = "asc" | "desc"; @@ -219,10 +220,7 @@ export default function ViewDocuments() {

    Business Documents

    -

    - Upload general business documents below. - {isLoadingDocuments && " Loading documents..."} -

    +

    Upload general business documents below.

    +
    + +
    ) : documents.length === 0 ? (
    No documents found. Upload your first document to get started! diff --git a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx b/frontend/app/location-based-risk/LocationBasedRiskCard.tsx deleted file mode 100644 index af1310eb..00000000 --- a/frontend/app/location-based-risk/LocationBasedRiskCard.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { useLeafletMap } from "./hooks/useLeafletMap"; -import { useGeoJSONLayers } from "./hooks/useGeoJSONLayers"; -import { useLeafletLoader } from "./hooks/useLeafletLoader"; -import { Spinner } from "@/components/ui/spinner"; - -interface LeafletGeoJSONMapProps { - lat: number | undefined; - long: number | undefined; - femaRiskCountyLookup: Map< - string, - { - countyFipsCode: string; - riskRating: string; - ealRating: string; - socialVuln: string; - communityResilience: string; - coastalFlooding: string; - drought: string; - wildFire: string; - } - >; -} - -const LeafletGeoJSONMap = ({ lat, long, femaRiskCountyLookup}: LeafletGeoJSONMapProps) => { - const mapRef = useRef(null); - const { isLoaded: leafletLoaded, error: leafletError } = useLeafletLoader(); - const [userLocation, setUserLocation] = useState<[number, number]>([lat || 0, long || 0]); - const { map, isReady: mapReady, panTo } = useLeafletMap(mapRef, leafletLoaded, userLocation); - const {loading} = useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); - - // Pan to new location whenever lat or long changes - useEffect(() => { - if (mapReady && lat !== undefined && long !== undefined) { - setUserLocation([lat, long]); - panTo(lat, long); - } - }, [lat, long, mapReady, panTo]); - - const isLoading = !leafletLoaded || !mapReady; - - if (leafletError) { - return ; - } - - return ( -
    - {isLoading && ( -
    - -
    - )} -
    -
    - ); -}; - -const ErrorDisplay: React.FC<{ error: string }> = ({ error }) => ( -
    -
    -
    Error: {error}
    -
    -
    -); - -export default LeafletGeoJSONMap; diff --git a/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx b/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx index bf60014a..401035a5 100644 --- a/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx +++ b/frontend/app/location-based-risk/RiskIndexOverviewCard.tsx @@ -14,7 +14,7 @@ interface RiskIndexOverviewCardProps { wildFire: string; } | undefined; - loading: boolean + loading: boolean; } export const RiskIndexOverviewCard = ({ riskAttributes, loading = false }: RiskIndexOverviewCardProps) => { @@ -41,9 +41,9 @@ export const RiskIndexOverviewCard = ({ riskAttributes, loading = false }: RiskI ); }; -export const HazardIndexOverviewCard = ({ riskAttributes, loading=false }: RiskIndexOverviewCardProps) => { +export const HazardIndexOverviewCard = ({ riskAttributes, loading = false }: RiskIndexOverviewCardProps) => { return ( - +

    Hazard Risk Rating

    diff --git a/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts b/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts index 43942a9a..223ba184 100644 --- a/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts +++ b/frontend/app/location-based-risk/hooks/useGeoJSONLayers.ts @@ -38,24 +38,14 @@ export const useGeoJSONLayers = ( }, []); useEffect(() => { - if ( - !map || - !window.L || - !isMapReady || - isLoadingGeoJsonData || - !geoJsonCountyData || - !femaRiskCountyLookup - ) { + if (!map || !window.L || !isMapReady || isLoadingGeoJsonData || !geoJsonCountyData || !femaRiskCountyLookup) { setLoading(true); return; } - if ( - hasAddedLayersRef.current - ) { + if (hasAddedLayersRef.current) { return; } try { - window.L.geoJSON(geoJsonCountyData, { style: (feature) => ({ color: colorFromSevarity( @@ -86,5 +76,5 @@ export const useGeoJSONLayers = ( } }, [geoJsonCountyData, femaRiskCountyLookup, map]); - return {loading} + return { loading }; }; diff --git a/frontend/app/notifications/notification.tsx b/frontend/app/notifications/notification.tsx index b5c53e29..68e8a686 100644 --- a/frontend/app/notifications/notification.tsx +++ b/frontend/app/notifications/notification.tsx @@ -84,7 +84,7 @@ export function LoadingNotification() {
    - +
    ); } diff --git a/frontend/components/dashboard/LocationRisk.tsx b/frontend/components/dashboard/LocationRisk.tsx index 2192627a..0f893a80 100644 --- a/frontend/components/dashboard/LocationRisk.tsx +++ b/frontend/components/dashboard/LocationRisk.tsx @@ -1,9 +1,7 @@ - "use client"; import React, { useEffect, useRef, useState } from "react"; import { Card, CardContent, CardTitle } from "@/components/ui/card"; import { InfoIcon } from "lucide-react"; -import { Spinner } from "@/components/ui/spinner"; import { HazardIndexOverviewCard, RiskIndexOverviewCard } from "../../app/location-based-risk/RiskIndexOverviewCard"; import { useSelectedLocation } from "@/app/location-based-risk/hooks/useSelectedLocation"; import { LocationsDropDown } from "./locationsDropDown"; @@ -16,15 +14,15 @@ import { LargeLoading } from "../loading"; export default function LocationRisk() { const { availableLocations, selectedLocation, setSelectedLocation } = useSelectedLocation(); const { countyLookup: femaRiskCountyLookup, lastUpdated } = useFEMARiskScore(); - + const mapRef = useRef(null); - const { isLoaded: leafletLoaded, error: leafletError } = useLeafletLoader(); + const { isLoaded: leafletLoaded } = useLeafletLoader(); const [userLocation, setUserLocation] = useState<[number, number]>([ selectedLocation?.lat || 0, - selectedLocation?.long || 0 + selectedLocation?.long || 0, ]); const { map, isReady: mapReady, panTo } = useLeafletMap(mapRef, leafletLoaded, userLocation); - const { loading:geoJsonLoading } = useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); + const { loading: geoJsonLoading } = useGeoJSONLayers(map, mapReady, femaRiskCountyLookup); // Pan to new location whenever selectedLocation changes useEffect(() => { @@ -41,9 +39,9 @@ export default function LocationRisk() { leafletLoaded, mapReady, geoJsonLoading, - isLoading + isLoading, }); - }, [isLoading]) + }, [isLoading]); return ( @@ -66,45 +64,45 @@ export default function LocationRisk() {
    - {isLoading && - - - - } - -
    -
    -
    -
    -
    -
    - - + {isLoading && ( + + + + )} + +
    +
    +
    +
    +
    +
    + + +
    + {lastUpdated && ( +

    + This data was last updated{" "} + {lastUpdated.toLocaleString("en-US", { + timeZone: "America/New_York", + dateStyle: "medium", + timeStyle: "short", + })} +

    + )}
    - {lastUpdated && ( -

    - This data was last updated{" "} - {lastUpdated.toLocaleString("en-US", { - timeZone: "America/New_York", - dateStyle: "medium", - timeStyle: "short", - })} -

    - )} -
    -
    +
    ); -} \ No newline at end of file +} diff --git a/frontend/components/loading.tsx b/frontend/components/loading.tsx index 50616987..cbde8536 100644 --- a/frontend/components/loading.tsx +++ b/frontend/components/loading.tsx @@ -23,11 +23,11 @@ export function LargeLoading() {
    From 36d1d55f87ff2f2aa333ba241ef6c8ba59b8a5ca Mon Sep 17 00:00:00 2001 From: ZainabImadulla Date: Fri, 28 Nov 2025 22:23:59 -0500 Subject: [PATCH 11/11] fixed typecheck issue --- frontend/components/table/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/components/table/index.tsx b/frontend/components/table/index.tsx index c9504356..37e4de77 100644 --- a/frontend/components/table/index.tsx +++ b/frontend/components/table/index.tsx @@ -4,11 +4,11 @@ import { Spinner } from "../ui/spinner"; export function Table({ table, - isLoading, + isLoading = false, onRowClick, }: { table: ReactTable; - isLoading: boolean; + isLoading?: boolean; onRowClick?: (row: T) => void; }) { return (