File dump from old repo
This commit is contained in:
33
.eslintrc.json
Normal file
33
.eslintrc.json
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/vue3-recommended",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"indent": ["error", 2],
|
||||
"accessor-pairs": "warn",
|
||||
"array-callback-return": "error",
|
||||
"brace-style": "warn",
|
||||
"consistent-return": "error",
|
||||
"default-case": "error",
|
||||
"eqeqeq": "warn",
|
||||
"no-case-declarations": "error",
|
||||
"no-empty-pattern": "warn",
|
||||
"no-fallthrough": "error",
|
||||
"no-unused-vars": "error",
|
||||
"no-useless-catch": "error",
|
||||
"no-var": "error",
|
||||
"semi": "warn",
|
||||
"no-extra-semi": "error",
|
||||
"yoda": "warn"
|
||||
}
|
||||
}
|
||||
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
],
|
||||
};
|
||||
13025
package-lock.json
generated
Normal file
13025
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
59
package.json
Normal file
59
package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "spellsaurus_client",
|
||||
"version": "1.0.0",
|
||||
"description": "The client interface of spellsaurus",
|
||||
"keywords": [
|
||||
"rpg",
|
||||
"dev",
|
||||
"spells",
|
||||
"roleplay",
|
||||
"website",
|
||||
"database"
|
||||
],
|
||||
"author": "AlexisNP",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/AlexisNP/spellsaurus/issues"
|
||||
},
|
||||
"homepage": "https://github.com/AlexisNP/spellsaurus#readme",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/AlexisNP/spellsaurus.git"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "vue-cli-service lint",
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^0.21.1",
|
||||
"bootstrap": "^4.5.0",
|
||||
"bootstrap-vue": "^2.15.0",
|
||||
"core-js": "^3.6.4",
|
||||
"jquery": "^3.5.1",
|
||||
"popper.js": "^1.16.1",
|
||||
"v-clipboard": "^2.2.3",
|
||||
"vue": "^2.6.11",
|
||||
"vue-cookies": "^1.7.0",
|
||||
"vue-masonry": "^0.11.8",
|
||||
"vue-meta": "^2.4.0",
|
||||
"vuex": "^3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "~4.2.0",
|
||||
"@vue/cli-plugin-eslint": "~4.2.0",
|
||||
"@vue/cli-service": "~4.2.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-vue": "^7.0.0-beta.4",
|
||||
"node-sass": "^4.14.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"vue-router": "^3.1.6",
|
||||
"vue-template-compiler": "^2.6.11"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.1 KiB |
BIN
public/favicon.png
Normal file
BIN
public/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
18
public/index.html
Normal file
18
public/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<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">
|
||||
<link rel="preload" as="font" href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,400;0,700;1,400;1,700&display=swap" crossorigin="anonymous">
|
||||
<link rel="preload" as="font" href="https://fonts.googleapis.com/css?family=Playfair+Display:400,400i,500,500i,600,600i,700,700i,800,800i,900,900i&subset=cyrillic,latin-ext" crossorigin="anonymous">
|
||||
<title>Auracle</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="srs"></div>
|
||||
</body>
|
||||
</html>
|
||||
9
src/api/api.js
Normal file
9
src/api/api.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import axios from "axios";
|
||||
|
||||
const url = process.env.VUE_APP_API_URL;
|
||||
|
||||
axios.defaults.headers.common[process.env.VUE_APP_API_KEY_NAME] = process.env.VUE_APP_API_KEY_VALUE;
|
||||
|
||||
export default axios.create({
|
||||
baseURL: url
|
||||
});
|
||||
12
src/api/ingredientsRepository.js
Normal file
12
src/api/ingredientsRepository.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import api from './api';
|
||||
|
||||
const resource = "/ingredients";
|
||||
|
||||
export default {
|
||||
getAll() {
|
||||
return api.get(`${resource}`);
|
||||
},
|
||||
getOne(id) {
|
||||
return api.get(`${resource}/${id}`);
|
||||
},
|
||||
};
|
||||
12
src/api/metaschoolsRepository.js
Normal file
12
src/api/metaschoolsRepository.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import api from './api';
|
||||
|
||||
const resource = "/meta_schools";
|
||||
|
||||
export default {
|
||||
getAll() {
|
||||
return api.get(`${resource}`);
|
||||
},
|
||||
getOne(id) {
|
||||
return api.get(`${resource}/${id}`);
|
||||
},
|
||||
};
|
||||
21
src/api/repositories.js
Normal file
21
src/api/repositories.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import spellsRepository from './spellsRepository';
|
||||
import schoolsRepository from './schoolsRepository';
|
||||
import metaschoolsRepository from './metaschoolsRepository';
|
||||
import variablesRepository from './variablesRepository';
|
||||
import ingredientsRepository from './ingredientsRepository';
|
||||
import usersRepository from './usersRepository';
|
||||
|
||||
// List of possible repositories
|
||||
const repositories = {
|
||||
spells: spellsRepository,
|
||||
schools: schoolsRepository,
|
||||
metaschools: metaschoolsRepository,
|
||||
variables: variablesRepository,
|
||||
ingredients: ingredientsRepository,
|
||||
users: usersRepository,
|
||||
};
|
||||
|
||||
// Usage : RepositoryFactoryInstance.get('reponame');
|
||||
export const RepositoryFactory = {
|
||||
get: name => repositories[name]
|
||||
};
|
||||
15
src/api/schoolsRepository.js
Normal file
15
src/api/schoolsRepository.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import api from './api';
|
||||
|
||||
const resource = "/schools";
|
||||
|
||||
export default {
|
||||
getAll() {
|
||||
return api.get(`${resource}`);
|
||||
},
|
||||
getOne(id) {
|
||||
return api.get(`${resource}/${id}`);
|
||||
},
|
||||
getSpellsFromOne(id) {
|
||||
return api.get(`${resource}/${id}/spells`);
|
||||
}
|
||||
};
|
||||
24
src/api/spellsRepository.js
Normal file
24
src/api/spellsRepository.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import api from './api';
|
||||
|
||||
const resource = "/spells";
|
||||
|
||||
export default {
|
||||
getAll() {
|
||||
return api.get(`${resource}`);
|
||||
},
|
||||
getPage(page) {
|
||||
return api.get(`${resource}/page/${page}`);
|
||||
},
|
||||
getOne(id) {
|
||||
return api.get(`${resource}/${id}`);
|
||||
},
|
||||
addOne(data) {
|
||||
return api.post(`${resource}`, data);
|
||||
},
|
||||
updateOne(id, data) {
|
||||
return api.put(`${resource}/${id}`, data);
|
||||
},
|
||||
deleteOne(id) {
|
||||
return api.delete(`${resource}/${id}`);
|
||||
}
|
||||
};
|
||||
18
src/api/usersRepository.js
Normal file
18
src/api/usersRepository.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import api from './api'
|
||||
|
||||
const resource = "/users"
|
||||
|
||||
export default {
|
||||
getOneFromUUID(uuid) {
|
||||
return api.get(`${resource}/${uuid}`,)
|
||||
},
|
||||
checkEmailAvailable(mail) {
|
||||
return api.get(`${resource}/available/${mail}`)
|
||||
},
|
||||
login(data) {
|
||||
return api.post(`${resource}/login`, data)
|
||||
},
|
||||
register(data) {
|
||||
return api.post(`${resource}`, data)
|
||||
},
|
||||
}
|
||||
12
src/api/variablesRepository.js
Normal file
12
src/api/variablesRepository.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import api from './api';
|
||||
|
||||
const resource = "/variables";
|
||||
|
||||
export default {
|
||||
getAll() {
|
||||
return api.get(`${resource}`);
|
||||
},
|
||||
getOne(id) {
|
||||
return api.get(`${resource}/${id}`);
|
||||
},
|
||||
};
|
||||
25
src/app.vue
Normal file
25
src/app.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div id="srs">
|
||||
<header>
|
||||
<navbar />
|
||||
</header>
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
metaInfo: {
|
||||
title: 'Auracle',
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
#srs {
|
||||
padding-top: 50px;
|
||||
min-height: 100vh;
|
||||
background: url('./assets/images/bg1.png') center center fixed repeat;
|
||||
}
|
||||
</style>
|
||||
BIN
src/assets/fonts/material-design-icons.woff2
Normal file
BIN
src/assets/fonts/material-design-icons.woff2
Normal file
Binary file not shown.
BIN
src/assets/images/bg1.png
Normal file
BIN
src/assets/images/bg1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 228 KiB |
36
src/assets/scss/_fonts.scss
Normal file
36
src/assets/scss/_fonts.scss
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
.font-display{ font-family: 'Playfair Display', serif; }
|
||||
.font-weight-100{ font-weight: 100; }
|
||||
.font-weight-200{ font-weight: 200; }
|
||||
.font-weight-300{ font-weight: 300; }
|
||||
.font-weight-400{ font-weight: 400; }
|
||||
.font-weight-500{ font-weight: 500; }
|
||||
.font-weight-600{ font-weight: 600; }
|
||||
.font-weight-700{ font-weight: 700; }
|
||||
.font-weight-800{ font-weight: 800; }
|
||||
.font-weight-900{ font-weight: 900; }
|
||||
|
||||
.line-height-100{ line-height: 100%; }
|
||||
|
||||
@font-face{
|
||||
font-family: 'Material Icons';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url('../fonts/material-design-icons.woff2') format('woff2');
|
||||
}
|
||||
.mad {
|
||||
font-family: 'Material Icons';
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
line-height: 1;
|
||||
transform: translateY(12.5%);
|
||||
letter-spacing: normal;
|
||||
text-transform: none;
|
||||
display: inline-block;
|
||||
white-space: nowrap;
|
||||
word-wrap: normal;
|
||||
direction: ltr;
|
||||
-webkit-font-feature-settings: 'liga';
|
||||
font-feature-settings: 'liga';
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
68
src/assets/scss/_global.scss
Normal file
68
src/assets/scss/_global.scss
Normal file
@@ -0,0 +1,68 @@
|
||||
// RESETS
|
||||
html {
|
||||
font-size: $font-size-root;
|
||||
font-family: $font-family !important;
|
||||
height: 100%;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
line-height: 1.15;
|
||||
color: $primary-base;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
box-sizing: border-box;
|
||||
&:before, &:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@at-root {
|
||||
@-ms-viewport { width: device-width; }
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
var {
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: none;
|
||||
border-top-style: solid;
|
||||
border-top-width: 1px;
|
||||
border-top-color: rgba($black, .25);
|
||||
}
|
||||
|
||||
// BOOTSTRAP OVERRIDES
|
||||
.nav-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.word-break{ word-wrap: break-word; }
|
||||
.cursor-pointer{ cursor: pointer; }
|
||||
|
||||
.font-weight-black {
|
||||
font-weight: $heavy;
|
||||
}
|
||||
|
||||
// UTILITIES
|
||||
.prewrap {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
// LAYOUTS
|
||||
.fullpage {
|
||||
min-height: calc(100vh - 56px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
31
src/assets/scss/_variables.scss
Normal file
31
src/assets/scss/_variables.scss
Normal file
@@ -0,0 +1,31 @@
|
||||
//// COLOURS
|
||||
// Global colours
|
||||
$white: #FFF;
|
||||
$black: #000;
|
||||
$dull-black: #0D0D0D;
|
||||
|
||||
$primary-base: #2c3e50;
|
||||
$primary-blue: #599EF4;
|
||||
$secondary-blue: #355F91;
|
||||
|
||||
$valid: #6AC47B;
|
||||
$warning: #F7A758;
|
||||
$alert: #F44E4E;
|
||||
|
||||
// FONT FAMILY BASES
|
||||
$font-family: 'Lato', sans-serif !default;
|
||||
|
||||
$light: 300;
|
||||
$regular: 400;
|
||||
$bold: 700;
|
||||
$heavy: 900;
|
||||
|
||||
$font-size-root: 16px !default;
|
||||
$font-size-base: 1rem !default;
|
||||
$line-height-base: 1.3 !default;
|
||||
|
||||
// Breakpoints
|
||||
$bp-sm: 576px;
|
||||
$bp-md: 768px;
|
||||
$bp-lg: 992px;
|
||||
$bp-xl: 1200px;
|
||||
110
src/components/global/loader.vue
Normal file
110
src/components/global/loader.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<div class="lds-roller">
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
<div />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Loader',
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.lds-roller {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
div {
|
||||
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||
transform-origin: 40px 40px;
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--primary);
|
||||
margin: -4px 0 0 -4px;
|
||||
}
|
||||
&:nth-child(1) {
|
||||
animation-delay: -0.036s;
|
||||
&:after {
|
||||
top: 63px;
|
||||
left: 63px;
|
||||
}
|
||||
}
|
||||
&:nth-child(2) {
|
||||
animation-delay: -0.072s;
|
||||
&:after {
|
||||
top: 68px;
|
||||
left: 56px;
|
||||
}
|
||||
}
|
||||
&:nth-child(3) {
|
||||
animation-delay: -0.108s;
|
||||
&:after {
|
||||
top: 71px;
|
||||
left: 48px;
|
||||
}
|
||||
}
|
||||
&:nth-child(4) {
|
||||
animation-delay: -0.144s;
|
||||
&:after {
|
||||
top: 72px;
|
||||
left: 40px;
|
||||
}
|
||||
}
|
||||
&:nth-child(5) {
|
||||
animation-delay: -0.18s;
|
||||
&:after {
|
||||
top: 71px;
|
||||
left: 32px;
|
||||
}
|
||||
}
|
||||
&:nth-child(6) {
|
||||
animation-delay: -0.216s;
|
||||
&:after {
|
||||
top: 68px;
|
||||
left: 24px;
|
||||
}
|
||||
}
|
||||
&:nth-child(7) {
|
||||
animation-delay: -0.252s;
|
||||
&:after {
|
||||
top: 63px;
|
||||
left: 17px;
|
||||
}
|
||||
}
|
||||
&:nth-child(8) {
|
||||
animation-delay: -0.288s;
|
||||
&:after {
|
||||
top: 56px;
|
||||
left: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes lds-roller {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
110
src/components/global/navbar/navbar.vue
Normal file
110
src/components/global/navbar/navbar.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template>
|
||||
<nav class="navbar navbar-expand-sm fixed-top navbar-dark bg-dark">
|
||||
<router-link
|
||||
:to="'/'"
|
||||
class="navbar-brand font-display font-weight-700"
|
||||
>
|
||||
Auracle
|
||||
</router-link>
|
||||
<button
|
||||
class="navbar-toggler"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#navbar"
|
||||
aria-controls="navbar"
|
||||
aria-expanded="false"
|
||||
aria-label="Toggle navigation"
|
||||
>
|
||||
<span class="navbar-toggler-icon" />
|
||||
</button>
|
||||
<div
|
||||
id="navbar"
|
||||
class="collapse navbar-collapse"
|
||||
>
|
||||
<ul
|
||||
v-if="links.length != 0"
|
||||
class="navbar-nav mr-auto"
|
||||
>
|
||||
<li
|
||||
v-for="(link, index) in links"
|
||||
:key="index"
|
||||
class="nav-item"
|
||||
>
|
||||
<router-link
|
||||
:to="link.url"
|
||||
class="nav-link"
|
||||
>
|
||||
{{ link.text }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
v-if="user"
|
||||
class="navbar-nav"
|
||||
>
|
||||
<router-link
|
||||
:to="'/profil'"
|
||||
class="nav-link"
|
||||
>
|
||||
{{ user.name }}
|
||||
</router-link>
|
||||
<div
|
||||
class="nav-link"
|
||||
@click="logoutUser()"
|
||||
>
|
||||
Deconnexion
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="navbar-nav"
|
||||
>
|
||||
<router-link
|
||||
:to="'/connexion'"
|
||||
class="nav-link"
|
||||
>
|
||||
Connexion
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Navbar',
|
||||
data() {
|
||||
return {
|
||||
links: [
|
||||
{
|
||||
text: 'Sortilèges',
|
||||
url: '/sorts',
|
||||
},
|
||||
{
|
||||
text: 'Écoles',
|
||||
url: '/ecoles',
|
||||
},
|
||||
{
|
||||
text: 'Chronologie',
|
||||
url: '/ages',
|
||||
}
|
||||
]
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.getUserProfile;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
logoutUser() {
|
||||
this.$store.dispatch('user_logout');
|
||||
this.$router.push('/');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
302
src/components/spells/add-spell-card.vue
Normal file
302
src/components/spells/add-spell-card.vue
Normal file
@@ -0,0 +1,302 @@
|
||||
<template>
|
||||
<b-modal
|
||||
ref="add_spell_modal"
|
||||
size="lg"
|
||||
modal-class="b-modal"
|
||||
>
|
||||
<template #modal-header="{ close }">
|
||||
<div
|
||||
id="spell_show_edit_modal"
|
||||
class="h1 modal-title font-display font-weight-bold"
|
||||
>
|
||||
<div class="line-height-100">
|
||||
<span v-if="!spell.name">Nouveau sort</span>
|
||||
<span v-if="spell.name">{{ spell.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
@click="close()"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<form
|
||||
id="add-spell"
|
||||
@submit="addSpell"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_name"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Nom :</label>
|
||||
<input
|
||||
id="spell_name"
|
||||
v-model="spell.name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="spell_name"
|
||||
placeholder="(256 caractères max.)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_description"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Description :</label>
|
||||
<textarea
|
||||
id="spell_description"
|
||||
v-model="spell.description"
|
||||
class="form-control"
|
||||
name="spell_description"
|
||||
placeholder="(2048 caractères max.)"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="spell_ritual"
|
||||
v-model="spell.is_ritual"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
name="spell_ritual"
|
||||
>
|
||||
<label
|
||||
for="spell_ritual"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Rituel ? </label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_level"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Niveau :</label>
|
||||
<input
|
||||
id="spell_level"
|
||||
v-model="spell.level"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="spell_level"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
placeholder="(Nombre entier de 0 à 100)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_schools"
|
||||
class="font-weight-bold col-form-label"
|
||||
>École(s) :</label>
|
||||
<select
|
||||
id="spell_schools"
|
||||
v-model="spell.schools"
|
||||
class="form-control"
|
||||
name="spell_schools"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(school, index) in all_schools"
|
||||
:key="index"
|
||||
:value="'school_' + school.id"
|
||||
>
|
||||
{{ school.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_charge"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Charge :</label>
|
||||
<input
|
||||
id="spell_charge"
|
||||
v-model="spell.charge"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="spell_charge"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
placeholder="(Nombre entier de 0 à 100)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_ingredients"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Ingrédient(s) :</label>
|
||||
<select
|
||||
id="spell_ingredients"
|
||||
v-model="spell.ingredients"
|
||||
class="form-control"
|
||||
name="spell_ingredients"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(ingredient,index) in all_ingredients"
|
||||
:key="index"
|
||||
:value="'ingredient_' + ingredient.id"
|
||||
>
|
||||
{{ ingredient.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_cost"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Coût :</label>
|
||||
<input
|
||||
id="spell_cost"
|
||||
v-model="spell.cost"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="spell_cost"
|
||||
placeholder="(32 caractères max.)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_variables"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Variable(s) :</label>
|
||||
<select
|
||||
id="spell_variables"
|
||||
v-model="spell.variables"
|
||||
class="form-control"
|
||||
name="spell_variables"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(variable,index) in all_variables"
|
||||
:key="index"
|
||||
:value="'variable_' + variable.id"
|
||||
>
|
||||
{{ variable.description }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template #modal-footer="{ close }">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
data-dismiss="modal"
|
||||
@click="close()"
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
<input
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
value="Enregistrer"
|
||||
form="add-spell"
|
||||
>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
|
||||
const Spells = RepositoryFactory.get('spells');
|
||||
const Schools = RepositoryFactory.get('schools');
|
||||
const Variables = RepositoryFactory.get('variables');
|
||||
const Ingredients = RepositoryFactory.get('ingredients');
|
||||
|
||||
export default {
|
||||
'name': 'AddSpellCard',
|
||||
data() {
|
||||
return {
|
||||
spell: {
|
||||
type: Object,
|
||||
name: "",
|
||||
description: "",
|
||||
is_ritual: false,
|
||||
level: 0,
|
||||
cost: "0",
|
||||
charge: 0,
|
||||
schools: [],
|
||||
variables: [],
|
||||
ingredients: [],
|
||||
},
|
||||
all_schools: [],
|
||||
all_variables: [],
|
||||
all_ingredients: [],
|
||||
};
|
||||
},
|
||||
created() {
|
||||
// Gets all relevant info for multiple selects
|
||||
let fetchSchools = Schools.getAll();
|
||||
let fetchVariables = Variables.getAll();
|
||||
let fetchIngredients = Ingredients.getAll();
|
||||
|
||||
Promise.all([fetchSchools, fetchVariables, fetchIngredients])
|
||||
.then(v => {
|
||||
this.all_schools = v[0].data;
|
||||
this.all_variables = v[1].data;
|
||||
this.all_ingredients = v[2].data;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$refs["add_spell_modal"].show();
|
||||
this.$root.$on('bv::modal::hide', () => {
|
||||
this.$emit('cancelAdd');
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
addSpell(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let schoolsData = Object.values(this.spell.schools).map(v => {
|
||||
return parseInt(v.slice(7));
|
||||
});
|
||||
let variablesData = Object.values(this.spell.variables).map(v => {
|
||||
return parseInt(v.slice(9));
|
||||
});
|
||||
let ingredientsData = Object.values(this.spell.ingredients).map(v => {
|
||||
return parseInt(v.slice(11));
|
||||
});
|
||||
|
||||
let data = {
|
||||
name: this.spell.name,
|
||||
description: this.spell.description,
|
||||
is_ritual: !!+this.spell.is_ritual,
|
||||
level: parseInt(this.spell.level),
|
||||
cost: this.spell.cost,
|
||||
charge: parseInt(this.spell.charge),
|
||||
schools: schoolsData,
|
||||
variables: variablesData,
|
||||
ingredients: ingredientsData,
|
||||
};
|
||||
|
||||
Spells.addOne(data)
|
||||
.then(v => {
|
||||
this.$emit('addSpell', v.data);
|
||||
this.$refs["add_spell_modal"].hide();
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
textarea, select {
|
||||
min-height: 10rem;
|
||||
max-height: 20rem;
|
||||
}
|
||||
</style>
|
||||
346
src/components/spells/edit-spell-card.vue
Normal file
346
src/components/spells/edit-spell-card.vue
Normal file
@@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<b-modal
|
||||
ref="edit_spell_modal"
|
||||
size="lg"
|
||||
modal-class="b-modal"
|
||||
:spell="computeSpell"
|
||||
>
|
||||
<template #modal-header="{ close }">
|
||||
<div
|
||||
id="spell_show_edit_modal"
|
||||
class="h1 modal-title font-display font-weight-bold"
|
||||
>
|
||||
<div class="line-height-100">
|
||||
{{ spell.name }}#{{ spell.id }}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="close"
|
||||
data-dismiss="modal"
|
||||
aria-label="Close"
|
||||
@click="close()"
|
||||
>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<form
|
||||
id="update-spell"
|
||||
ref="update-spell"
|
||||
@submit="updateSpell"
|
||||
>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_name"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Nom :</label>
|
||||
<input
|
||||
id="spell_name"
|
||||
v-model="spell.name"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="spell_name"
|
||||
placeholder="(256 caractères max.)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_description"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Description :</label>
|
||||
<textarea
|
||||
id="spell_description"
|
||||
v-model="spell.description"
|
||||
class="form-control"
|
||||
name="spell_description"
|
||||
placeholder="(2048 caractères max.)"
|
||||
/>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="spell_ritual"
|
||||
v-model="spell.is_ritual"
|
||||
type="checkbox"
|
||||
class="form-check-input"
|
||||
name="spell_ritual"
|
||||
>
|
||||
<label
|
||||
for="spell_ritual"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Rituel ? </label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_level"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Niveau :</label>
|
||||
<input
|
||||
id="spell_level"
|
||||
v-model="spell.level"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="spell_level"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
placeholder="(Nombre entier de 0 à 100)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_schools"
|
||||
class="font-weight-bold col-form-label"
|
||||
>École(s) :</label>
|
||||
<select
|
||||
id="spell_schools"
|
||||
v-model="spell.spell_school_ids_value"
|
||||
class="form-control"
|
||||
name="spell_schools"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(school, index) in all_schools"
|
||||
:key="index"
|
||||
:value="'school_' + school.id"
|
||||
>
|
||||
{{ school.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_charge"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Charge :</label>
|
||||
<input
|
||||
id="spell_charge"
|
||||
v-model="spell.charge"
|
||||
type="number"
|
||||
class="form-control"
|
||||
name="spell_charge"
|
||||
min="0"
|
||||
max="100"
|
||||
step="1"
|
||||
placeholder="(Nombre entier de 0 à 100)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_ingredients"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Ingrédient(s) :</label>
|
||||
<select
|
||||
id="spell_ingredients"
|
||||
v-model="spell.spell_ingredient_ids_value"
|
||||
class="form-control"
|
||||
name="spell_ingredients"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(ingredient,index) in all_ingredients"
|
||||
:key="index"
|
||||
:value="'ingredient_' + ingredient.id"
|
||||
>
|
||||
{{ ingredient.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_cost"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Coût :</label>
|
||||
<input
|
||||
id="spell_cost"
|
||||
v-model="spell.cost"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="spell_cost"
|
||||
placeholder="(32 caractères max.)"
|
||||
>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label
|
||||
for="spell_variables"
|
||||
class="font-weight-bold col-form-label"
|
||||
>Variable(s) :</label>
|
||||
<select
|
||||
id="spell_variables"
|
||||
v-model="spell.spell_variable_ids_value"
|
||||
class="form-control"
|
||||
name="spell_variables"
|
||||
multiple
|
||||
>
|
||||
<option
|
||||
v-for="(variable,index) in all_variables"
|
||||
:key="index"
|
||||
:value="'variable_' + variable.id"
|
||||
>
|
||||
{{ variable.description }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<template #modal-footer="{ close }">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger"
|
||||
data-dismiss="modal"
|
||||
@click="close()"
|
||||
>
|
||||
Fermer
|
||||
</button>
|
||||
<!-- <input type="button" class="btn btn-success" value="Enregistrer comme nouveau" @click="cloneSpell()"> -->
|
||||
<input
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
value="Enregistrer"
|
||||
form="update-spell"
|
||||
>
|
||||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Spells = RepositoryFactory.get('spells');
|
||||
const Schools = RepositoryFactory.get('schools');
|
||||
const Variables = RepositoryFactory.get('variables');
|
||||
const Ingredients = RepositoryFactory.get('ingredients');
|
||||
|
||||
export default {
|
||||
name: 'EditSpellCard',
|
||||
props: {
|
||||
spell: {
|
||||
type: Object,
|
||||
required: true,
|
||||
id: Number,
|
||||
name: String,
|
||||
description: String,
|
||||
is_ritual: Boolean,
|
||||
schools: Array,
|
||||
variables: Array,
|
||||
ingredients: Array,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
all_schools: [],
|
||||
all_variables: [],
|
||||
all_ingredients: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
computeSpell() {
|
||||
let output = this.spell;
|
||||
output.spell_school_ids = [];
|
||||
output.spell_variable_ids = [];
|
||||
output.spell_ingredient_ids = [];
|
||||
output.spell_school_ids_value = [];
|
||||
output.spell_variable_ids_value = [];
|
||||
output.spell_ingredient_ids_value = [];
|
||||
|
||||
output.schools.forEach(element => {
|
||||
output.spell_school_ids.push(element['id']);
|
||||
});
|
||||
output.variables.forEach(element => {
|
||||
output.spell_variable_ids.push(element['id']);
|
||||
});
|
||||
output.ingredients.forEach(element => {
|
||||
output.spell_ingredient_ids.push(element['id']);
|
||||
});
|
||||
|
||||
output.spell_school_ids.forEach(element => {
|
||||
output.spell_school_ids_value.push("school_" + element);
|
||||
});
|
||||
output.spell_variable_ids.forEach(element => {
|
||||
output.spell_variable_ids_value.push("variable_" + element);
|
||||
});
|
||||
output.spell_ingredient_ids.forEach(element => {
|
||||
output.spell_ingredient_ids_value.push("ingredient_" + element);
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
computeSpell: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.$refs["edit_spell_modal"].show();
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// Gets all relevant info for multiple selects
|
||||
let fetchSchools = Schools.getAll();
|
||||
let fetchVariables = Variables.getAll();
|
||||
let fetchIngredients = Ingredients.getAll();
|
||||
|
||||
Promise.all([fetchSchools, fetchVariables, fetchIngredients])
|
||||
.then(v => {
|
||||
this.all_schools = v[0].data;
|
||||
this.all_variables = v[1].data;
|
||||
this.all_ingredients = v[2].data;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
mounted() {
|
||||
this.$refs["edit_spell_modal"].show();
|
||||
this.$root.$on('bv::modal::hide', () => {
|
||||
this.$emit('editSpell', {});
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
cloneSpell() {
|
||||
},
|
||||
updateSpell(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let schoolsData = Object.values(this.spell.spell_school_ids_value).map(v => {
|
||||
return parseInt(v.slice(7));
|
||||
});
|
||||
let variablesData = Object.values(this.spell.spell_variable_ids_value).map(v => {
|
||||
return parseInt(v.slice(9));
|
||||
});
|
||||
let ingredientsData = Object.values(this.spell.spell_ingredient_ids_value).map(v => {
|
||||
return parseInt(v.slice(11));
|
||||
});
|
||||
|
||||
let data = {
|
||||
name: this.spell.name,
|
||||
description: this.spell.description,
|
||||
is_ritual: !!+this.spell.is_ritual,
|
||||
level: parseInt(this.spell.level),
|
||||
cost: this.spell.cost,
|
||||
charge: parseInt(this.spell.charge),
|
||||
schools: schoolsData,
|
||||
variables: variablesData,
|
||||
ingredients: ingredientsData,
|
||||
};
|
||||
|
||||
Spells.updateOne(this.spell.id, data)
|
||||
.then(v => {
|
||||
this.$emit('updateSpell', v.data);
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
textarea, select {
|
||||
min-height: 10rem;
|
||||
max-height: 20rem;
|
||||
}
|
||||
</style>
|
||||
204
src/components/spells/spell-card.vue
Normal file
204
src/components/spells/spell-card.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div
|
||||
:class="main_school"
|
||||
class="col-12 col-sm-6 col-lg-4 col-xl-3 mb-4 grid-item grid-sizer"
|
||||
>
|
||||
<div
|
||||
class="spellcard bg-white p-4 rounded text-dark shadow"
|
||||
style="border-left-width:4px;border-left-style:solid;"
|
||||
>
|
||||
<div
|
||||
:title="spell.name"
|
||||
class="h3 font-display font-weight-bold text-wrap word-break"
|
||||
style="line-height:100%;"
|
||||
>
|
||||
<router-link
|
||||
:to="`/sorts/${spell.id}`"
|
||||
class="text-decoration-none"
|
||||
>
|
||||
{{ spell.name }}
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="font-weight-700 text-muted d-inline-block">
|
||||
Niveau {{ spell.level }}
|
||||
</div>
|
||||
<span> · </span>
|
||||
<div class="text-muted d-inline-block">
|
||||
<span
|
||||
v-for="(school,index) in spell.schools"
|
||||
:key="index"
|
||||
>
|
||||
<span v-if="index!=0">, </span>
|
||||
<router-link
|
||||
:to="`ecoles/${school.id}`"
|
||||
class="text-secondary"
|
||||
>{{ school.name }}</router-link>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="spell.charge!=0"
|
||||
class="small font-weight-bold"
|
||||
>
|
||||
<span>Charge {{ spell.charge }} tour(s)</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="spell.is_ritual"
|
||||
class="small font-weight-bold"
|
||||
>
|
||||
<span>Rituel</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="spell.ingredients.length>0"
|
||||
class="small"
|
||||
>
|
||||
<span class="font-weight-bold">Nécessite </span>
|
||||
<span
|
||||
v-for="(ingredient,index) in spell.ingredients"
|
||||
:key="index"
|
||||
>
|
||||
<span v-if="index!=0">, </span>
|
||||
<span>{{ ingredient.name }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:id="'spell_description_' + spell.id"
|
||||
v-clipboard="spell.description"
|
||||
v-b-tooltip.click
|
||||
placement="bottom"
|
||||
title="Description copiée !"
|
||||
class="small text-muted mt-2"
|
||||
>
|
||||
<span class="prewrap">{{ spell.description }}</span>
|
||||
</div>
|
||||
|
||||
<div class="mt-2">
|
||||
<div class="font-weight-bold d-inline-block">
|
||||
<span>Coût : </span>{{ spell.cost }}
|
||||
</div>
|
||||
<div
|
||||
v-if="spell.variables.length>0"
|
||||
class="small d-inline-block"
|
||||
>
|
||||
, où :
|
||||
</div>
|
||||
<div class="small">
|
||||
<span
|
||||
v-for="(variable,index) in spell.variables"
|
||||
:key="index"
|
||||
>
|
||||
<span class="font-weight-bold">
|
||||
<span v-if="index!=0"><br></span>
|
||||
<span>{{ String.fromCharCode(120+index) }}</span>
|
||||
</span>
|
||||
<span> = {{ variable.description }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<footer
|
||||
v-if="user"
|
||||
class="text-right"
|
||||
>
|
||||
<a class="h5 text-secondary mr-1">
|
||||
<i
|
||||
class="mad"
|
||||
@click="editSpell(spell)"
|
||||
>edit</i>
|
||||
</a>
|
||||
<a class="h5 text-danger">
|
||||
<i
|
||||
class="mad"
|
||||
@click="deleteSpell(spell)"
|
||||
>delete</i>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Spells = RepositoryFactory.get('spells');
|
||||
|
||||
export default {
|
||||
name: 'SpellCard',
|
||||
props: {
|
||||
spell: Object,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
main_school: this.spell.schools[0].name,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.getUserProfile;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.main_school = this.main_school.toLowerCase();
|
||||
},
|
||||
methods: {
|
||||
editSpell(spell) {
|
||||
this.$emit('editSpell', spell);
|
||||
},
|
||||
deleteSpell(spell) {
|
||||
Spells.deleteOne(this.spell.id)
|
||||
.then(() => {
|
||||
this.$emit('deleteSpell', spell);
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.spell-card {
|
||||
footer {
|
||||
a {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
@mixin colorschool($sname,$scolor) {
|
||||
&.#{$sname} {
|
||||
.spellcard { border-left-color: $scolor; }
|
||||
.h3>a { color: $scolor; }
|
||||
}
|
||||
}
|
||||
@include colorschool(lumomancie,#babaa4);
|
||||
@include colorschool(vitamancie,#57ab6e);
|
||||
@include colorschool(obstrumancie,#bd4a66);
|
||||
@include colorschool(tenebromancie,#404842);
|
||||
@include colorschool(necromancie,#5d4777);
|
||||
@include colorschool(morbomancie,#d8733d);
|
||||
@include colorschool(pyromancie,#b6362a);
|
||||
@include colorschool(hydromancie,#3f68c7);
|
||||
@include colorschool(electromancie,#cd9731);
|
||||
@include colorschool(terramancie,#7e5540);
|
||||
@include colorschool(sidéromancie,#58697a);
|
||||
@include colorschool(caelomancie,#a8a8a8);
|
||||
@include colorschool(légimancie,#5dbabd);
|
||||
@include colorschool(illusiomancie,#9f63a1);
|
||||
@include colorschool(cruciomancie,#252451);
|
||||
@include colorschool(chronomancie,#79896a);
|
||||
@include colorschool(spatiomancie,#2d4776);
|
||||
@include colorschool(kénomancie,#101010);
|
||||
@include colorschool(lutomancie,#4e2827);
|
||||
@include colorschool(échomancie,#6d9fd1);
|
||||
@include colorschool(protomancie,#4f5751);
|
||||
@include colorschool(rebumancie,#8e7245);
|
||||
@include colorschool(vocamancie,#247864);
|
||||
@include colorschool(somamancie,#976c67);
|
||||
@include colorschool(antimancie,#ad95c1);
|
||||
}
|
||||
|
||||
</style>
|
||||
296
src/components/spells/spells-list.vue
Normal file
296
src/components/spells/spells-list.vue
Normal file
@@ -0,0 +1,296 @@
|
||||
<template>
|
||||
<div class="spell-list-wrapper">
|
||||
<div class="mb-4">
|
||||
<form>
|
||||
<div class="form-group mb-2">
|
||||
<input
|
||||
id="search_terms"
|
||||
v-model="search_text"
|
||||
type="text"
|
||||
class="form-control"
|
||||
name="search_terms"
|
||||
placeholder="Rechercher l'arcane"
|
||||
>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="search_fields_name"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="search_term"
|
||||
value="search_fields_name"
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="search_fields_name"
|
||||
>Nom</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="search_fields_description"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="search_term"
|
||||
value="search_fields_description"
|
||||
checked
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="search_fields_description"
|
||||
>Description</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="search_fields_schools"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="search_term"
|
||||
value="search_fields_schools"
|
||||
disabled
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="search_fields_schools"
|
||||
>
|
||||
École(s)
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="search_fields_ingredients"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="search_term"
|
||||
value="search_fields_ingredients"
|
||||
disabled
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="search_fields_ingredients"
|
||||
>Ingrédients</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input
|
||||
id="search_fields_variables"
|
||||
class="form-check-input"
|
||||
type="radio"
|
||||
name="search_term"
|
||||
value="search_fields_variables"
|
||||
disabled
|
||||
>
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="search_fields_variables"
|
||||
>Variables</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button
|
||||
v-if="user"
|
||||
type="button"
|
||||
class="btn font-display font-weight-bold btn-lg btn-outline-dark btn-block shadow-sm mb-4"
|
||||
@click="showAdd"
|
||||
>
|
||||
<i class="mad">add</i>
|
||||
<span>Ajouter un sort</span>
|
||||
</button>
|
||||
|
||||
<div
|
||||
v-if="filteredSpells.length > 0"
|
||||
class="spell-list-wrapper"
|
||||
>
|
||||
<div
|
||||
v-masonry
|
||||
class="row spells-list"
|
||||
transition-duration="1s"
|
||||
item-selector=".spell-card"
|
||||
>
|
||||
<spell-card
|
||||
v-for="(spell) in filteredSpells"
|
||||
:key="spell.id"
|
||||
v-masonry-tile
|
||||
class="spell-card"
|
||||
:spell="spell"
|
||||
@editSpell="editSpell"
|
||||
@deleteSpell="deleteSpell"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<edit-spell-card
|
||||
v-if="Object.keys(current_edit_spell).length > 0"
|
||||
:spell="current_edit_spell"
|
||||
@editSpell="editSpell"
|
||||
@updateSpell="updateSpell"
|
||||
/>
|
||||
|
||||
<add-spell-card
|
||||
v-if="adding_spell"
|
||||
@cancelAdd="cancelAdd"
|
||||
@addSpell="addSpell"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="loader-wrapper"
|
||||
>
|
||||
<loader />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import SpellCard from "./spell-card";
|
||||
import EditSpellCard from "./edit-spell-card";
|
||||
import AddSpellCard from "./add-spell-card";
|
||||
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Spells = RepositoryFactory.get('spells');
|
||||
const Schools = RepositoryFactory.get('schools');
|
||||
|
||||
export default {
|
||||
name: 'Spellslist',
|
||||
components: {
|
||||
'spell-card': SpellCard,
|
||||
'edit-spell-card': EditSpellCard,
|
||||
'add-spell-card': AddSpellCard,
|
||||
},
|
||||
props: {
|
||||
schoolId: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
spells: [],
|
||||
loading: false,
|
||||
current_edit_spell: {},
|
||||
currentIndex: 1,
|
||||
adding_spell: false,
|
||||
search_text: "",
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
filteredSpells() {
|
||||
return this.spells;
|
||||
},
|
||||
user() {
|
||||
return this.$store.state.user;
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.spells = this.filteredSpells;
|
||||
if (!this.school_id) {
|
||||
this.getInitialSpells();
|
||||
} else {
|
||||
this.getInitialSchoolSpells();
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (!this.school_id) {
|
||||
this.scroll();
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getInitialSpells() {
|
||||
Spells.getPage(this.currentIndex)
|
||||
.then(v => {
|
||||
this.loading = true;
|
||||
let spells = this.filteredSpells;
|
||||
spells.push(v.data);
|
||||
this.spells = spells[0];
|
||||
})
|
||||
.then(() => {
|
||||
this.currentIndex++;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
getInitialSchoolSpells() {
|
||||
Schools.getSpellsFromOne(this.school_id)
|
||||
.then(v => {
|
||||
this.loading = true;
|
||||
let spells = this.filteredSpells;
|
||||
spells.push(v.data.spells);
|
||||
this.spells = spells[0];
|
||||
})
|
||||
.then(() => {
|
||||
this.currentIndex++;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
scroll() {
|
||||
window.onscroll = () => {
|
||||
if (((window.innerHeight + window.scrollY) - document.body.offsetHeight) >= -1) {
|
||||
Spells.getPage(this.currentIndex)
|
||||
.then(v => {
|
||||
this.loading = true;
|
||||
let spells = this.filteredSpells;
|
||||
for (let i = 0; i < v.data.length; i++) {
|
||||
const element = v.data[i];
|
||||
spells.push(element);
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.currentIndex++;
|
||||
this.loading = false;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
}
|
||||
};
|
||||
},
|
||||
editSpell(e) {
|
||||
this.current_edit_spell = e;
|
||||
},
|
||||
showAdd() {
|
||||
this.adding_spell = true;
|
||||
},
|
||||
cancelAdd() {
|
||||
this.adding_spell = false;
|
||||
},
|
||||
// Receives events to update the data
|
||||
addSpell(e) {
|
||||
Spells.getOne(e.id)
|
||||
.then(v => {
|
||||
let spells = this.filteredSpells;
|
||||
spells.unshift(v.data);
|
||||
})
|
||||
.then(() => {
|
||||
this.adding_spell = false;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
updateSpell(e) {
|
||||
let oldSpell = this.spells.find(x => x.id === e.id);
|
||||
this.spells.splice(this.spells.indexOf(oldSpell), 1, e);
|
||||
this.current_edit_spell = {};
|
||||
},
|
||||
deleteSpell(e) {
|
||||
this.spells.splice(this.spells.indexOf(e), 1);
|
||||
this.current_edit_spell = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.loader-wrapper {
|
||||
margin-top: 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
318
src/components/timeline/timeline.vue
Normal file
318
src/components/timeline/timeline.vue
Normal file
@@ -0,0 +1,318 @@
|
||||
<template>
|
||||
<ul class="timeline">
|
||||
<li class="timeline-item period first">
|
||||
<div class="timeline-info" />
|
||||
<div class="timeline-content">
|
||||
<h2 class="timeline-title">
|
||||
LE RENOUVEAU
|
||||
</h2>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<div class="timeline-info">
|
||||
<span>March 12, 2016</span>
|
||||
</div>
|
||||
<div class="timeline-marker" />
|
||||
<div class="timeline-content">
|
||||
<h3 class="timeline-title">
|
||||
Event Title
|
||||
</h3>
|
||||
<p>Nullam vel sem. Nullam vel sem. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Donec vitae sapien ut libero venenatis faucibus. ullam dictum felis eu pede mollis pretium. Pellentesque ut neque.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<div class="timeline-info">
|
||||
<span>March 23, 2016</span>
|
||||
</div>
|
||||
<div class="timeline-marker" />
|
||||
<div class="timeline-content">
|
||||
<h3 class="timeline-title">
|
||||
Event Title
|
||||
</h3>
|
||||
<p>Nullam vel sem. Nullam vel sem. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Donec vitae sapien ut libero venenatis faucibus. ullam dictum felis eu pede mollis pretium. Pellentesque ut neque. </p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item period">
|
||||
<div class="timeline-info" />
|
||||
<div class="timeline-marker" />
|
||||
<div class="timeline-content">
|
||||
<h2 class="timeline-title">
|
||||
April 2016
|
||||
</h2>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<div class="timeline-info">
|
||||
<span>April 02, 2016</span>
|
||||
</div>
|
||||
<div class="timeline-marker" />
|
||||
<div class="timeline-content">
|
||||
<h3 class="timeline-title">
|
||||
Event Title
|
||||
</h3>
|
||||
<p>Nullam vel sem. Nullam vel sem. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Donec vitae sapien ut libero venenatis faucibus. ullam dictum felis eu pede mollis pretium. Pellentesque ut neque. </p>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<div class="timeline-info">
|
||||
<span>April 28, 2016</span>
|
||||
</div>
|
||||
<div class="timeline-marker" />
|
||||
<div class="timeline-content">
|
||||
<h3 class="timeline-title">
|
||||
Event Title
|
||||
</h3>
|
||||
<p>Nullam vel sem. Nullam vel sem. Integer ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Donec orci lectus, aliquam ut, faucibus non, euismod id, nulla. Donec vitae sapien ut libero venenatis faucibus. ullam dictum felis eu pede mollis pretium. Pellentesque ut neque. </p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Timeline',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/*==================================
|
||||
TIMELINE
|
||||
==================================*/
|
||||
|
||||
/*-- GENERAL STYLES
|
||||
------------------------------*/
|
||||
.timeline {
|
||||
margin: 0;
|
||||
padding-top: 25px;
|
||||
width: 100%;
|
||||
line-height: 1.4em;
|
||||
list-style: none;
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
/*----- TIMELINE ITEM -----*/
|
||||
|
||||
.timeline-item {
|
||||
padding-left: 40px;
|
||||
position: relative;
|
||||
&:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*----- TIMELINE INFO -----*/
|
||||
|
||||
.timeline-info {
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3px;
|
||||
margin: 0 0 .5em 0;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
/*----- TIMELINE MARKER -----*/
|
||||
|
||||
.timeline-marker {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0; left: 0;
|
||||
width: 15px;
|
||||
&:before {
|
||||
background: $primary-blue;
|
||||
border: 3px solid transparent;
|
||||
border-radius: 100%;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
top: 4px; left: 0;
|
||||
width: 15px;
|
||||
transition: background 0.3s ease-in-out, border 0.3s ease-in-out;
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
width: 3px;
|
||||
background: #CCD5DB;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 24px; bottom: 0; left: 6px;
|
||||
}
|
||||
.timeline-item:last-child &:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
.timeline-item:not(.period):hover .timeline-marker:before {
|
||||
background: transparent;
|
||||
border: 3px solid $primary-blue;
|
||||
}
|
||||
|
||||
/*----- TIMELINE CONTENT -----*/
|
||||
|
||||
.timeline-content {
|
||||
padding-bottom: 40px;
|
||||
p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*----- TIMELINE PERIOD -----*/
|
||||
|
||||
.period {
|
||||
padding: 0;
|
||||
.timeline-info {
|
||||
display: none;
|
||||
}
|
||||
.timeline-marker {
|
||||
&:before {
|
||||
background: transparent;
|
||||
content: "";
|
||||
width: 15px;
|
||||
height: auto;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
top: 0;
|
||||
bottom: 30px;
|
||||
position: absolute;
|
||||
border-top: 3px solid #CCD5DB;
|
||||
border-bottom: 3px solid #CCD5DB;
|
||||
}
|
||||
&:after {
|
||||
content: "";
|
||||
height: 32px;
|
||||
top: auto;
|
||||
}
|
||||
}
|
||||
&:not(.first) {
|
||||
.timeline-content {
|
||||
padding: 40px 0 70px;
|
||||
}
|
||||
}
|
||||
.timeline-title {
|
||||
margin: 0;
|
||||
font-family: 'Playfair Display', sans-serif;
|
||||
font-weight: $bold;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------
|
||||
MOD: TIMELINE SPLIT
|
||||
----------------------------------------------*/
|
||||
|
||||
.timeline-split {
|
||||
@media (min-width: 768px) {
|
||||
.timeline {
|
||||
display: table;
|
||||
}
|
||||
.timeline-item {
|
||||
display: table-row;
|
||||
padding: 0;
|
||||
}
|
||||
.timeline-info,
|
||||
.timeline-marker,
|
||||
.timeline-content,
|
||||
.period .timeline-info {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
}
|
||||
.timeline-marker {
|
||||
position: relative;
|
||||
}
|
||||
.timeline-content {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.timeline-info {
|
||||
padding-right: 30px;
|
||||
}
|
||||
.period .timeline-title {
|
||||
position: relative;
|
||||
left: -45px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------
|
||||
MOD: TIMELINE CENTERED
|
||||
----------------------------------------------*/
|
||||
|
||||
.timeline-centered {
|
||||
@extend .timeline-split;
|
||||
@media (min-width: 992px) {
|
||||
&,
|
||||
.timeline-item,
|
||||
.timeline-info,
|
||||
.timeline-marker,
|
||||
.timeline-content {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.timeline-item {
|
||||
padding-bottom: 40px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.timeline-marker {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -7.5px;
|
||||
}
|
||||
.timeline-info,
|
||||
.timeline-content {
|
||||
width: 50%;
|
||||
}
|
||||
> .timeline-item:nth-child(odd) .timeline-info {
|
||||
float: left;
|
||||
text-align: right;
|
||||
padding-right: 30px;
|
||||
}
|
||||
> .timeline-item:nth-child(odd) .timeline-content {
|
||||
float: right;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
}
|
||||
> .timeline-item:nth-child(even) .timeline-info {
|
||||
float: right;
|
||||
text-align: left;
|
||||
padding-left: 30px;
|
||||
}
|
||||
> .timeline-item:nth-child(even) .timeline-content {
|
||||
float: left;
|
||||
text-align: right;
|
||||
padding-right: 30px;
|
||||
}
|
||||
> .timeline-item.period .timeline-content {
|
||||
float: none;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
.timeline-item.period {
|
||||
padding: 50px 0 90px;
|
||||
}
|
||||
.period .timeline-marker:after {
|
||||
height: 30px;
|
||||
bottom: 0;
|
||||
top: auto;
|
||||
}
|
||||
.period .timeline-title {
|
||||
left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------
|
||||
MOD: MARKER OUTLINE
|
||||
----------------------------------------------*/
|
||||
|
||||
.marker-outline {
|
||||
.timeline-marker {
|
||||
&:before {
|
||||
background: transparent;
|
||||
border-color: $primary-blue;
|
||||
}
|
||||
}
|
||||
.timeline-item:hover .timeline-marker:before {
|
||||
background: $primary-blue;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
17
src/components/user/profile/update-form.vue
Normal file
17
src/components/user/profile/update-form.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<form>
|
||||
<div>Update profile for {{ user.name }}</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'UpdateForm',
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
default: null,
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
7
src/global-components.js
Normal file
7
src/global-components.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import Navbar from './components/global/navbar/navbar';
|
||||
import Loader from './components/global/loader';
|
||||
|
||||
export default [
|
||||
Navbar,
|
||||
Loader
|
||||
];
|
||||
76
src/main.js
Normal file
76
src/main.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// Core
|
||||
import Vue from 'vue';
|
||||
import VueRouter from 'vue-router';
|
||||
import App from './app.vue';
|
||||
import VueMeta from 'vue-meta';
|
||||
|
||||
Vue.use(VueMeta, {
|
||||
// optional pluginOptions
|
||||
refreshOnceOnNavigation: true
|
||||
});
|
||||
|
||||
// Environment
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
// Store
|
||||
import store from './store';
|
||||
|
||||
// Global components
|
||||
import Globals from './global-components.js';
|
||||
Globals.forEach(component => {
|
||||
Vue.component(component.name, component);
|
||||
});
|
||||
|
||||
// Cookies
|
||||
import VueCookies from 'vue-cookies';
|
||||
Vue.use(VueCookies);
|
||||
|
||||
// Jquery
|
||||
import jquery from 'jquery';
|
||||
window.$ = jquery;
|
||||
window.jquery = jquery;
|
||||
|
||||
// Styles
|
||||
// Fonts
|
||||
import './assets/scss/_fonts.scss';
|
||||
import './assets/scss/_global.scss';
|
||||
|
||||
import 'bootstrap/dist/css/bootstrap.css';
|
||||
import 'bootstrap-vue/dist/bootstrap-vue.css';
|
||||
import 'bootstrap/dist/js/bootstrap.js';
|
||||
|
||||
// Plugins
|
||||
import { BootstrapVue } from 'bootstrap-vue';
|
||||
Vue.use(BootstrapVue);
|
||||
|
||||
// Masonry (will probably get removed in the very very near future lmao)
|
||||
import { VueMasonryPlugin } from 'vue-masonry';
|
||||
Vue.use(VueMasonryPlugin);
|
||||
|
||||
// Clipboard (might find a better one or code it myself idk)
|
||||
import clipboard from 'v-clipboard';
|
||||
Vue.use(clipboard);
|
||||
|
||||
// FUNCTIONS
|
||||
// Let's be honest i will 1000% refactor this.
|
||||
let filter = (text, length, clamp) => {
|
||||
clamp = clamp || '...';
|
||||
let node = document.createElement('div');
|
||||
node.innerHTML = text;
|
||||
let content = node.textContent;
|
||||
return content.length > length ? content.slice(0, length) + clamp : content;
|
||||
};
|
||||
Vue.filter('truncate', filter);
|
||||
|
||||
// Router
|
||||
import router from './routes';
|
||||
Vue.use(VueRouter);
|
||||
|
||||
// Mount Vue
|
||||
const app = new Vue({
|
||||
render: h => h(App),
|
||||
router,
|
||||
store: store
|
||||
});
|
||||
app.$mount('#srs');
|
||||
47
src/pages/index-page.vue
Normal file
47
src/pages/index-page.vue
Normal file
@@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div class="container-fluid">
|
||||
<section class="d-flex justify-content-center align-items-center">
|
||||
<main class="px-4">
|
||||
<h1 class="title text-dark mb-4 font-display font-weight-black line-height-100">
|
||||
"C'est magique, ta gueule."
|
||||
</h1>
|
||||
<div class="lead font-display font-weight-bold">
|
||||
— N'importe quel MJ, une fois dans sa vie
|
||||
</div>
|
||||
<hr class="w-50">
|
||||
<div class="lead">
|
||||
<span class="font-display font-weight-bold">Auracle</span> est une base de données publique en ligne pour sortilèges, en soutien au jeu de rôle sur table <span class="font-weight-bold text-secondary">Leïm</span>.
|
||||
</div>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'IndexPage',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
section {
|
||||
min-height: calc(100vh - 56px);
|
||||
main {
|
||||
width: 1000px;
|
||||
.title {
|
||||
color: $white;
|
||||
font-size: 64px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: $bp-md) {
|
||||
section {
|
||||
main {
|
||||
.title {
|
||||
font-size: 48px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
src/pages/schools/schools-page.vue
Normal file
25
src/pages/schools/schools-page.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h1 class="display-3 font-display mb-3">
|
||||
Écoles
|
||||
</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'SchoolsPage',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Maîtrises'
|
||||
},
|
||||
};
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
54
src/pages/schools/single-school-page.vue
Normal file
54
src/pages/schools/single-school-page.vue
Normal file
@@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<div
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h1 class="display-3 font-display mb-3">
|
||||
{{ school.name }}
|
||||
</h1>
|
||||
<p>{{ school.description }}</p>
|
||||
<spell-list :school_id="id" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Schools = RepositoryFactory.get('schools');
|
||||
|
||||
import SpellsList from "@/components/spells/spells-list";
|
||||
|
||||
export default {
|
||||
name: 'SingleSchoolPage',
|
||||
metaInfo() {
|
||||
return {
|
||||
titleTemplate: `%s - ${this.school.name}`,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
'spell-list': SpellsList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: this.$route.params.id,
|
||||
school: {},
|
||||
errors: {
|
||||
loading: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
Schools.getOne(this.id)
|
||||
.then(v => {
|
||||
this.school = v.data;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
50
src/pages/spells/single-spell-page.vue
Normal file
50
src/pages/spells/single-spell-page.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h1 class="display-3 font-display mb-3">
|
||||
{{ spell.name }}
|
||||
</h1>
|
||||
<p class="prewrap">
|
||||
{{ spell.description }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Spells = RepositoryFactory.get('spells');
|
||||
|
||||
export default {
|
||||
name: 'SingleSpellPage',
|
||||
metaInfo() {
|
||||
return {
|
||||
titleTemplate: this.spell.name ? `%s - ${this.spell.name}` : '%s',
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: this.$route.params.id,
|
||||
spell: {},
|
||||
errors: {
|
||||
loading: "",
|
||||
}
|
||||
};
|
||||
},
|
||||
created() {
|
||||
Spells.getOne(this.id)
|
||||
.then(v => {
|
||||
this.spell = v.data;
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
27
src/pages/spells/spells-page.vue
Normal file
27
src/pages/spells/spells-page.vue
Normal file
@@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h1 class="display-3 font-display mb-3">
|
||||
Sortilèges
|
||||
</h1>
|
||||
<spell-list />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SpellsList from "@/components/spells/spells-list.vue";
|
||||
|
||||
export default {
|
||||
name: 'SpellsPage',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Grimoire'
|
||||
},
|
||||
components: {
|
||||
'spell-list': SpellsList,
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
||||
29
src/pages/timelines/timeline-page.vue
Normal file
29
src/pages/timelines/timeline-page.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h1 class="display-3 font-display mb-3">
|
||||
Chronologie
|
||||
</h1>
|
||||
<timeline />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Timeline from "@/components/timeline/timeline";
|
||||
|
||||
export default {
|
||||
name: 'TimelinePage',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Âges'
|
||||
},
|
||||
components: {
|
||||
'timeline': Timeline
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
182
src/pages/user/login-page.vue
Normal file
182
src/pages/user/login-page.vue
Normal file
@@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="container-fluid">
|
||||
<section class="d-flex justify-content-center align-items-center">
|
||||
<main>
|
||||
<div class="title font-display mb-3">Connexion</div>
|
||||
|
||||
<form @submit="logUser">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Addresse mail</label>
|
||||
<input
|
||||
v-model="email"
|
||||
type="email"
|
||||
id="email"
|
||||
class="form-control"
|
||||
:class="{ 'is-invalid': errors.email || errors.login }"
|
||||
placeholder="john.doe@gmail.com"
|
||||
autocomplete="email">
|
||||
|
||||
<small
|
||||
v-if="!errors.email"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.
|
||||
</small>
|
||||
<small
|
||||
v-if="errors.email"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.email }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Mot de passe</label>
|
||||
<input
|
||||
v-model="password"
|
||||
type="password"
|
||||
id="password"
|
||||
class="form-control"
|
||||
:class="{ 'is-invalid': errors.password || errors.login }"
|
||||
autocomplete="current-password">
|
||||
<small
|
||||
v-if="!errors.password"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Nous vous conseillons d'utiliser un mot de passe unique pour cette application seulement.
|
||||
</small>
|
||||
<small
|
||||
v-if="errors.password"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.password }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input
|
||||
type="checkbox"
|
||||
v-model="remember_me"
|
||||
id="remember-me"
|
||||
class="form-check-input">
|
||||
<label
|
||||
class="form-check-label"
|
||||
for="remember-me"
|
||||
>
|
||||
Rester connecté
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<small v-if="errors.login" class="text-danger">{{ errors.login }}</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Connexion</button>
|
||||
<router-link :to="'/inscription'" class="btn btn-dark">Inscription</router-link>
|
||||
<small class="form-text text-muted">Mot de passe oublié ? <a href="#">Vous pouvez le changer ici !</a></small>
|
||||
|
||||
</form>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'login-page',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Connexion'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
email: "",
|
||||
password: "",
|
||||
remember_me: false,
|
||||
submitted: false,
|
||||
|
||||
errors: {
|
||||
email: "",
|
||||
password: "",
|
||||
login: "",
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async logUser(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Resets old errors if any
|
||||
for (const o in this.errors) {
|
||||
this.errors[o] = ""
|
||||
}
|
||||
this.submitted = true;
|
||||
|
||||
let userInput = {
|
||||
"user": {
|
||||
"mail": this.email,
|
||||
"password": this.password,
|
||||
},
|
||||
"remember_me": this.remember_me
|
||||
};
|
||||
|
||||
// Check is all inputs are set
|
||||
if (!userInput.user.mail) {
|
||||
this.errors.email = "Vous devez renseigner une addresse mail."
|
||||
}
|
||||
if (!userInput.user.password) {
|
||||
this.errors.password = "Vous devez renseigner un mot de passe."
|
||||
}
|
||||
|
||||
// Stops the method if any errors exist
|
||||
for (const o in this.errors) {
|
||||
if (this.errors[o] != "") {
|
||||
this.submitted = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no errors are present, submits the form
|
||||
if (this.submitted) {
|
||||
this.$store.dispatch('user_login', userInput)
|
||||
.then(() => {
|
||||
this.$router.push('/profil')
|
||||
})
|
||||
.catch(err => {
|
||||
if (err.status != 500) {
|
||||
this.errors.email = err.data.error;
|
||||
} else {
|
||||
this.errors.login = err.data.error;
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
section {
|
||||
min-height: calc(100vh - 56px);
|
||||
main {
|
||||
width: 600px;
|
||||
.title {
|
||||
font-size: 5rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
section {
|
||||
main {
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
111
src/pages/user/profile-page.vue
Normal file
111
src/pages/user/profile-page.vue
Normal file
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="user"
|
||||
id="spell-container"
|
||||
class="container-fluid p-4"
|
||||
>
|
||||
<h2 class="display-3 font-display mb-3">
|
||||
<span class="username">{{ user.name }}</span>
|
||||
</h2>
|
||||
<span>Membre depuis le {{ registered_date }}</span>
|
||||
|
||||
<ul
|
||||
id="tabs-tab"
|
||||
class="nav nav-tabs mt-3 mb-3"
|
||||
role="tablist"
|
||||
>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
id="tabs-home-tab"
|
||||
class="nav-link active"
|
||||
data-toggle="pill"
|
||||
href="#tabs-home"
|
||||
role="tab"
|
||||
aria-controls="tabs-home"
|
||||
aria-selected="true"
|
||||
>Mes sorts</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a
|
||||
id="tabs-contact-tab"
|
||||
class="nav-link"
|
||||
data-toggle="pill"
|
||||
href="#tabs-contact"
|
||||
role="tab"
|
||||
aria-controls="tabs-contact"
|
||||
aria-selected="false"
|
||||
>Paramètres</a>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
id="tabs-tabContent"
|
||||
class="tab-content"
|
||||
>
|
||||
<div
|
||||
id="tabs-home"
|
||||
class="tab-pane fade show active"
|
||||
role="tabpanel"
|
||||
aria-labelledby="tabs-home-tab"
|
||||
>
|
||||
...
|
||||
</div>
|
||||
<div
|
||||
id="tabs-contact"
|
||||
class="tab-pane fade"
|
||||
role="tabpanel"
|
||||
aria-labelledby="tabs-contact-tab"
|
||||
>
|
||||
<update-form :user="user" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import UpdateForm from '@/components/user/profile/update-form.vue';
|
||||
|
||||
export default {
|
||||
name: 'ProfilePage',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Profil'
|
||||
},
|
||||
components: {
|
||||
UpdateForm,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
user() {
|
||||
return this.$store.getters.getUserProfile;
|
||||
},
|
||||
registered_date() {
|
||||
let raw_date = new Date(this.user.register_date);
|
||||
|
||||
let date_options = {
|
||||
day: 'numeric',
|
||||
month: 'long',
|
||||
year: 'numeric',
|
||||
};
|
||||
|
||||
return new Intl.DateTimeFormat("fr", date_options).format(raw_date);
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.title {
|
||||
font-size: 5rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
216
src/pages/user/register-page.vue
Normal file
216
src/pages/user/register-page.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="container-fluid">
|
||||
<section class="d-flex justify-content-center align-items-center">
|
||||
<main>
|
||||
<div class="title font-display mb-3">Inscription</div>
|
||||
<form @submit="registerUser">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Nom d'utilisateur</label>
|
||||
<input
|
||||
v-model="name"
|
||||
:class="{
|
||||
'is-invalid': errors.name,
|
||||
'is-valid': submitted && !errors.name }"
|
||||
type="text"
|
||||
id="username"
|
||||
class="form-control"
|
||||
placeholder="John Doe"
|
||||
autocomplete="username">
|
||||
<small
|
||||
v-if="!errors.name"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Vous pouvez changer votre pseudo à tout moment.
|
||||
</small>
|
||||
|
||||
<small
|
||||
v-if="errors.name"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.name }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Adresse mail</label>
|
||||
<input
|
||||
v-model="email"
|
||||
:class="{
|
||||
'is-invalid': errors.email,
|
||||
'is-valid': submitted && !errors.email }"
|
||||
type="email"
|
||||
id="email"
|
||||
class="form-control"
|
||||
placeholder="john.doe@gmail.com"
|
||||
autocomplete="email">
|
||||
<small
|
||||
v-if="!errors.email"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Votre addresse mail nous servira principalement à vous identifier et à retrouver votre compte si vous oubliez vos informations de connexion.
|
||||
</small>
|
||||
|
||||
<small
|
||||
v-if="errors.email"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.email }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Mot de passe</label>
|
||||
<input
|
||||
:class="{
|
||||
'is-invalid': errors.password,
|
||||
'is-valid': submitted && !errors.password }"
|
||||
v-model="password"
|
||||
type="password"
|
||||
id="password"
|
||||
class="form-control"
|
||||
autocomplete="password">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password_check">Confirmer le mot de passe</label>
|
||||
<input
|
||||
v-model="password_check"
|
||||
:class="{
|
||||
'is-invalid': errors.password_check || errors.password,
|
||||
'is-valid': submitted && !errors.password_check}"
|
||||
type="password"
|
||||
id="password_check"
|
||||
class="form-control"
|
||||
autocomplete="password-verification">
|
||||
<small
|
||||
v-if="!errors.password && !errors.password_check"
|
||||
class="form-text text-muted"
|
||||
>
|
||||
Votre mot de passe doit idéalement contenir des symboles ainsi que des lettres et des chiffres.
|
||||
</small>
|
||||
|
||||
<small
|
||||
v-if="errors.password"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.password }}
|
||||
</small>
|
||||
<small
|
||||
v-if="errors.password_check"
|
||||
class="form-text text-danger"
|
||||
>
|
||||
{{ errors.password_check }}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<small v-if="errors.register" class="text-danger">{{ errors.register }}</small>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-dark">Inscription</button>
|
||||
</form>
|
||||
</main>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'register-page',
|
||||
metaInfo: {
|
||||
titleTemplate: '%s - Inscription'
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
password_check: "",
|
||||
submitted: false,
|
||||
errors: {
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
password_check: "",
|
||||
register: "",
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async registerUser(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Resets old errors if any
|
||||
for (const o in this.errors) {
|
||||
this.errors[o] = ""
|
||||
}
|
||||
this.submitted = true;
|
||||
|
||||
let userInput = {
|
||||
"user": {
|
||||
"name": this.name,
|
||||
"mail": this.email,
|
||||
"password": this.password
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all inputs are valid without sending anything
|
||||
if (!userInput.user.name) {
|
||||
this.errors.name = "Vous devez renseigner un pseudonyme."
|
||||
}
|
||||
if (!userInput.user.mail) {
|
||||
this.errors.email = "Vous devez renseigner une addresse mail."
|
||||
}
|
||||
if (!userInput.user.password) {
|
||||
this.errors.password = "Vous devez renseigner un mot de passe."
|
||||
}
|
||||
if ((userInput.user.password.localeCompare(this.password_check)) != 0) {
|
||||
this.errors.password_check = "Les mots de passe ne correspondent pas."
|
||||
}
|
||||
|
||||
// Stops the method if any errors exist
|
||||
for (const o in this.errors) {
|
||||
if (this.errors[o] != "") {
|
||||
this.submitted = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no errors are present, submits the form
|
||||
if (this.submitted) {
|
||||
this.$store.dispatch('user_register', userInput)
|
||||
.then(() => {
|
||||
this.$router.push('/');
|
||||
})
|
||||
.catch(err => {
|
||||
// Sets errors in case something wrong comes back from the API
|
||||
if (err.status === 409) {
|
||||
this.errors.email = err.data.error;
|
||||
} else {
|
||||
this.errors.register = "Une erreur inconnue est survenue, rééssayez plus tard.";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
section {
|
||||
min-height: calc(100vh - 56px);
|
||||
main {
|
||||
width: 600px;
|
||||
.title {
|
||||
font-size: 5rem;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.title {
|
||||
font-size: 3.5rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
102
src/routes.js
Normal file
102
src/routes.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import VueRouter from 'vue-router';
|
||||
|
||||
import store from './store';
|
||||
|
||||
// Pages
|
||||
import Index from "@/pages/index-page";
|
||||
|
||||
import Spells from "@/pages/spells/spells-page";
|
||||
import SpellSingle from "@/pages/spells/single-spell-page";
|
||||
|
||||
import Schools from "./pages/schools/schools-page";
|
||||
import SchoolSingle from "@/pages/schools/single-school-page";
|
||||
|
||||
import Timeline from "./pages/timelines/timeline-page";
|
||||
|
||||
import Profile from "@/pages/user/profile-page";
|
||||
import Login from "@/pages/user/login-page";
|
||||
import Register from "@/pages/user/register-page";
|
||||
|
||||
// Routes
|
||||
const routes = [
|
||||
{
|
||||
path: "*",
|
||||
redirect: "/",
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
component: Index,
|
||||
},
|
||||
{
|
||||
path: '/connexion',
|
||||
component: Login,
|
||||
meta: {
|
||||
loginFilter: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/inscription',
|
||||
component: Register,
|
||||
meta: {
|
||||
loginFilter: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/sorts',
|
||||
component: Spells,
|
||||
},
|
||||
{
|
||||
path: '/sorts/:id',
|
||||
component: SpellSingle,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/ecoles',
|
||||
component: Schools,
|
||||
},
|
||||
{
|
||||
path: '/ecoles/:id',
|
||||
component: SchoolSingle,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/ages',
|
||||
component: Timeline,
|
||||
},
|
||||
{
|
||||
path: '/profil',
|
||||
component: Profile,
|
||||
props: true,
|
||||
meta: {
|
||||
logoutFilter: true,
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes,
|
||||
linkActiveClass: "",
|
||||
linkExactActiveClass: "active",
|
||||
});
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.matched.some(record => record.meta.loginFilter)) {
|
||||
if (store.getters.getUserProfile) {
|
||||
next({
|
||||
path: '/',
|
||||
});
|
||||
}
|
||||
next();
|
||||
} else if (to.matched.some(record => record.meta.logoutFilter)) {
|
||||
if (!store.getters.getUserProfile) {
|
||||
next({
|
||||
path: '/connexion',
|
||||
});
|
||||
}
|
||||
next();
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
export default router;
|
||||
19
src/store/index.js
Normal file
19
src/store/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
import VueCookies from 'vue-cookies';
|
||||
Vue.use(VueCookies);
|
||||
Vue.$cookies.config('30d','','');
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
import user from './modules/user.store';
|
||||
|
||||
const debug = process.env.NODE_ENV !== 'production';
|
||||
|
||||
export default new Vuex.Store({
|
||||
modules: {
|
||||
user
|
||||
},
|
||||
strict: debug
|
||||
});
|
||||
23
src/store/modules/spell.store.js
Normal file
23
src/store/modules/spell.store.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const state = {
|
||||
|
||||
};
|
||||
|
||||
const getters = {
|
||||
|
||||
};
|
||||
|
||||
const actions = {
|
||||
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
|
||||
};
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
};
|
||||
91
src/store/modules/user.store.js
Normal file
91
src/store/modules/user.store.js
Normal file
@@ -0,0 +1,91 @@
|
||||
import cookie from 'vue-cookies';
|
||||
|
||||
// API
|
||||
import { RepositoryFactory } from "@/api/repositories";
|
||||
const Users = RepositoryFactory.get('users');
|
||||
|
||||
export const namespaced = true;
|
||||
|
||||
const state = {
|
||||
profile: cookie.get('user_profile') || null,
|
||||
};
|
||||
|
||||
const getters = {
|
||||
getUserProfile: state => {
|
||||
return state.profile;
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
loginUser(state, user) {
|
||||
state.profile = user;
|
||||
},
|
||||
|
||||
logoutUser(state) {
|
||||
state.profile = null;
|
||||
},
|
||||
|
||||
registerUser() {
|
||||
// Will contain the email call eventually
|
||||
}
|
||||
};
|
||||
|
||||
const actions = {
|
||||
/**
|
||||
* @param data
|
||||
* An object containing :
|
||||
* - user object with mail and password hash properties
|
||||
* - remember_me boolean to check cookie expiration time
|
||||
*/
|
||||
user_login ({ commit }, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Users.login(data.user)
|
||||
.then(v => {
|
||||
let user = v.data.user;
|
||||
|
||||
// Check if the use wishes to be remembered
|
||||
if (data.remember_me) {
|
||||
cookie.set('user_profile', user, 60 * 60 * 24 * 30); // Expires after a month
|
||||
} else {
|
||||
cookie.set('user_profile', user, 0); // Expires after browser session expires
|
||||
}
|
||||
|
||||
commit('loginUser', user);
|
||||
resolve(user);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err.response);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
user_logout ({ commit }) {
|
||||
cookie.remove('user_profile');
|
||||
commit('logoutUser');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param data
|
||||
* An object containing :
|
||||
* - user object with string mail, string name, and string password
|
||||
*/
|
||||
user_register ({ commit }, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Users.register(data.user)
|
||||
.then(() => {
|
||||
commit('registerUser');
|
||||
resolve();
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err.response);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations,
|
||||
};
|
||||
19
vue.config.js
Normal file
19
vue.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
lintOnSave: false,
|
||||
configureWebpack: {
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": path.resolve(__dirname, 'src/')
|
||||
}
|
||||
}
|
||||
},
|
||||
css: {
|
||||
loaderOptions: {
|
||||
scss: {
|
||||
prependData: `@import "@/assets/scss/_variables.scss";`
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user