🎉 init project

pull/1/head
Alexis Feron 7 months ago
parent 3c24963884
commit 3685155a1d

11
.gitignore vendored

@ -22,6 +22,16 @@ yarn-error.log*
lerna-debug.log* lerna-debug.log*
.pnpm-debug.log* .pnpm-debug.log*
.DS_Store
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Diagnostic reports (https://nodejs.org/api/report.html) # Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
@ -93,6 +103,7 @@ web_modules/
.env.test.local .env.test.local
.env.production.local .env.production.local
.env.local .env.local
.env.*.local
# parcel-bundler cache (https://parceljs.org/) # parcel-bundler cache (https://parceljs.org/)
.cache .cache

@ -1,3 +1,27 @@
# front # Memory Map
Front du projet MemoryMap Front du projet MemoryMap
## Project setup
```
npm install
```
### Compiles and hot-reloads for development
```
npm run serve
```
### Compiles and minifies for production
```
npm run build
```
### Lints and fixes files
```
npm run lint
```

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

12450
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
{
"name": "memory_map",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.8.3",
"flowbite": "^2.5.1",
"leaflet": "^1.9.4",
"vue": "^3.2.13"
},
"devDependencies": {
"@babel/core": "^7.12.16",
"@babel/eslint-parser": "^7.12.16",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"autoprefixer": "^10.4.20",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.12"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/vue3-essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "@babel/eslint-parser"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead",
"not ie 11"
]
}

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<title>Memory Map</title>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

@ -0,0 +1,19 @@
<template>
<div class="container mx-auto h-screen">
<NavBar />
<LeafletMap />
</div>
</template>
<script>
import LeafletMap from "./components/LeafletMap.vue";
import NavBar from "./components/NavBar.vue";
export default {
name: "App",
components: {
NavBar,
LeafletMap,
},
};
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

@ -0,0 +1,75 @@
<template>
<div class="map-container h-[calc(100vh_-_72px)]">
<div id="map" class="h-full w-full"></div>
</div>
</template>
<script>
import L from "leaflet";
import "leaflet/dist/leaflet.css";
import { createApp, h, onMounted } from "vue";
import { monuments } from "../data/stub";
import MonumentMarker from "./MonumentMarker.vue";
export default {
name: "LeafletMap",
setup() {
onMounted(() => {
// Initialiser la carte
const map = L.map("map").setView([46.603354, 1.888334], 6); // TODO: A modifier pour centrer par rapport aux points de la carte
// Supprimer les attributions de la carte Leaflet
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "",
}).addTo(map);
map.attributionControl.setPrefix("");
// Définir une fonction pour créer des icônes avec Heroicons
const createDivIcon = (htmlContent) => {
return L.divIcon({
html: htmlContent,
className: "text-2xl text-blue-500", // Utilise les classes CSS de Flowbite ou Tailwind pour styliser les icônes
iconSize: [24, 24], // Taille approximative du conteneur de l'icône
iconAnchor: [12, 24], // Point d'ancrage de l'icône (centre-bas)
popupAnchor: [0, -24], // Point d'ancrage du popup (juste au-dessus du marqueur)
});
};
// Définir les icônes Heroicons
const visitedIcon = createDivIcon(`
<svg class="w-6 h-6 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M11.906 1.994a8.002 8.002 0 0 1 8.09 8.421 7.996 7.996 0 0 1-1.297 3.957.996.996 0 0 1-.133.204l-.108.129c-.178.243-.37.477-.573.699l-5.112 6.224a1 1 0 0 1-1.545 0L5.982 15.26l-.002-.002a18.146 18.146 0 0 1-.309-.38l-.133-.163a.999.999 0 0 1-.13-.202 7.995 7.995 0 0 1 6.498-12.518ZM15 9.997a3 3 0 1 1-5.999 0 3 3 0 0 1 5.999 0Z" clip-rule="evenodd"/>
</svg>
`);
const notVisitedIcon = createDivIcon(`
<svg class="w-[24px] h-[24px] text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-width="3" d="M11.083 5.104c.35-.8 1.485-.8 1.834 0l1.752 4.022a1 1 0 0 0 .84.597l4.463.342c.9.069 1.255 1.2.556 1.771l-3.33 2.723a1 1 0 0 0-.337 1.016l1.03 4.119c.214.858-.71 1.552-1.474 1.106l-3.913-2.281a1 1 0 0 0-1.008 0L7.583 20.8c-.764.446-1.688-.248-1.474-1.106l1.03-4.119A1 1 0 0 0 6.8 14.56l-3.33-2.723c-.698-.571-.342-1.702.557-1.771l4.462-.342a1 1 0 0 0 .84-.597l1.753-4.022Z"/>
</svg>
`);
// Ajouter les marqueurs pour chaque monument
monuments.forEach((monument) => {
// Choisir l'icône en fonction de la propriété `visited`
const icon = monument.visited ? visitedIcon : notVisitedIcon;
// Ajouter un marqueur avec l'icône appropriée
const marker = L.marker(monument.coords, { icon }).addTo(map);
// Créer un conteneur DOM pour monter le composant Vue
const popupContainer = document.createElement("div");
// Monter le composant Vue MonumentMarker sur le conteneur
const app = createApp({
render() {
return h(MonumentMarker, { monument });
},
});
app.mount(popupContainer);
// Attacher le popup au marqueur avec le contenu monté
marker.bindPopup(popupContainer);
});
});
},
};
</script>

@ -0,0 +1,130 @@
<template>
<div class="text-center">
<strong>{{ monument.name }}</strong>
<div
class="relative carousel overflow-hidden"
v-if="monument.images.length > 0"
>
<!-- Carousel wrapper -->
<div
class="relative h-40 mt-2 overflow-hidden rounded-lg flex items-center justify-center"
>
<div
v-for="(image, index) in monument.images"
:key="index"
:class="[
'absolute inset-0 transition-opacity duration-700 ease-in-out',
{
'opacity-100': index === currentIndex,
'opacity-0': index !== currentIndex,
},
]"
>
<img
:src="image"
:alt="monument.name"
class="object-contain max-h-full max-w-full h-full w-auto mx-auto"
/>
</div>
</div>
<!-- Slider controls -->
<div v-if="monument.images.length > 1">
<button
type="button"
class="absolute top-0 left-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
@click="prevSlide"
>
<span
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
>
<svg
class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 6 10"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M5 1 1 5l4 4"
/>
</svg>
<span class="sr-only">Previous</span>
</span>
</button>
<button
type="button"
class="absolute top-0 right-0 z-30 flex items-center justify-center h-full cursor-pointer group focus:outline-none"
@click="nextSlide"
>
<span
class="inline-flex items-center justify-center w-8 h-8 rounded-full bg-black/30 dark:bg-black-800/30 group-hover:bg-black/50 dark:group-hover:bg-black-800/60"
>
<svg
class="w-4 h-4 text-white dark:text-gray-800 rtl:rotate-180"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 6 10"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 9l4-4-4-4"
/>
</svg>
<span class="sr-only">Next</span>
</span>
</button>
</div>
</div>
<p v-html="formattedDescription"></p>
</div>
</template>
<script>
export default {
name: "MonumentMarker",
props: {
monument: {
type: Object,
required: true,
},
},
data() {
return {
currentIndex: 0,
};
},
computed: {
formattedDescription() {
return this.formatDescription(this.monument.description);
},
},
methods: {
// Remplace les mentions d'utilisateurs par des liens cliquables
formatDescription(description) {
const regex = /@(\w+(-\w+)*(\.\w+(-\w+)*)*)/g;
return description.replace(
regex,
'<a href="/profile/$1" class="text-blue-500 hover:underline">@$1</a>'
);
},
prevSlide() {
this.currentIndex =
(this.currentIndex - 1 + this.monument.images.length) %
this.monument.images.length;
},
nextSlide() {
this.currentIndex = (this.currentIndex + 1) % this.monument.images.length;
},
},
// mounted() {
// setInterval(this.nextSlide, 3000);
// }
};
</script>

@ -0,0 +1,152 @@
<template>
<nav class="bg-white border-gray-200 dark:bg-gray-900">
<div
class="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4"
>
<a class="flex items-center space-x-3 rtl:space-x-reverse">
<img src="../assets/logo.png" class="h-10" alt="Memory Map Logo" />
<span
class="self-center text-2xl font-semibold whitespace-nowrap dark:text-white"
>Memory Map</span
>
</a>
<div class="flex lg:order-2">
<button
type="button"
data-collapse-toggle="navbar-search"
aria-controls="navbar-search"
aria-expanded="false"
class="lg:hidden text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none focus:ring-4 focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-2.5 me-1"
>
<svg
class="w-5 h-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</svg>
<span class="sr-only">Search</span>
</button>
<div class="relative hidden lg:block">
<div
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
>
<svg
class="w-4 h-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</svg>
<span class="sr-only">Search icon</span>
</div>
<input
type="text"
id="search-navbar"
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Search..."
/>
</div>
<button
data-collapse-toggle="navbar-search"
type="button"
class="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
aria-controls="navbar-search"
aria-expanded="false"
>
<span class="sr-only">Open main menu</span>
<svg
class="w-5 h-5"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 17 14"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M1 1h15M1 7h15M1 13h15"
/>
</svg>
</button>
</div>
<div
class="items-center justify-between hidden w-full lg:flex lg:w-auto lg:order-1"
id="navbar-search"
>
<div class="relative mt-3 lg:hidden">
<div
class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none"
>
<svg
class="w-4 h-4 text-gray-500 dark:text-gray-400"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 20"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"
/>
</svg>
</div>
<input
type="text"
id="search-navbar"
class="block w-full p-2 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-blue-500 focus:border-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
placeholder="Search..."
/>
</div>
<ul
class="flex flex-col p-4 lg:p-0 mt-4 font-medium border border-gray-100 rounded-lg bg-gray-50 lg:space-x-8 rtl:space-x-reverse lg:flex-row lg:mt-0 lg:border-0 lg:bg-white dark:bg-gray-800 lg:dark:bg-gray-900 dark:border-gray-700"
>
<li class="flex items-center space-x-2">
<a
href="#"
class="block py-2 px-3 text-white bg-blue-700 rounded md:bg-transparent md:text-blue-700 md:p-0 dark:text-white md:dark:text-blue-500"
aria-current="page"
>Home</a
>
</li>
<li class="flex items-center space-x-2">
<a href="#" class="block py-2 text-gray-900 dark:text-white"
>Quests</a
>
</li>
<li class="flex items-center space-x-2">
<a href="#" class="block py-2 text-gray-900 dark:text-white"
>Add a pin</a
>
</li>
<li class="flex items-center space-x-2">
<a href="#" class="block py-2 text-gray-900 dark:text-white"
>Friends</a
>
</li>
</ul>
</div>
</div>
</nav>
</template>

@ -0,0 +1,30 @@
export const monuments = [
{
coords: [48.85837, 2.294481],
name: "Tour Eiffel",
images: [
"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQGGib245tSFiK1Qcx0cB0dZsoVyJElwsY3kA&s",
"https://encrypted-tbn2.gstatic.com/licensed-image?q=tbn:ANd9GcTLB9B0j50rJbcSbdja9_hySHS6_KATbhTK_iCeWeNKtA92hTmTX5nTW3udjjovZrnU1JxqAjMS_VqHnMwHGhTs35-sU-7B29_X_T3uLV8",
],
description: "Visité en 2020 avec la famille, un moment inoubliable.",
visited: true,
},
{
coords: [43.296482, 5.36978],
name: "Vieux Port de Marseille",
images: [
"https://upload.wikimedia.org/wikipedia/commons/thumb/7/74/Marseille_Old_Port.jpg/390px-Marseille_Old_Port.jpg",
],
description:
"Découvert lors d'un week-end ensoleillé en 2019 avec @John-Doe.",
visited: true,
},
{
coords: [48.636063, -1.511457],
name: "Mont Saint-Michel",
images: [],
description: "",
visited: false,
},
// ...
];

@ -0,0 +1,6 @@
import "flowbite";
import { createApp } from "vue";
import App from "./App.vue";
import "./assets/main.css";
createApp(App).mount("#app");

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
"./node_modules/flowbite/**/*.js",
],
theme: {
extend: {},
},
plugins: [require("flowbite/plugin")],
};

@ -0,0 +1,5 @@
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
transpileDependencies: true,
});
Loading…
Cancel
Save