Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
2d4299e
feat: completed drawing flow
jeafreezy Jun 11, 2025
ee14773
chore: removed drafts model in dataset detail
jeafreezy Jun 16, 2025
5c6c176
chore: finalized upload flow
jeafreezy Jun 25, 2025
6501410
feat: finalized drawing flow + updated results dashboard
jeafreezy Jun 25, 2025
d637ebd
feat: finalized offline prediction options
jeafreezy Jun 28, 2025
6d3a96c
feat: finalized offline predictions
jeafreezy Jun 29, 2025
6a0d98d
feat: finalized the mobile responsiveness of offline predictions
jeafreezy Jun 29, 2025
372735e
chore: enable logs in running predictions
jeafreezy Jun 29, 2025
55f7358
chore: updated prediction card
jeafreezy Jun 29, 2025
95c0e9f
fix: fixed layout toggle bug
jeafreezy Jun 29, 2025
56ca94c
chore: default offline prediction layout to table
jeafreezy Jun 30, 2025
7c4f246
feat: wip with mapswipe integration
jeafreezy Jul 1, 2025
36f433c
chore: fixed import bug
jeafreezy Jul 1, 2025
eea68f9
chore: updated logo font
jeafreezy Jul 1, 2025
fb38523
chore: removed unnecessary font assets
jeafreezy Jul 1, 2025
8445b1f
Merge pull request #430 from jeafreezy/offline-predictions
kshitijrajsharma Jul 1, 2025
300a20b
chore: updated logo font
jeafreezy Jul 1, 2025
0870302
chore: removed unnecessary font assets
jeafreezy Jul 1, 2025
03e9a28
Merge branch 'feat/update-fair-logo' of https://github.com/jeafreezy/…
jeafreezy Jul 1, 2025
8ae02eb
Merge pull request #435 from jeafreezy/feat/update-logo-branding
kshitijrajsharma Jul 1, 2025
71d59b2
feat: wip with mapswipe integration
jeafreezy Jul 1, 2025
3caeafd
chore: fixed import bug
jeafreezy Jul 1, 2025
bc2d282
Merge branch 'feat/mapswipe-integration' of https://github.com/jeafre…
jeafreezy Jul 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions frontend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ VITE_MAX_TRAINING_AREA_SIZE = 5000000
# Default value: 5797 square meters.
VITE_MIN_TRAINING_AREA_SIZE = 5797

# The maximum file size allowed for training area upload, measure in bytes.
# Data type: Positive Integer (e.g., 500000).
# Default value: 5242880 bytes (5 MB).
VITE_MAX_TRAINING_AREA_UPLOAD_FILE_SIZE = 5242880
# The maximum file size allowed for training area upload, measured in bytes.
# Data type: Positive Integer (e.g., 1048576 for 1 MB).
# Default value: 1048576 bytes (1 MB).
VITE_MAX_TRAINING_AREA_UPLOAD_FILE_SIZE = 1048576

# The current version of the application.
# This is used in the OSM redirect callback when a training area is opened in OSM.
Expand Down Expand Up @@ -209,4 +209,22 @@ VITE_FAIR_MODELS_BASE_PATH = '/mnt/efsmount/data'
# The offset step for adjusting the training labels in the training area page.
# Data type: Positive Integer (e.g., 0.5).
# Default value: 0.5.
VITE_OFFSET_STEP = 0.5
VITE_OFFSET_STEP = 0.5

# The Firebase configuration for MapSwipe.
# This configuration is used to connect to the Firebase project for MapSwipe.
# Note: The values below are examples and should be replaced with your actual Firebase project configuration.
# - apiKey: String (e.g., "AIxxxxxx").
# - authDomain: String (e.g., "dev-hxxxxxxxxx").
# - databaseURL: String (e.g., "https://dev-hxxxxxxxx").
# - projectId: String (e.g., "dev-xxxxxxxx").
# - storageBucket: String (e.g., "dev-heigit-xxxxxxxx").
# - messagingSenderId: String (e.g., "72xxxxxx").
# - appId: String (e.g, "1:7240294dxxxxx)
VITE_MAPSWIPE_FIREBASE_API_KEY="",
VITE_MAPSWIPE_FIREBASE_AUTH_DOMAIN="",
VITE_MAPSWIPE_FIREBASE_DATABASE_URL="",
VITE_MAPSWIPE_FIREBASE_PROJECT_ID="",
VITE_MAPSWIPE_FIREBASE_STORAGE_BUCKET="",
VITE_MAPSWIPE_FIREBASE_MESSAGING_SENDER_ID="",
VITE_MAPSWIPE_FIREBASE_APP_ID=""
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@turf/helpers": "^7.2.0",
"axios": "^1.7.7",
"clsx": "^2.1.1",
"firebase": "^11.10.0",
"framer-motion": "^11.9.0",
"geojson": "^0.5.0",
"maplibre-gl": "^5.3.1",
Expand Down
810 changes: 793 additions & 17 deletions frontend/pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions frontend/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ import { TourProvider } from "@reactour/tour";
import { APP_TOUR_STEPS } from "@/constants/site-tour";
import { AuthProvider } from "./providers/auth-provider";
import { HelmetProvider } from "react-helmet-async";
import { initializeApp } from "firebase/app";
import { firebaseConfig } from "@/config";

// Initialize Firebase
initializeApp(firebaseConfig);

export const App = () => {
const queryClient = new QueryClient({
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/providers/auth-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ const AuthContext = createContext<TAuthContext>({
token: "",
user: {} as TUser,
authenticateUser: async () => Promise.resolve(),
logout: () => { },
logout: () => {},
isAuthenticated: false,
setUser: () => { },
setUser: () => {},
});

export const useAuth = () => {
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/app/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,17 @@ const router = createBrowserRouter([
};
},
},
{
path: APPLICATION_ROUTES.PROFILE_OFFLINE_PREDICTIONS,
lazy: async () => {
const { UserProfileOfflinePredictionsPage } = await import(
"@/app/routes/profile/offline-predictions"
);
return {
Component: () => <UserProfileOfflinePredictionsPage />,
};
},
},
],
},
/**
Expand Down
1 change: 1 addition & 0 deletions frontend/src/app/routes/datasets/dataset-detail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export const TrainingDatasetsDetailPage = () => {
title="Models Using this Dataset"
datasetId={data.id}
disableStatusFilter
status={0}
/>
</div>
</div>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/app/routes/models/models-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
ModelListGridLayout,
ModelListTableLayout,
} from "@/features/models/layouts";
import { LayoutToggle, ModelsMap } from "@/features/models/components";
import { LayoutToggle } from "@/components/shared/layout-toggle";
import { ModelsMap } from "@/features/models/components";
import {
CategoryFilter,
DateRangeFilter,
Expand Down
165 changes: 165 additions & 0 deletions frontend/src/app/routes/profile/offline-predictions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { useAuth } from "@/app/providers/auth-provider";
import { Head } from "@/components/seo";
import { OrderingFilter, Pagination, SearchFilter } from "@/components/shared";
import { LayoutToggle } from "@/components/shared/layout-toggle";
import { LayoutView } from "@/enums";
import { ProfileSectionHeader } from "@/features/user-profile/components";
import OfflinePredictionsTable from "@/features/offline-predictions/components/offline-predictions-table";
import { OfflinePredictionsList } from "@/features/offline-predictions/components/offline-predictions-list";
import { useOfflinePredictionsQueryParams } from "@/features/offline-predictions/hooks/use-predictions";
import { SEARCH_PARAMS } from "@/utils/search-params";
import { useDialog } from "@/hooks/use-dialog";
import { useState } from "react";
import { TOfflinePrediction } from "@/types";
import { PredictionResultDrawer } from "@/features/offline-predictions/components/predictions-results-drawer";
import { TrainingLogsDialog } from "@/features/offline-predictions/components/dialogs/training-logs-dialog";
import { CreateMapswipeProjectDialog } from "@/features/mapswipe/components/mapswipe-project-creation-dialog";

export const UserProfileOfflinePredictionsPage = () => {
const { user } = useAuth();

const {
data,
isError,
isPending,
isPlaceholderData,
query,
updateQuery,
refetch,
} = useOfflinePredictionsQueryParams(user.osm_id);
const { isOpened, openDialog, closeDialog } = useDialog();
const {
isOpened: isPredictionResultOpened,
openDialog: openPredictionResultDialog,
closeDialog: closePredictionResultDialog,
} = useDialog();
const {
isOpened: isMapSwipeProjectCreationDialogOpened,
openDialog: openMapSwipeProjectCreationDialog,
closeDialog: closeMapSwipeProjectCreationDialog,
} = useDialog();
const [activeTaskId, setActiveTaskId] = useState<string | null>(null);
const [activePrediction, setActivePrediction] =
useState<TOfflinePrediction | null>(null);
const handleTrainingLogsModal = (taskId: string) => {
setActiveTaskId(taskId);
openDialog();
};
const handlePredictionResultModal = (prediction: TOfflinePrediction) => {
setActivePrediction(prediction);
openPredictionResultDialog();
};

const handleCreateOrViewMapSwipeProject = (
prediction: TOfflinePrediction,
) => {
const mapSwipeProjectExists = prediction.mapswipe_id;
if (!mapSwipeProjectExists) {
openMapSwipeProjectCreationDialog();
}
setActivePrediction(prediction);
};
return (
<>
{activePrediction && (
<CreateMapswipeProjectDialog
isOpened={isMapSwipeProjectCreationDialogOpened}
closeDialog={closeMapSwipeProjectCreationDialog}
predictionResult={activePrediction}
/>
)}
{activePrediction && (
<PredictionResultDrawer
tileServiceUrl={activePrediction.config.source}
predictionId={activePrediction.id}
isOpened={isPredictionResultOpened}
closeDialog={closePredictionResultDialog}
/>
)}
{activeTaskId && (
<TrainingLogsDialog
taskId={activeTaskId}
isOpened={isOpened}
closeDialog={closeDialog}
/>
)}
<Head title="Offline Predictions" />
<div className="space-y-8 h-full">
{/* Section heading */}
<div className="w-full gap-y-6 sm:gap-y-0 flex flex-col sm:flex-row justify-between items-start sm:items-center">
<ProfileSectionHeader title={"Predictions"} />
<SearchFilter
query={query}
updateQuery={updateQuery}
placeholder="Search ..."
className="w-full max-w-full sm:w-auto"
/>
</div>
<div className="flex gap-y-6 flex-col md:flex-row md:gap-y-0 w-full justify-between md:items-center">
<div className="flex items-center justify-between w-full">
<p className="text-body-3 font-semibold text-nowrap">
{data?.count} prediction
{data?.count && data?.count > 1 ? "s" : ""}
</p>
<LayoutToggle
query={query}
updateQuery={updateQuery}
isMobile
iconSize="icon"
/>
</div>
<div className="flex w-full justify-between md:justify-end items-center md:gap-x-4">
<OrderingFilter
query={query}
updateQuery={updateQuery}
disabled={isError || isPending}
className="inline-flex"
/>
<div>
<Pagination
totalLength={data?.count as number}
hasNextPage={data?.hasNext as boolean}
hasPrevPage={data?.hasPrev as boolean}
disableNextPage={!data?.hasNext || isPlaceholderData}
disablePrevPage={!data?.hasPrev}
query={query}
updateQuery={updateQuery}
isPlaceholderData={isPlaceholderData}
scrollToTopOnPageSwitch
/>
</div>
<LayoutToggle
query={query}
updateQuery={updateQuery}
iconSize="icon"
/>
</div>
</div>
{query[SEARCH_PARAMS.layout] === LayoutView.LIST ? (
<OfflinePredictionsTable
data={data?.results ?? []}
isError={isError}
isPending={isPending}
handleTrainingLogsModal={handleTrainingLogsModal}
handlePredictionResultModal={handlePredictionResultModal}
handleCreateOrViewMapSwipeProject={
handleCreateOrViewMapSwipeProject
}
/>
) : (
<OfflinePredictionsList
isPending={isPending}
data={data?.results ?? []}
isError={isError}
refetch={refetch}
handleTrainingLogsModal={handleTrainingLogsModal}
handlePredictionResultModal={handlePredictionResultModal}
handleCreateOrViewMapSwipeProject={
handleCreateOrViewMapSwipeProject
}
/>
)}
</div>
</>
);
};
Loading