✨ Add & display loves on map
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { getDBConnexion } from '../utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
export const GET = async (req: NextRequest) => {
|
||||
const userID = req.headers.get('Authorization');
|
||||
|
||||
if (!userID) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pool = getDBConnexion();
|
||||
|
||||
const { rowCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "user" WHERE "id"=$1 LIMIT 1;`,
|
||||
[userID]
|
||||
);
|
||||
if (rowCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { rows } = await pool.query(
|
||||
`SELECT * FROM "point" WHERE "userid"=$1;`,
|
||||
[userID]
|
||||
);
|
||||
|
||||
await pool.end();
|
||||
|
||||
return NextResponse.json(rows);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Internal error',
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const POST = async (req: NextRequest) => {
|
||||
const userID = req.headers.get('Authorization');
|
||||
|
||||
if (!userID) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pool = getDBConnexion();
|
||||
|
||||
const { rowCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "user" WHERE "id"=$1 LIMIT 1;`,
|
||||
[userID]
|
||||
);
|
||||
if (rowCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { geopoint, location, comment } = await req.json();
|
||||
|
||||
if (!geopoint || !location) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message:
|
||||
'Missing geopoint and/or location field(s) in request body',
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const pointId = v4();
|
||||
|
||||
await pool.query(
|
||||
'INSERT INTO "point" ("id", "userid", "geopoint", "location", "comment") VALUES ($1, $2, $3, $4, $5);',
|
||||
[pointId, userID, geopoint, location, comment]
|
||||
);
|
||||
|
||||
await pool.end();
|
||||
|
||||
return NextResponse.json({
|
||||
message: 'Successfully added point',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Internal error',
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const PUT = async (req: NextRequest) => {
|
||||
const userID = req.headers.get('Authorization');
|
||||
|
||||
if (!userID) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pool = getDBConnexion();
|
||||
|
||||
const { rowCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "user" WHERE "id"=$1 LIMIT 1;`,
|
||||
[userID]
|
||||
);
|
||||
if (rowCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { pointId, geopoint, location, comment } = await req.json();
|
||||
|
||||
if (!pointId || !geopoint || !location) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message:
|
||||
'Missing pointId and/or geopoint and/or location field(s) in request body',
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { rowCount: pointsCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "point" WHERE "id"=$1 AND "userid"=$2 LIMIT 1;`,
|
||||
[pointId, userID]
|
||||
);
|
||||
|
||||
if (pointsCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Point not found',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await pool.query(
|
||||
'UPDATE "point" SET "geopoint"=$1, "location"=$2, "comment"=$3 WHERE "id"=$4 AND "userid"=$5;',
|
||||
[geopoint, location, comment, pointId, userID]
|
||||
);
|
||||
|
||||
await pool.end();
|
||||
|
||||
return NextResponse.json({
|
||||
message: 'Successfully updated point',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Internal error',
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const DELETE = async (req: NextRequest) => {
|
||||
const userID = req.headers.get('Authorization');
|
||||
|
||||
if (!userID) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const pool = getDBConnexion();
|
||||
|
||||
const { rowCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "user" WHERE "id"=$1 LIMIT 1;`,
|
||||
[userID]
|
||||
);
|
||||
if (rowCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Unauthorized request',
|
||||
},
|
||||
{
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { pointId } = await req.json();
|
||||
|
||||
if (!pointId) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Missing pointId field in request body',
|
||||
},
|
||||
{
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const { rowCount: pointsCount } = await pool.query(
|
||||
`SELECT COUNT(*) FROM "point" WHERE "id"=$1 AND "userid"=$2 LIMIT 1;`,
|
||||
[pointId, userID]
|
||||
);
|
||||
|
||||
if (pointsCount != 1) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Point not found',
|
||||
},
|
||||
{
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await pool.query('DELETE FROM "point" WHERE "id"=$1 AND "userid"=$2;', [
|
||||
pointId,
|
||||
userID,
|
||||
]);
|
||||
|
||||
await pool.end();
|
||||
|
||||
return NextResponse.json({
|
||||
message: 'Successfully delete point',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return NextResponse.json(
|
||||
{
|
||||
error: true,
|
||||
message: 'Internal error',
|
||||
},
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,75 @@
|
||||
import { FormEvent } from 'react';
|
||||
|
||||
export const useActions = ({
|
||||
viewState,
|
||||
setIsAddLoveOpened,
|
||||
setIsLoading,
|
||||
setAddLoveMessage,
|
||||
}) => {
|
||||
const toggleLoveMenu = () => {
|
||||
setIsAddLoveOpened((prev) => !prev);
|
||||
};
|
||||
|
||||
const addPoint = async (ev: FormEvent<HTMLFormElement>) => {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!ev.currentTarget.reportValidity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const formData = new FormData(ev.currentTarget);
|
||||
const location = formData.get('location');
|
||||
const comment = formData.get('comment');
|
||||
|
||||
const body: { [key: string]: any } = {
|
||||
location: location.toString(),
|
||||
};
|
||||
|
||||
if (comment && comment.toString().length > 0) {
|
||||
body.comment = comment.toString();
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('token');
|
||||
|
||||
const { latitude, longitude } = viewState;
|
||||
|
||||
const point = {
|
||||
type: 'Point',
|
||||
coordinates: [longitude, latitude],
|
||||
};
|
||||
|
||||
body.geopoint = point;
|
||||
|
||||
try {
|
||||
const request = await fetch('/api/point', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: token,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if (request.status == 403) {
|
||||
setIsLoading(false);
|
||||
setAddLoveMessage('Reconnectez-vous pour réessayer');
|
||||
return;
|
||||
}
|
||||
if (request.status == 400) {
|
||||
setIsLoading(false);
|
||||
setAddLoveMessage('Erreur interne. Veuillez réessayer');
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
setAddLoveMessage('Erreur interne. Veuillez réessayer');
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return { toggleLoveMenu, addPoint };
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export const useData = () => {
|
||||
const [canUseGeolocation, setCanUseGeolocation] = useState(false);
|
||||
const [geoError, setGeoError] = useState(null);
|
||||
|
||||
const [initialState, setInitialState] = useState({
|
||||
longitude: 2.209666999999996,
|
||||
latitude: 46.232192999999995,
|
||||
zoom: 5,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if ('geolocation' in navigator == false) {
|
||||
setCanUseGeolocation(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setCanUseGeolocation(true);
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
position => {
|
||||
setInitialState(prev => ({
|
||||
...prev,
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
}));
|
||||
},
|
||||
error => {
|
||||
if (error.PERMISSION_DENIED) {
|
||||
setGeoError('permission_denied');
|
||||
console.warn(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
initialState,
|
||||
geoError,
|
||||
canUseGeolocation,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,119 @@
|
||||
import { IconLayer } from 'deck.gl';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { StringifiedPinIcon } from '@/assets/PinIcon';
|
||||
|
||||
export const useData = () => {
|
||||
const deckRef = useRef(null);
|
||||
|
||||
const [canUseGeolocation, setCanUseGeolocation] = useState(false);
|
||||
const [geoError, setGeoError] = useState(null);
|
||||
|
||||
const [layers, setLayers] = useState([]);
|
||||
|
||||
const [isAddLoveOpened, setIsAddLoveOpened] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [addLoveMessage, setAddLoveMessage] = useState(null);
|
||||
|
||||
const [viewState, setViewState] = useState<any>({
|
||||
longitude: 2.209666999999996,
|
||||
latitude: 46.232192999999995,
|
||||
zoom: 5,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if ('geolocation' in navigator == false) {
|
||||
setCanUseGeolocation(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setCanUseGeolocation(true);
|
||||
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
setViewState((prev) => ({
|
||||
...prev,
|
||||
latitude: position.coords.latitude,
|
||||
longitude: position.coords.longitude,
|
||||
}));
|
||||
},
|
||||
(error) => {
|
||||
if (error.PERMISSION_DENIED) {
|
||||
setGeoError('permission_denied');
|
||||
console.warn(error.message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
(async () => {
|
||||
const dataset = await getDataset();
|
||||
|
||||
const datasetLayer = new IconLayer({
|
||||
id: 'lovePoints',
|
||||
data: dataset,
|
||||
iconAtlas: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(
|
||||
StringifiedPinIcon()
|
||||
)}`,
|
||||
iconMapping: {
|
||||
marker: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 100,
|
||||
anchorY: 100,
|
||||
},
|
||||
},
|
||||
getIcon: (d) => 'marker',
|
||||
getSize: (d) => 35,
|
||||
getPosition: (d) => d.geometry.coordinates,
|
||||
billboard: true,
|
||||
pickable: true,
|
||||
});
|
||||
|
||||
setLayers([datasetLayer]);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
return {
|
||||
deckRef,
|
||||
viewState,
|
||||
setViewState,
|
||||
geoError,
|
||||
layers,
|
||||
canUseGeolocation,
|
||||
isAddLoveOpened,
|
||||
setIsAddLoveOpened,
|
||||
isLoading,
|
||||
setIsLoading,
|
||||
addLoveMessage,
|
||||
setAddLoveMessage,
|
||||
};
|
||||
};
|
||||
|
||||
const getDataset = async () => {
|
||||
const token = localStorage.getItem('token');
|
||||
const request = await fetch('/api/point', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
Authorization: token,
|
||||
},
|
||||
});
|
||||
|
||||
const response = await request.json();
|
||||
|
||||
if (response) {
|
||||
const formattedData = response.map((point) => {
|
||||
const properties = { ...point };
|
||||
delete properties.geopoint;
|
||||
|
||||
return {
|
||||
type: 'Feature',
|
||||
geometry: point.geopoint,
|
||||
properties: properties,
|
||||
};
|
||||
});
|
||||
|
||||
return formattedData;
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
+99
-6
@@ -1,25 +1,52 @@
|
||||
'use client';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// Imports
|
||||
//! Imports
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// --------------------------------------------------- Components ------------------------------------------------------
|
||||
import { DeckGL } from 'deck.gl';
|
||||
import { Map } from 'react-map-gl';
|
||||
import Image from 'next/image';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// -------------------------------------------------- Hooks & Utils ----------------------------------------------------
|
||||
import { useData } from './hooks/useData';
|
||||
import { useActions } from './hooks/useActions';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
// ------------------------------------------------- Assets & Styles ---------------------------------------------------
|
||||
import HeartIcon from '@/assets/HeartIcon';
|
||||
import Input from '@/components/Input/Input';
|
||||
import CloseIcon from '@/assets/CloseIcon';
|
||||
import AddIcon from '@/assets/AddIcon';
|
||||
import pin from '@/assets/pin.png';
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
import './styles.scss';
|
||||
import Loader from '@/assets/Loader';
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
const Home = () => {
|
||||
const { canUseGeolocation, geoError, initialState } = useData();
|
||||
const {
|
||||
deckRef,
|
||||
viewState,
|
||||
setViewState,
|
||||
geoError,
|
||||
canUseGeolocation,
|
||||
layers,
|
||||
isAddLoveOpened,
|
||||
setIsAddLoveOpened,
|
||||
isLoading,
|
||||
setIsLoading,
|
||||
addLoveMessage,
|
||||
setAddLoveMessage,
|
||||
} = useData();
|
||||
|
||||
const { toggleLoveMenu, addPoint } = useActions({
|
||||
viewState,
|
||||
setIsAddLoveOpened,
|
||||
setIsLoading,
|
||||
setAddLoveMessage,
|
||||
});
|
||||
|
||||
if (!canUseGeolocation && !geoError) {
|
||||
return <p className='appMessage'>Chargement...</p>;
|
||||
@@ -35,10 +62,13 @@ const Home = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='page'>
|
||||
<div className='mapContainer'>
|
||||
<DeckGL
|
||||
initialViewState={initialState}
|
||||
layers={[]}
|
||||
ref={deckRef}
|
||||
initialViewState={viewState}
|
||||
onViewStateChange={(e) => setViewState(e.viewState)}
|
||||
layers={layers}
|
||||
controller
|
||||
useDevicePixels={false}
|
||||
getCursor={({ isDragging }) =>
|
||||
@@ -52,15 +82,78 @@ const Home = () => {
|
||||
height: '100%',
|
||||
}}
|
||||
mapboxAccessToken={process.env.MAPBOX_TOKEN}
|
||||
mapStyle={'mapbox://styles/mapbox/standard'}
|
||||
mapStyle={
|
||||
'mapbox://styles/unepicier/cm1p8yuvq00qx01r24vg4f3ej'
|
||||
}
|
||||
preserveDrawingBuffer
|
||||
></Map>
|
||||
</DeckGL>
|
||||
{isAddLoveOpened && (
|
||||
<Image
|
||||
src={pin}
|
||||
alt='pin'
|
||||
width={30}
|
||||
className='pinImage'
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<button className='dropLove'>
|
||||
{isAddLoveOpened && (
|
||||
<div className='addLoveContainer'>
|
||||
<p className='addLoveHeader'>Ajouter un love ici</p>
|
||||
|
||||
<form
|
||||
className='addLoveForm'
|
||||
onSubmit={addPoint}
|
||||
>
|
||||
<Input
|
||||
type='text'
|
||||
name='location'
|
||||
placeholder="C'était où ?"
|
||||
autoComplete='off'
|
||||
required
|
||||
/>
|
||||
<Input
|
||||
type='text'
|
||||
name='comment'
|
||||
placeholder='Un commentaire'
|
||||
autoComplete='off'
|
||||
/>
|
||||
|
||||
<div className='buttonsContainer'>
|
||||
<button
|
||||
type='button'
|
||||
className='button'
|
||||
onClick={toggleLoveMenu}
|
||||
disabled={isLoading}
|
||||
>
|
||||
<CloseIcon />
|
||||
Annuler
|
||||
</button>
|
||||
|
||||
<button
|
||||
type='submit'
|
||||
className='button'
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? <Loader /> : <AddIcon />}
|
||||
Ajouter
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isAddLoveOpened && (
|
||||
<button
|
||||
type='button'
|
||||
className='dropLove'
|
||||
onClick={toggleLoveMenu}
|
||||
>
|
||||
<HeartIcon />
|
||||
Placer un love
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+61
-1
@@ -1,6 +1,20 @@
|
||||
.mapContainer {
|
||||
.page {
|
||||
position: relative;
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > .mapContainer {
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
|
||||
& > .pinImage {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
& > .dropLove {
|
||||
z-index: 1;
|
||||
@@ -18,6 +32,52 @@
|
||||
background-color: #d94253;
|
||||
color: #ffffff;
|
||||
box-shadow: 0 2px 6px rgba(255, 255, 255, 0.6);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& > .addLoveContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding-bottom: 40px;
|
||||
|
||||
& > .addLoveHeader {
|
||||
padding: 10px;
|
||||
background-color: #d94253;
|
||||
font-size: 1.1rem;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
& > .addLoveForm {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 5px 10px;
|
||||
|
||||
& > .buttonsContainer {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
|
||||
& > .button {
|
||||
display: grid;
|
||||
grid-template-columns: 20px max-content;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
padding: 5px 15px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
background-color: #d94253;
|
||||
font-size: 1rem;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { SVGProps } from 'react';
|
||||
|
||||
const AddIcon = (props: SVGProps<SVGSVGElement>) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
viewBox='0 0 512 512'
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='32'
|
||||
d='M256 112v288M400 256H112'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddIcon;
|
||||
+1
-1
@@ -10,7 +10,7 @@ const Loader = (props?: SVGProps<SVGSVGElement>) => {
|
||||
>
|
||||
<g
|
||||
fill='none'
|
||||
fill-rule='evenodd'
|
||||
fillRule='evenodd'
|
||||
>
|
||||
<g
|
||||
transform='translate(1 1)'
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
const PinIcon = (props) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
version='1.1'
|
||||
x='0px'
|
||||
y='0px'
|
||||
viewBox='0 0 100 100'
|
||||
{...props}
|
||||
>
|
||||
<g>
|
||||
<path d='M82.1,34.9C80.9,19.2,68.1,6.5,52.3,5.4C33.5,4.1,17.8,19,17.8,37.6c0,6.7,2,12.9,5.5,18c4.2,6.2,16.2,23.7,22.6,33.1 c2,2.9,6.3,2.9,8.2,0c6.4-9.3,18.4-26.9,22.6-33.1C80.7,49.8,82.7,42.6,82.1,34.9z M50,56c-10.2,0-18.5-8.3-18.5-18.5 S39.8,19,50,19c10.2,0,18.5,8.3,18.5,18.5S60.2,56,50,56z' />
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default PinIcon;
|
||||
|
||||
export const StringifiedPinIcon = () => {
|
||||
return `
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
version='1.1'
|
||||
width='100'
|
||||
height='100'
|
||||
viewBox='0 0 100 100'
|
||||
fill='#d94253'
|
||||
>
|
||||
<g>
|
||||
<path
|
||||
d='M82.1,34.9C80.9,19.2,68.1,6.5,52.3,5.4C33.5,4.1,17.8,19,17.8,37.6c0,6.7,2,12.9,5.5,18c4.2,6.2,16.2,23.7,22.6,33.1 c2,2.9,6.3,2.9,8.2,0c6.4-9.3,18.4-26.9,22.6-33.1C80.7,49.8,82.7,42.6,82.1,34.9z M50,56c-10.2,0-18.5-8.3-18.5-18.5 S39.8,19,50,19c10.2,0,18.5,8.3,18.5,18.5S60.2,56,50,56z'
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
`;
|
||||
};
|
||||
Generated
+15
-1
@@ -19,7 +19,8 @@
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-map-gl": "^7.1.7",
|
||||
"react-slot-counter": "^3.0.1"
|
||||
"react-slot-counter": "^3.0.1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
@@ -8430,6 +8431,19 @@
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
||||
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vt-pbf": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
|
||||
|
||||
+2
-1
@@ -20,7 +20,8 @@
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-map-gl": "^7.1.7",
|
||||
"react-slot-counter": "^3.0.1"
|
||||
"react-slot-counter": "^3.0.1",
|
||||
"uuid": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.2",
|
||||
|
||||
Reference in New Issue
Block a user