diff --git a/restapi/src/builders/locationBuilder.ts b/restapi/src/builders/locationBuilder.ts index 55bcfe4..deb69bc 100644 --- a/restapi/src/builders/locationBuilder.ts +++ b/restapi/src/builders/locationBuilder.ts @@ -16,10 +16,11 @@ export function thingToLocation(locationThing:Thing) : Location { latitude: getDecimal(locationThing, SCHEMA_INRUPT.latitude)!, longitude: getDecimal(locationThing, SCHEMA_INRUPT.longitude)!, isShared: false, - isOwnLocation: false + isOwnLocation: false, + owner:"" } } - +//TODO: aƱadir owner export function locationToThing(location:Location):Thing{ return buildThing(createThing()) .addStringNoLocale(SCHEMA_INRUPT.name, location.name) diff --git a/restapi/src/types.d.ts b/restapi/src/types.d.ts index cac0b87..b092363 100644 --- a/restapi/src/types.d.ts +++ b/restapi/src/types.d.ts @@ -7,14 +7,16 @@ export type Location = { latitude:number, longitude:number isShared:boolean, - isOwnLocation:boolean + isOwnLocation:boolean, + owner:string }; export type Review = { markerId:string, comment:string, score:double, - encodedPhoto:string + encodedPhoto:string, + owner:string } export type Friend = { diff --git a/webapp/src/components/FriendsPage/FriendsView.tsx b/webapp/src/components/FriendsPage/FriendsView.tsx index 86c324c..0d8684e 100644 --- a/webapp/src/components/FriendsPage/FriendsView.tsx +++ b/webapp/src/components/FriendsPage/FriendsView.tsx @@ -1,25 +1,23 @@ import React, { ReactNode } from 'react'; import { - Stack, - Container, - Box, - Flex, - Text, - Heading, - SimpleGrid, - Grid, - Table, - Thead, - Th, - Tr, - Tbody, - useColorModeValue, - FormControl, - Input, - FormLabel, - Button, - HStack, - Spinner, + Stack, + Container, + Box, + Text, + Heading, + Grid, + Table, + Thead, + Th, + Tr, + Tbody, + useColorModeValue, + FormControl, + Input, + FormLabel, + Button, + HStack, + Spinner, useToast, } from '@chakra-ui/react'; import {useGetFriendsQuery, useAddFriendMutation} from "../../app/services/Friend"; import {Friend} from '../../types'; @@ -29,6 +27,7 @@ export function AddFriendsView(){ let [addFriendMutation, {isLoading, isError, error}] = useAddFriendMutation(); let [webId, setWebId] = React.useState(''); let [nickName, setNickName] = React.useState(''); + const toast = useToast(); return ( @@ -38,27 +37,45 @@ export function AddFriendsView(){
{ - event.preventDefault(); - const newFriend: Friend = { - webId: webId, nickName: nickName, - loMapOnly: false, name: "", profilePic: "", - }; - const handleSubmit = (event: React.FormEvent) => { + if(webId.trim().length>0 && nickName.trim().length>0) { event.preventDefault(); - addFriendMutation(newFriend); - - }; - handleSubmit(event) - /* TODO: - Right now when we do this, the textfield is not restored so: - - visually there is something written - - You can submit the form because the is something written - - But the field that is sent is empty - - We need to restore the textfield someway or dont erase the content here - - setWebId(""); - setNickName(""); - */ + setWebId(''); + setNickName(''); + const newFriend: Friend = { + webId: webId, nickName: nickName, + loMapOnly: false, name: "", profilePic: "", + }; + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + addFriendMutation(newFriend); + if (!isError) { + toast({ + title: 'Friend Added', + description: "The friend has been added successfully.", + status: 'success', + duration: 7000, + isClosable: true, + }); + } else { + toast({ + title: 'Friend not added', + description: "There has been an error adding the friend.", + status: 'error', + duration: 7000, + isClosable: true, + }); + } + }; + handleSubmit(event) + } else { + toast({ + title: 'Fields not correctly filled', + description: "Please fill both fields before submitting.", + status: 'warning', + duration: 7000, + isClosable: true, + }); + } }}/> @@ -67,6 +84,7 @@ export function AddFriendsView(){ placeholder="asdfghjkl123456" _placeholder={{color: 'gray.500'}} type="text" + value={webId} onChange={(e) => setWebId(e.currentTarget.value)} /> @@ -78,6 +96,7 @@ export function AddFriendsView(){ placeholder="Motosarius" _placeholder={{color: 'gray.500'}} type="text" + value={nickName} onChange={(e) => setNickName(e.currentTarget.value)} /> @@ -90,9 +109,10 @@ export function AddFriendsView(){ _hover={{ bg: 'orange.500', }}> - { isLoading? : "Add" } + {isLoading ? + ( ) : + (

Add

)} - @@ -124,7 +144,7 @@ export default function FriendsView(): JSX.Element { Your friends {isLoading - ? (

Cargando amigos

) + ? (

Loading friends

) : ( diff --git a/webapp/src/components/Map.tsx b/webapp/src/components/Map.tsx index e0e2a25..a04990a 100644 --- a/webapp/src/components/Map.tsx +++ b/webapp/src/components/Map.tsx @@ -25,32 +25,34 @@ import { Text, useDisclosure, CardFooter, - ButtonGroup + ButtonGroup, Spinner, AlertIcon, AlertTitle, AlertDescription, Alert, useToast, createStandaloneToast } from '@chakra-ui/react'; import "../css/react_leaflet.css"; import 'leaflet/dist/leaflet.css'; import {Icon, LatLngExpression} from 'leaflet'; import markerIconPng from "leaflet/dist/images/marker-icon.png"; -import {useDispatch, useSelector} from 'react-redux'; +import {useDispatch} from 'react-redux'; import {useAddLocationMutation, useGetLocationsQuery} from "../app/services/Location"; import type {MapMarker} from '../types'; import {LocationType} from "../locationType"; import DetailsDrawer from './mapComponents/LocationReviewsView' + export function LocationMarkerWithStore() { - // const [position, setPosition] : LatLng = {lat: 0, lng: 0}; const dispatch = useDispatch(); const [lati, setLat] = useState(0); const [lngi, setLng] = useState(0); const {isOpen, onClose, onOpen} = useDisclosure(); let [addLocationMutation, {isLoading, isError, error}] = useAddLocationMutation(); + + const initialRef = React.useRef(null) var [name, setName] = React.useState('') var [category, setCategory] = React.useState('bar') - var [details, setDetails] = React.useState('') var [isShared, setShared] = React.useState(false) + const toast = useToast(); const map = useMapEvents({ click: (e) => { @@ -60,16 +62,28 @@ export function LocationMarkerWithStore() { onOpen(); setName('') setCategory('bar') - setDetails('') setShared(false) }, }) - + const checkAndClose = () => { + if(name.trim().length > 0 && category.trim().length > 0) { + onClose(); + } else { + toast({ + title: 'Marker not added', + description: "Locations must have a non blank name, please try again.", + status: 'warning', + duration: 7000, + isClosable: true, + }); + } + }; //Use .map to iterate and generate the corresponding markers //This need to be optimiced because I think it generates again //all the markers on top of each other return ( + <> @@ -77,31 +91,49 @@ export function LocationMarkerWithStore() { - { + if(name.trim().length > 0 && category.trim().length > 0) { event.preventDefault(); - const marker : MapMarker = {latitude : lati, longitude : lngi, - name: name,locationType: category as LocationType, id: lati + " - " + lngi,isShared: isShared}; + const marker: MapMarker = { + latitude: lati, + longitude: lngi, + name: name, + locationType: category as LocationType, + id: lati + " - " + lngi, + isShared: isShared, + owner: "", + isOwnLocation:true + }; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); addLocationMutation(marker); + toast({ + title: 'Marker Added', + description: "The location has been added successfully", + status: 'success', + duration: 3000, + isClosable: true, + }); }; handleSubmit(event) setName('') setCategory('bar') - setDetails('') setShared(false) - }}> + }}}> Name + setName(e.currentTarget.value)}/> setName(e.currentTarget.value)}/> Category - setCategory(e.currentTarget.value)}> @@ -111,7 +143,7 @@ export function LocationMarkerWithStore() { setShared(e.target.checked)}> + onChange={(e) => setShared(e.target.checked)}> Public @@ -119,11 +151,12 @@ export function LocationMarkerWithStore() { - + + ); } @@ -137,7 +170,8 @@ export default function MapElement(): JSX.Element { const [showShops, setShowShops] = useState(true) const [showSights, setShowSights] = useState(true) const [showMonuments, setShowMonuments] = useState(true) - const [showSharedLocations, setShowSharedLocations] = useState(true) + const [showMyLocations, setShowMyLocations] = useState(true) + const [showFriendLocations, setShowFriendLocations] = useState(true) return ( showShops} setShowShops={setShowShops} showSights={() => showSights} setShowSights={setShowSights} showMonuments={() => showMonuments} setShowMonuments={setShowMonuments} - showSharedLocations={() => showSharedLocations} setShowSharedLocations={setShowSharedLocations} + showMyLocations={() => showMyLocations} setShowMyLocations={setShowMyLocations} + showFriendLocations={() => showFriendLocations} setShowFriendLocations={setShowFriendLocations} /> @@ -161,10 +196,20 @@ export default function MapElement(): JSX.Element { - - {locations?.filter( loc => { - if(loc.isShared && showSharedLocations == false) return false + {isLoading + ? + ( + + Loading markers + + ) : + <> + ({locations?.filter( loc => { + if(loc.isOwnLocation && showMyLocations == false) return false + if(loc.isOwnLocation == false && showFriendLocations == false) return false if(loc.locationType == LocationType.restaurant && showRestaurants) return true if(loc.locationType == LocationType.bar && showBars) return true if(loc.locationType == LocationType.shop && showShops) return true @@ -183,10 +228,14 @@ export default function MapElement(): JSX.Element { latitude={location.latitude} longitude={location.longitude} isShared={location.isShared} + owner={location.owner} + isOwnLocation={location.isOwnLocation} /> - ))} + ))}) + + } @@ -204,8 +253,10 @@ interface FilterModalProps { setShowSights : React.Dispatch>, showMonuments : () => boolean, setShowMonuments : React.Dispatch>, - showSharedLocations : () => boolean, - setShowSharedLocations : React.Dispatch>, + showMyLocations : () => boolean, + setShowMyLocations : React.Dispatch>, + showFriendLocations : () => boolean, + setShowFriendLocations : React.Dispatch>, } export const FilterModal : FC = ( props ) : JSX.Element => { @@ -218,12 +269,13 @@ export const FilterModal : FC = ( props ) : JSX.Element => { { id: "shops", name: "Shops", value: props.showShops, onChange: props.setShowShops }, { id: "sights", name: "Sights", value: props.showSights, onChange: props.setShowSights }, { id: "monuments", name: "Monuments", value: props.showMonuments, onChange: props.setShowMonuments }, - { id: "sharedLocations", name: "Shared Locations", value: props.showSharedLocations, onChange: props.setShowSharedLocations } + { id: "myLocations", name: "My Locations", value: props.showMyLocations, onChange: props.setShowMyLocations }, + { id: "friendLocations", name: "Friend Locations", value: props.showFriendLocations, onChange: props.setShowFriendLocations } ] }; const propsChecked = (props.showBars() && props.showRestaurants() && props.showShops() - && props.showMonuments() && props.showSharedLocations() && props.showSights()); + && props.showMonuments() && props.showSights()); return (
@@ -268,38 +320,49 @@ export const FilterModal : FC = ( props ) : JSX.Element => { } export function PopupContent(marker: MapMarker){ + return( - - - } spacing='4' minWidth={'sm'}> - - - Name - - - {marker.name} - - - - - Category - - - {marker.locationType} - - - - - - - - - - + + + } spacing='4' minWidth={'sm'}> + + + Name + + + {marker.name} + + + + + Category + + + {marker.locationType} + + + + + Created by + + + {marker.owner} + + + + + + + + + + ) -} \ No newline at end of file +} diff --git a/webapp/src/components/mapComponents/LocationReviewsView.tsx b/webapp/src/components/mapComponents/LocationReviewsView.tsx index a167d79..d03946c 100644 --- a/webapp/src/components/mapComponents/LocationReviewsView.tsx +++ b/webapp/src/components/mapComponents/LocationReviewsView.tsx @@ -9,15 +9,32 @@ import { DrawerHeader, DrawerOverlay, FormControl, - FormLabel, Heading, Input, + FormLabel, + Heading, + Input, Modal, ModalBody, ModalCloseButton, - ModalContent, ModalFooter, + ModalContent, + ModalFooter, ModalHeader, ModalOverlay, - Stack, Text, Textarea, Image, - useDisclosure, Flex, DrawerFooter, Spacer, Popover, PopoverTrigger, PopoverContent, PopoverArrow, PopoverHeader, PopoverBody, PopoverCloseButton + Stack, + Text, + Textarea, + Image, + useDisclosure, + Flex, + DrawerFooter, + Spacer, + Popover, + PopoverTrigger, + PopoverContent, + PopoverArrow, + PopoverHeader, + PopoverBody, + PopoverCloseButton, + Spinner, createStandaloneToast } from "@chakra-ui/react"; import React, {ChangeEvent, useState} from "react"; // @ts-ignore @@ -51,17 +68,23 @@ export default function DetailsDrawer(marker: MapMarker) { -

Reviews

- - {reviews?.map( (review) => ( + {isLoading + ? (

Loading reviews

) : + ( + ({reviews?.map((review) => ( - {review.encodedPhoto!="" ? : <> } - {review.score >0 ? : <> } + {review.owner} + {review.encodedPhoto!="" ? + : <> } + {review.score >0 ? : <> } {review.comment.length != 0 ?{review.comment} : <> } ))} - +
) + }
@@ -71,7 +94,9 @@ export default function DetailsDrawer(marker: MapMarker) { id={marker.id} latitude={marker.latitude} longitude={marker.longitude} - isShared={marker.isShared}/> + isShared={marker.isShared} + owner={marker.owner} + isOwnLocation={marker.isOwnLocation}/> @@ -95,6 +120,10 @@ export function AddCommentForm(marker: MapMarker) { } }; + var [reviewAdded, setReviewAdded] = React.useState(false) + const { ToastContainer, toast } = createStandaloneToast() + const idToast = 'addedRevSuccess-Toast' + const resetAndOpen = () => { onOpen() setTextComment(""); @@ -109,8 +138,7 @@ export function AddCommentForm(marker: MapMarker) { - - + @@ -123,7 +151,7 @@ export function AddCommentForm(marker: MapMarker) {
{ event.preventDefault(); - const review : Review = {markerId:marker.id, comment:textComment, photo: file ,score:rating, encodedPhoto: ""}; + const review : Review = {markerId:marker.id, comment:textComment, photo: file ,score:rating, encodedPhoto: "", owner:""}; const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); @@ -132,6 +160,13 @@ export function AddCommentForm(marker: MapMarker) { .then(result => { review.encodedPhoto = result; addReviewMutation(review); + toast({ + title: 'Review Added', + description: "Your review has been added successfully", + status: 'success', + duration: 7000, + isClosable: true, + }); }) .catch(err => { console.log(err); @@ -154,10 +189,6 @@ export function AddCommentForm(marker: MapMarker) { handleSubmit(event) }}> - {(file.name != "") && - - } - or click to upload an image - + ) : + () + } Adding Reviews - While adding a review you can upload an image, add a score or/and add + While adding a review you can upload an image, add a score or/and add a text comment without or adding any of the other two but you must submit at least one. diff --git a/webapp/src/types.d.ts b/webapp/src/types.d.ts index bad4dfa..c06b6cd 100644 --- a/webapp/src/types.d.ts +++ b/webapp/src/types.d.ts @@ -6,15 +6,18 @@ export type MapMarker = { locationType:LocationType, latitude:number, longitude:number, - isShared: boolean + isShared: boolean, + owner: string, + isOwnLocation: boolean }; export type Review = { markerId:string, comment:string, photo:File, - score:double - encodedPhoto:string + score:double, + encodedPhoto:string, + owner:string }