Skip to content

Commit

Permalink
Merge pull request #234 from hotosm/feat/change-take-off-point
Browse files Browse the repository at this point in the history
Feat: Change take off point and task description page mobile responsive
  • Loading branch information
nrjadkry authored Sep 24, 2024
2 parents 4423a0c + 76a4d31 commit 92d153f
Show file tree
Hide file tree
Showing 18 changed files with 433 additions and 99 deletions.
4 changes: 2 additions & 2 deletions src/backend/app/projects/project_logic.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,12 +200,12 @@ async def get_project_info_from_s3(project_id: uuid.UUID, task_id: uuid.UUID):
# Generate a presigned URL for the assets ZIP file
try:
# Check if the object exists
assets_path = f"processed/{project_id}/{task_id}/assets.zip"
assets_path = f"projects/{project_id}/{task_id}/assets.zip"
get_object_metadata(settings.S3_BUCKET_NAME, assets_path)

# If it exists, generate the presigned URL
presigned_url = get_presigned_url(
settings.S3_BUCKET_NAME, assets_path, expires=3600
settings.S3_BUCKET_NAME, assets_path, expires=2
)
except S3Error as e:
if e.code == "NoSuchKey":
Expand Down
11 changes: 7 additions & 4 deletions src/backend/app/s3.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from minio import Minio
from io import BytesIO
from typing import Any
from datetime import timedelta


def s3_client():
Expand Down Expand Up @@ -182,20 +183,22 @@ def list_objects_from_bucket(bucket_name: str, prefix: str):
return objects


def get_presigned_url(bucket_name: str, object_name: str, expires: int = 3600):
def get_presigned_url(bucket_name: str, object_name: str, expires: int = 2):
"""Generate a presigned URL for an object in an S3 bucket.
Args:
bucket_name (str): The name of the S3 bucket.
object_name (str): The name of the object in the bucket.
expires (int, optional): The time in seconds until the URL expires.
Defaults to 3600.
expires (int, optional): The time in hours until the URL expires.
Defaults to 2 hour.
Returns:
str: The presigned URL to access the object.
"""
client = s3_client()
return client.presigned_get_object(bucket_name, object_name, expires=expires)
return client.presigned_get_object(
bucket_name, object_name, expires=timedelta(hours=expires)
)


def get_object_metadata(bucket_name: str, object_name: str):
Expand Down
1 change: 1 addition & 0 deletions src/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@turf/area": "^7.0.0",
"@turf/bbox": "^7.0.0",
"@turf/centroid": "^7.0.0",
"@turf/helpers": "^7.0.0",
"@turf/meta": "^7.0.0",
"@turf/flatten": "^7.0.0",
"@turf/length": "^7.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const ImageCard = ({
return (
<>
<div
className="naxatw-flex naxatw-h-24 naxatw-w-[6.75rem] naxatw-flex-col naxatw-gap-1 naxatw-rounded-lg naxatw-bg-gray-100 naxatw-px-1 hover:naxatw-bg-gray-300"
className="naxatw-flex naxatw-h-24 naxatw-w-full naxatw-flex-col naxatw-gap-1 naxatw-rounded-lg naxatw-bg-gray-100 naxatw-px-1 hover:naxatw-bg-gray-300"
role="presentation"
onClick={() => dispatch(setSelectedImage(image))}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,24 @@
/* eslint-disable no-await-in-loop */
/* eslint-disable no-console */
/* eslint-disable no-unused-vars */
import { useEffect, useState, useRef } from 'react';
import { motion } from 'framer-motion';
import { useParams } from 'react-router-dom';
import { useMutation } from '@tanstack/react-query';
import { motion } from 'framer-motion';
import { useEffect, useRef, useState } from 'react';

import { useTypedDispatch, useTypedSelector } from '@Store/hooks';
import { Button } from '@Components/RadixComponents/Button';
import { getImageUploadLink } from '@Services/droneOperator';
import {
checkAllImages,
setCheckedImages,
showPopover,
unCheckAllImages,
checkAllImages,
} from '@Store/actions/droneOperatorTask';
import Icon from '@Components/common/Icon';
import { Button } from '@Components/RadixComponents/Button';
import { getImageUploadLink } from '@Services/droneOperator';
import delay from '@Utils/createDelay';
import chunkArray from '@Utils/createChunksOfArray';
import { useTypedDispatch, useTypedSelector } from '@Store/hooks';
import callApiSimultaneously from '@Utils/callApiSimultaneously';
import chunkArray from '@Utils/createChunksOfArray';
import delay from '@Utils/createDelay';
import widthCalulator from '@Utils/percentageCalculator';
import ImageCard from './ImageCard';
import FilesUploadingPopOver from '../LoadingBox';
import ImageCard from './ImageCard';
import PreviewImage from './PreviewImage';

// interface IImageBoxPopOverProps {
Expand Down Expand Up @@ -151,7 +148,7 @@ const ImageBoxPopOver = () => {
className={`naxatw-grid naxatw-gap-4 ${clickedImage ? 'naxatw-grid-cols-[70%_auto]' : 'naxatw-grid-cols-1'}`}
>
<div
className={`scrollbar-images-grid naxatw-grid naxatw-h-[28rem] naxatw-gap-4 naxatw-overflow-y-auto ${clickedImage ? 'naxatw-grid-cols-5' : 'naxatw-grid-cols-6'}`}
className={`scrollbar-images-grid naxatw-grid naxatw-h-[28rem] naxatw-gap-4 naxatw-overflow-y-auto ${clickedImage ? 'naxatw-grid-cols-2 md:naxatw-grid-cols-5' : 'naxatw-grid-cols-3 md:naxatw-grid-cols-6'}`}
>
{imageObject?.map((image, index) => (
<ImageCard
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Icon from '@Components/common/Icon';
import { useNavigate } from 'react-router-dom';
import { toggleModal } from '@Store/actions/common';
import { useDispatch } from 'react-redux';

interface IFilesUploadingPopOverProps {
show: boolean;
Expand All @@ -14,12 +15,14 @@ const FilesUploadingPopOver = ({
filesLength,
uploadedFiles,
}: IFilesUploadingPopOverProps) => {
const navigate = useNavigate();
const dispatch = useDispatch();
// const navigate = useNavigate();

// function to redirect to dashboard after 2 seconds
function redirectToDashboard() {
// function to close modal
function closeModal() {
setTimeout(() => {
navigate('/dashboard');
// navigate('/dashboard');
dispatch(toggleModal());
}, 2000);
return null;
}
Expand Down Expand Up @@ -49,8 +52,8 @@ const FilesUploadingPopOver = ({
<p className="naxatw-text-[0.875rem] naxatw-text-[#7A7676]">
{uploadedFiles === filesLength && uploadedFiles !== 0 ? (
<>
<p>Redirecting to Dashboard ...</p>
{redirectToDashboard()}
{/* <p>Redirecting to Dashboard ...</p> */}
{closeModal()}
</>
) : (
`${uploadedFiles} / ${filesLength} Files Uploaded`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
/* eslint-disable no-nested-ternary */
import { useState, useEffect } from 'react';
import { useParams } from 'react-router-dom';
import { motion } from 'framer-motion';
import { Button } from '@Components/RadixComponents/Button';
import Tab from '@Components/common/Tabs';
import { useGetIndividualTaskQuery, useGetTaskWaypointQuery } from '@Api/tasks';
import { useTypedDispatch, useTypedSelector } from '@Store/hooks';
import { setSecondPageState } from '@Store/actions/droneOperatorTask';
import { Button } from '@Components/RadixComponents/Button';
import useWindowDimensions from '@Hooks/useWindowDimensions';
import { useTypedSelector } from '@Store/hooks';
import hasErrorBoundary from '@Utils/hasErrorBoundary';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { toast } from 'react-toastify';
import UploadsBox from './UploadsBox';
import MapSection from '../MapSection';
import DescriptionBox from './DescriptionBox';
import UploadsBox from './UploadsBox';

const { BASE_URL } = process.env;

Expand All @@ -19,6 +19,9 @@ const DroneOperatorDescriptionBox = () => {
const secondPageStates = useTypedSelector(state => state.droneOperatorTask);
const { secondPageState, secondPage } = secondPageStates;
const [animated, setAnimated] = useState(false);
const [showDownloadOptions, setShowDownloadOptions] =
useState<boolean>(false);
const { width } = useWindowDimensions();

const { data: taskDescription }: Record<string, any> =
useGetIndividualTaskQuery(taskId as string);
Expand Down Expand Up @@ -78,20 +81,6 @@ const DroneOperatorDescriptionBox = () => {
);
}
};
const dispatch = useTypedDispatch();

const headerTabOptions = [
{
id: 1,
label: 'Description',
value: 'description',
},
{
id: 2,
label: 'Uploads',
value: 'uploads',
},
];

const handleDownloadFlightPlan = () => {
fetch(
Expand Down Expand Up @@ -147,38 +136,48 @@ const DroneOperatorDescriptionBox = () => {
<p className="naxatw-text-[0.875rem] naxatw-font-normal naxatw-leading-normal naxatw-text-[#484848]">
Task #{taskDescription?.project_task_index}
</p>
<div className="naxatw-flex naxatw-gap-1">
<Button
variant="ghost"
className="naxatw-border naxatw-border-[#D73F3F] naxatw-text-[0.875rem] naxatw-text-[#D73F3F]"
leftIcon="download"
iconClassname="naxatw-text-[1.125rem]"
onClick={() => downloadGeojson()}
>
Download Waypoints Geojson
</Button>

<div className="naxatw-relative">
<Button
variant="ghost"
className="naxatw-border naxatw-border-[#D73F3F] naxatw-text-[0.875rem] naxatw-text-[#D73F3F]"
leftIcon="download"
iconClassname="naxatw-text-[1.125rem]"
onClick={() => handleDownloadFlightPlan()}
onClick={() => setShowDownloadOptions(prev => !prev)}
>
Download Flight Plan
Download
</Button>
{showDownloadOptions && (
<div className="naxatw-absolute naxatw-right-0 naxatw-top-10 naxatw-z-20 naxatw-w-[140px] naxatw-rounded-sm naxatw-border naxatw-bg-white naxatw-shadow-2xl">
<div
className="naxatw-cursor-pointer naxatw-px-3 naxatw-py-2 hover:naxatw-bg-redlight"
role="button"
tabIndex={0}
onKeyDown={() => handleDownloadFlightPlan()}
onClick={() => {
handleDownloadFlightPlan();
setShowDownloadOptions(false);
}}
>
Download flight plan
</div>
<div
className="naxatw-cursor-pointer naxatw-px-3 naxatw-py-2 hover:naxatw-bg-redlight"
role="button"
tabIndex={0}
onKeyDown={() => downloadGeojson()}
onClick={() => {
downloadGeojson();
setShowDownloadOptions(false);
}}
>
Download geojson
</div>
</div>
)}
</div>
</div>
<Tab
onTabChange={value => {
dispatch(setSecondPageState(value));
}}
tabOptions={headerTabOptions}
activeTab={secondPageState}
orientation="row"
className={`naxatw-h-[3rem] naxatw-border-b naxatw-bg-transparent hover:naxatw-border-b-2 hover:naxatw-border-red ${!secondPage ? 'naxatw-hidden' : 'naxatw-block'}`}
activeClassName="naxatw-border-b-2 naxatw-bg-transparent naxatw-border-red"
clickable
/>
{width < 640 && <MapSection />}
{renderComponent(secondPageState)}
</div>
</>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* eslint-disable no-param-reassign */
import { MapInstanceType } from '@Components/common/MapLibreComponents/types';
import { useEffect } from 'react';

interface IGetCoordinatesOnClick {
map?: MapInstanceType;
isMapLoaded?: Boolean;
getCoordinates: any;
}

const GetCoordinatesOnClick = ({
map,
isMapLoaded,
getCoordinates,
}: IGetCoordinatesOnClick) => {
useEffect(() => {
if (!map || !isMapLoaded) return () => {};
map.getCanvas().style.cursor = 'crosshair';
map.on('click', e => {
const latLng = e.lngLat;
getCoordinates(latLng);
});

return () => {
map.getCanvas().style.cursor = '';
};
}, [map, isMapLoaded, getCoordinates]);
return null;
};

export default GetCoordinatesOnClick;
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
interface IShowInfo {
heading?: string;
message: string;
wrapperClassName?: string;
className?: string;
}

const ShowInfo = ({
message,
className,
heading,
wrapperClassName,
}: IShowInfo) => {
return (
<div
className={`naxatw-absolute naxatw-left-[calc(50%-7.5rem)] naxatw-top-2 naxatw-z-10 naxatw-w-[15rem] naxatw-rounded-lg naxatw-bg-white naxatw-p-2 naxatw-shadow-xl ${wrapperClassName}`}
>
<div className="naxatw-flex naxatw-items-center naxatw-gap-1">
<i className="material-icons-outlined naxatw-text-base">info</i>{' '}
<h6 className="naxatw-text-base naxatw-font-semibold naxatw-text-gray-600">
{heading}
</h6>
</div>
<div className={`naxatw-text-xs naxatw-text-gray-500 ${className}`}>
{message}
</div>
</div>
);
};

export default ShowInfo;
Loading

0 comments on commit 92d153f

Please sign in to comment.