From 3f6ebf23692269c756f0aa86c46314faf20973a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Poussard?= <tpoussard@neogeo.fr> Date: Mon, 19 Jul 2021 17:56:50 +0200 Subject: [PATCH] Reorganize base in App.js, with store & router and use login template --- package-lock.json | 19 +++- package.json | 3 +- src/App.vue | 149 +++++++++++++++++++++++++-- src/main.js | 2 + src/router/index.js | 19 ++-- src/store/index.js | 103 +++++++++++++++++++ src/views/Base.vue | 151 ---------------------------- src/{components => views}/Index.vue | 46 +++------ src/views/Login.vue | 92 +++++++++++++++++ 9 files changed, 380 insertions(+), 204 deletions(-) create mode 100644 src/store/index.js delete mode 100644 src/views/Base.vue rename src/{components => views}/Index.vue (75%) create mode 100644 src/views/Login.vue diff --git a/package-lock.json b/package-lock.json index b312f8d4..3782d704 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "geocontrib-pwa", + "name": "geocontrib-frontend", "version": "0.1.0", "lockfileVersion": 2, "requires": true, @@ -11,7 +11,8 @@ "core-js": "^3.6.5", "register-service-worker": "^1.7.1", "vue": "^2.6.11", - "vue-router": "^3.2.0" + "vue-router": "^3.2.0", + "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", @@ -14274,6 +14275,14 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "node_modules/vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "peerDependencies": { + "vue": "^2.0.0" + } + }, "node_modules/watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", @@ -27043,6 +27052,12 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "vuex": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz", + "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==", + "requires": {} + }, "watchpack": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.5.tgz", diff --git a/package.json b/package.json index 609cd047..17e4abf4 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "core-js": "^3.6.5", "register-service-worker": "^1.7.1", "vue": "^2.6.11", - "vue-router": "^3.2.0" + "vue-router": "^3.2.0", + "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", diff --git a/src/App.vue b/src/App.vue index 2bc8c171..748654ee 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,146 @@ <template> - <div id="app"> - <!-- <div id="nav"> - <router-link to="/">Home</router-link> | - <router-link to="/about">About</router-link> - </div> --> - <router-view/> - </div> + <body> + <header class="header-menu"> + <div class="ui container"> + <div class="ui inverted icon menu"> + <a href="" class="header item"> + <!-- <img class="ui mini right spaced image" :src="LOGO_PATH"> + <img class="ui mini right spaced image" :src="$process.env.VUE_APP_LOGO_PATH"> --> + <img + class="ui mini right spaced image" + src="@/assets/logo-neogeo-circle.png" + /> + {{ APPLICATION_NAME }} + </a> + + <div v-if="project" class="ui dropdown item"> + Projet : {{ project.title }} + <i class="dropdown icon"></i> + <div class="menu" style="z-index: 401"> + <a + class="item" + href="{% url 'geocontrib:project' slug=project.slug %}" + > + <i class="home icon"></i>Accueil + </a> + <a + class="item" + href="{% url 'geocontrib:feature_list' slug=project.slug %}" + > + <i class="list icon"></i>Liste & Carte + </a> + {% if project and permissions|lookup:'is_project_administrator' %} + <a + class="item" + href="{% url 'geocontrib:project_mapping' slug=project.slug %}" + > + <i class="map icon"></i>Fonds cartographiques + </a> + <a + class="item" + href="{% url 'geocontrib:project_members' slug=project.slug %}" + > + <i class="users icon"></i>Membres + </a> + {% endif %} + </div> + </div> + + <div class="right menu"> + <!-- {% if user.is_authenticated %} + <a class="item" href="{% url 'geocontrib:my_account' %}"> + {{ user.get_full_name|default:user.username }} + </a> + {% if project or user.is_administrator %} + <div class="item ui label"> + {% if project %}{{ USER_LEVEL_PROJECTS|lookup:project.slug }}<br>{% endif %} + {% if user.is_administrator == True %}Gestionnaire métier{% endif %} + </div> + {% endif %} + {% if not SSO_SETTED %} + <a class="item" href="{% url 'geocontrib:logout' %}"><i class="ui logout icon"></i></a> + {% endif %} + {% elif not SSO_SETTED %} --> + <router-link to="/connexion" class="item">Se Connecter</router-link> + <!-- {% endif %} --> + </div> + </div> + </div> + </header> + <main> + <div class="ui stackable grid centered container"> + <!-- {% if messages %} --> + <!-- <div v-if="false" class="row"> + <div class="fourteen wide column"> + {% for message in messages %} + {% if message.tags == 'success'%} + <div class="ui positive message"> + {% else %} + <div class="ui info message"> + {% endif %} + {% endfor %} + <div class="header"> + <i class="info circle icon"></i> Informations + </div> + <ul class="list"> + {% for message in messages %} + {{ message }} + {% endfor %} + </ul> + </div> + </div> + </div> --> + <!-- {% endif %} --> + + <!-- <SignIn v-if="!user"/> --> + <!-- <Index v-if="user" :user="user" :projects="projects" /> --> + + <!-- <Login v-if="logging" /> --> + <!-- <Index v-else /> --> + <router-view /> + </div> + </main> + + <footer> + <div class="ui compact text menu"> + <!-- // todo: ajouter liens --> + <a class="item" href="#"></a> + <a class="item" href="#"></a> + <p class="item">Version {{ PACKAGE_VERSION }}</p> + </div> + </footer> + </body> </template> +<script> +import { mapState } from "vuex"; + +export default { + name: "App", + computed: { + ...mapState(["logging", "projects"]), + LOGO_PATH: () => process.env.VUE_APP_LOGO_PATH, + APPLICATION_NAME: () => process.env.VUE_APP_APPLICATION_NAME, + PACKAGE_VERSION: () => process.env.PACKAGE_VERSION || "0", + }, + data() { + return { + user: null, + project: null, + }; + }, + created() { + this.$store.dispatch("GET_PROJECTS"); + this.$store.dispatch("GET_COOKIE", "csrftoken"); // * ne récupère plus le cookie arès avoir vidé le cache ?! + }, +}; +</script> +<style> +@import "./assets/styles/base.css"; +@import "./assets/resources/semantic-ui-2.4.2/semantic.min.css"; +.header-menu { + min-width: 560px; +} +</style> + \ No newline at end of file diff --git a/src/main.js b/src/main.js index 9f15cebd..d12df39b 100644 --- a/src/main.js +++ b/src/main.js @@ -2,10 +2,12 @@ import Vue from 'vue' import App from './App.vue' import './registerServiceWorker' import router from './router' +import store from './store' Vue.config.productionTip = false new Vue({ router, + store, render: h => h(App) }).$mount('#app') diff --git a/src/router/index.js b/src/router/index.js index 007c7be7..563b6ec9 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -1,23 +1,24 @@ import Vue from 'vue' import VueRouter from 'vue-router' -import Base from '../views/Base.vue' +import Index from '../views/Index.vue' +//import Login from '../views/Login.vue' Vue.use(VueRouter) const routes = [ { path: '/', - name: 'Base', - component: Base + name: 'Index', + component: Index }, - //{ - // path: '/mon-compte', - // name: 'My-account', + { + path: '/connexion', + name: 'Login', // route level code-splitting - // this generates a separate chunk (about.[hash].js) for this route + // this generates a separate chunk (login.[hash].js) for this route // which is lazy-loaded when the route is visited. - // component: () => import(/* webpackChunkName: "about" */ '../views/My-account.vue') - //} + component: () => import(/* webpackChunkName: "login" */ '../views/Login.vue') + } ] const router = new VueRouter({ diff --git a/src/store/index.js b/src/store/index.js new file mode 100644 index 00000000..45412bf6 --- /dev/null +++ b/src/store/index.js @@ -0,0 +1,103 @@ +const axios = require("axios"); + +import Vue from 'vue'; +import Vuex from 'vuex'; +import router from '../router' +//import modules from './modules'; + +Vue.use(Vuex); + +const GET_PROJECT_USER = GET_PROJECT_USER; +const GET_PROJECTS = GET_PROJECTS; +const CHECK_LOGIN = CHECK_LOGIN; +const SET_COOKIE = SET_COOKIE; + + +export default new Vuex.Store({ + // modules, + state: { + cookie: null, + logged: false, + project: null, + projectMembers: null, + projects: [], + }, + + mutations: { + SET_PROJECTS(state, projects) { + state.projects = projects; + }, + SET_PROJECT(state, project) { + state.project = project; + }, + SET_PROJECT_MEMBERS(state, projectMembers) { + state.projectMembers = projectMembers; + }, + SET_LOGGED(state, payload) { + state.logged = payload; + }, + SET_COOKIE(state, cookie) { + state.cookie = cookie + } + }, + + actions: { + GET_PROJECTS({ commit }) { + axios + .get("http://localhost:8000/api/projects/") + .then((response) => (commit("SET_PROJECTS", response.data))) + .catch((error) => { + throw error; + }); + }, + GET_PROJECT({ commit }, project_slug) { + axios + .get(`http://localhost:8000/api/projet/${project_slug}/project`) + .then((response) => (commit("SET_PROJECT", response.data))) + .catch((error) => { + throw error; + }); + }, + GET_PROJECT_USER({ commit }, project_slug) { + axios + .get(`http://localhost:8000/api/projet/${project_slug}/utilisateurs`) + .then((response) => (commit("SET_PROJECT_MEMBERS", response.data.members))) + .catch((error) => { + throw error; + }); + }, + CHECK_LOGIN({ commit }, payload) { + if (payload.username && payload.password) { + axios + .post("http://localhost:8000/api/login/", { + username: payload.username, + password: payload.password, + }) + .then(() => (commit("SET_LOGGED", true), router.push("/"))) + .catch((error) => { + throw error; + }); + } + console.log("router", router) + }, + GET_COOKIE({ commit }, name) { + let cookieValue = null; + + if (document.cookie && document.cookie !== "") { + const cookies = document.cookie.split(";"); + for (let i = 0; i < cookies.length; i++) { + const cookie = cookies[i].trim(); + + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) === name + "=") { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + + break; + } + } + } + commit("SET_COOKIE", cookieValue); + }, + } + +}); diff --git a/src/views/Base.vue b/src/views/Base.vue deleted file mode 100644 index 99d3ca8f..00000000 --- a/src/views/Base.vue +++ /dev/null @@ -1,151 +0,0 @@ -<template> -<body> - <header class="header-menu"> - <div class="ui container"> - <div class="ui inverted icon menu"> - <a href="" class="header item"> - <!-- <img class="ui mini right spaced image" :src="LOGO_PATH"> - <img class="ui mini right spaced image" :src="$process.env.VUE_APP_LOGO_PATH"> --> - <img class="ui mini right spaced image" src="@/assets/logo-neogeo-circle.png"> - {{ APPLICATION_NAME }} - </a> - - <div v-if="project" class="ui dropdown item"> - Projet : {{ project.title }} - <i class="dropdown icon"></i> - <div class="menu" style="z-index:401;"> - <a class="item" href="{% url 'geocontrib:project' slug=project.slug %}"> - <i class="home icon"></i>Accueil - </a> - <a class="item" href="{% url 'geocontrib:feature_list' slug=project.slug %}"> - <i class="list icon"></i>Liste & Carte - </a> - {% if project and permissions|lookup:'is_project_administrator' %} - <a class="item" href="{% url 'geocontrib:project_mapping' slug=project.slug %}"> - <i class="map icon"></i>Fonds cartographiques - </a> - <a class="item" href="{% url 'geocontrib:project_members' slug=project.slug %}"> - <i class="users icon"></i>Membres - </a> - {% endif %} - </div> - </div> - - <div class="right menu"> - <!-- {% if user.is_authenticated %} - <a class="item" href="{% url 'geocontrib:my_account' %}"> - {{ user.get_full_name|default:user.username }} - </a> - {% if project or user.is_administrator %} - <div class="item ui label"> - {% if project %}{{ USER_LEVEL_PROJECTS|lookup:project.slug }}<br>{% endif %} - {% if user.is_administrator == True %}Gestionnaire métier{% endif %} - </div> - {% endif %} - {% if not SSO_SETTED %} - <a class="item" href="{% url 'geocontrib:logout' %}"><i class="ui logout icon"></i></a> - {% endif %} - {% elif not SSO_SETTED %} - <a class="item" href="{% url 'geocontrib:login' %}">Se Connecter</a> - {% endif %} --> - </div> - </div> - </div> - </header> - <main> - <div class="ui stackable grid centered container"> - <!-- {% if messages %} --> - <!-- <div v-if="false" class="row"> - <div class="fourteen wide column"> - {% for message in messages %} - {% if message.tags == 'success'%} - <div class="ui positive message"> - {% else %} - <div class="ui info message"> - {% endif %} - {% endfor %} - <div class="header"> - <i class="info circle icon"></i> Informations - </div> - <ul class="list"> - {% for message in messages %} - {{ message }} - {% endfor %} - </ul> - </div> - </div> - </div> --> - <!-- {% endif %} --> - - <Index :user="user" :projects="projects"/> - </div> - </main> - - <footer> - <div class="ui compact text menu"> - <a class="item" href="{% url 'geocontrib:legal' %}">Mentions légales</a> - <a class="item" href="{% url 'geocontrib:help' %}">Aide</a> - <p class="item">Version {{PACKAGE_VERSION}}</p> - </div> - </footer> - -</body> - -</template> - -<script> -//const LOGO_PATH = require(process.env.VUE_APP_LOGO_PATH); -const axios = require("axios"); -import Index from "@/components/Index.vue"; - - -export default { - name: "App", - components: { - Index: Index - }, - computed: { - LOGO_PATH: () => process.env.VUE_APP_LOGO_PATH, - APPLICATION_NAME: () => process.env.VUE_APP_APPLICATION_NAME, - PACKAGE_VERSION: () => process.env.PACKAGE_VERSION || '0', - }, - data() { - return { - user: null, - project: null, - projects: null, - } - }, - created() { - //this.getUser() - this.getProjects(); - }, - methods: { - getUser() { - axios.get("http://localhost:8000/api/projet/1-vuetification/utilisateurs") - .then(response => this.user = response.data.members) - .catch(error => { - throw(error) - }) - }, - getProjects() { - axios - .get("http://localhost:8000/api/projects/") - // .then((response) => (this.projects = response.data.projects)) - .then((response) => (this.projects = response.data)) - .catch((error) => { - throw(error); - }); - }, - }, -}; -</script> - -<style> - @import '../assets/styles/base.css'; - @import '../assets/resources/semantic-ui-2.4.2/semantic.min.css'; - .header-menu { - min-width: 560px; -} -</style> - \ No newline at end of file diff --git a/src/components/Index.vue b/src/views/Index.vue similarity index 75% rename from src/components/Index.vue rename to src/views/Index.vue index 25974163..6cd320ac 100644 --- a/src/components/Index.vue +++ b/src/views/Index.vue @@ -15,15 +15,11 @@ <h4 id="les_projets" class="ui horizontal divider header">PROJETS</h4> <!-- {% if can_create_project %} --> - <a - class="ui green basic button" - href="{% url 'geocontrib:project_create' %}" + <a class="ui green basic button" href="#" ><!-- //todo : get base url and add the rest --> <i class="plus icon"></i> Créer un nouveau projet </a> - <a - class="ui blue basic button right floated" - href="{% url 'geocontrib:project_type_list' %}" + <a class="ui blue basic button right floated" href="#" ><!-- //todo : get base url and add the rest --> <i class="copy icon"></i> Accéder à la liste des modèles de projets </a> @@ -31,14 +27,12 @@ <div v-if="projects" class="ui divided items"> <div v-for="project in projects" class="item" :key="project.slug"> <div class="ui tiny image"> + <!-- // todo: récupérer l'image sur serveur front (et non back) --> <img :src="project.thumbnail" /> + <!-- // todo: récupérer l'image par défaut --> </div> <div class="middle aligned content"> - <a - class="header" - href="{% url 'geocontrib:project' slug=project.slug %}" - >{{ project.title }}</a - ><!-- //todo : get base url and add the slug --> + <a class="header" @click="open_project(project.slug)">{{ project.title }}</a> <div class="description"> <p>{{ project.description }}</p> </div> @@ -54,7 +48,7 @@ <span> Mon niveau d'autorisation : <!-- //todo: get this value --> - {{ USER_LEVEL_PROJECTS|lookup:project.slug }} + <!-- {{ USER_LEVEL_PROJECTS|lookup:project.slug }} --> <!-- //todo: get user --> {{ user && user.is_administrator == True @@ -92,37 +86,21 @@ </template> <script> -//const axios = require("axios"); +import { mapState } from "vuex"; export default { name: "Index", - /* data() { - return { - projects: null, - }; - }, */ - props: ["user", "projects"], computed: { + ...mapState(["projects", "user"]), LOGO_PATH: () => process.env.VUE_APP_LOGO_PATH, APPLICATION_NAME: () => process.env.VUE_APP_APPLICATION_NAME, APPLICATION_ABSTRACT: () => process.env.VUE_APP_APPLICATION_ABSTRACT, }, - created() { - // this.getProjects(); - }, methods: { - /* getProjects() { - axios - .get("http://localhost:8000/api/projects/") - .then((response) => (this.projects = response.data.projects)) - .catch((error) => { - console.log(error); - }); - }, */ - /* toLocaleDate(dateStr) { - const dateObj = new Date(dateStr); - return dateObj.toLocaleDateString(); - } */ + open_project(project_slug) { + console.log("open",project_slug); + this.$store.dispatch("GET_PROJECT", project_slug) + } }, }; </script> \ No newline at end of file diff --git a/src/views/Login.vue b/src/views/Login.vue new file mode 100644 index 00000000..a23831ba --- /dev/null +++ b/src/views/Login.vue @@ -0,0 +1,92 @@ +<template> + <div> + <div class="row"> + <div class="fourteen wide column"> + <img + class="ui centered small image" + src="@/assets/logo-neogeo-circle.png" + /> + <h2 class="ui center aligned icon header"> + <div class="content"> + {{ APPLICATION_NAME }} + <div class="sub header">{{ APPLICATION_ABSTRACT }}</div> + </div> + </h2> + </div> + </div> + <div class="row"> + <div class="six wide column"> + <h3 class="ui horizontal divider header">CONNEXION</h3> + + <div v-if="form.errors" class="ui warning message"> + <div class="header"> + Les informations d'identification sont incorrectes. + </div> + NB: Seuls les comptes actifs peuvent se connecter. + </div> + + <form class="ui form" role="form" type="post" @submit.prevent="login"> + <!-- {% csrf_token %} --> + <input name="csrfmiddlewaretoken" :value="$store.csrftoken" type="hidden" /> + + <div class="ui stacked secondary segment"> + <div class="six field required"> + <div class="ui left icon input"> + <i class="user icon"></i> + <input + v-model="username_value" + type="text" + name="username" + placeholder="Utilisateur" + /> + </div> + </div> + <div class="six field required"> + <div class="ui left icon input"> + <i class="lock icon"></i> + <input + v-model="password_value" + type="password" + name="password" + placeholder="Mot de passe" + /> + </div> + </div> + <button class="ui fluid large teal submit button" type="submit"> + Login + </button> + </div> + </form> + </div> + </div> + </div> +</template> + +<script> +export default { + name: "Login", + data() { + return { + username_value: null, + password_value: null, + logged: false, + form: { + errors: null, + }, + }; + }, + computed: { + LOGO_PATH: () => process.env.VUE_APP_LOGO_PATH, + APPLICATION_NAME: () => process.env.VUE_APP_APPLICATION_NAME, + APPLICATION_ABSTRACT: () => process.env.VUE_APP_APPLICATION_ABSTRACT, + }, + methods: { + login() { + this.$store.dispatch("CHECK_LOGIN", { + username: this.username_value, + password: this.password_value, + }); + }, + }, +}; +</script> \ No newline at end of file -- GitLab